diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..74226f367f6dd --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,507 @@ +version: 2.1 + +setup: true + +# Add pipeline parameters for manual triggers +parameters: + run-browserstack-tests: + type: boolean + default: false + run-firebase-deploy: + type: boolean + default: false + run-testflight-beta: + type: boolean + default: false + run-release-deploy: + type: boolean + default: false + dry-run-mode: + type: boolean + default: false + description: "Enable dry-run mode to simulate all operations without actual deployments" + simulate-version-change: + type: boolean + default: false + description: "For dry-run mode: simulate version change (true) or no change (false)" + # Internal parameter - set when continuing to release pipeline + is-release-pipeline: + type: boolean + default: false + +environment_common: &environment_common + environment: + CI: true + +macos_common: &macos_common + macos: + xcode: "16.4" + resource_class: m4pro.medium + +jobs: + + check-version-bump: + <<: *environment_common + docker: + - image: cimg/base:stable + steps: + - checkout + - attach_workspace: + at: . + - run: + name: Check for MARKETING_VERSION change + command: | + VERSION_CHANGED=false + + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Testing version change detection" + if [[ "<< pipeline.parameters.simulate-version-change >>" == "true" ]]; then + echo "✅ SIMULATED: Version changed" + VERSION_CHANGED=true + else + echo "ℹ️ SIMULATED: No version change detected" + VERSION_CHANGED=false + fi + else + echo "🚀 PRODUCTION MODE: Checking actual version change" + if sh ./check_marketing_version.sh; then + echo "✅ Version changed" + VERSION_CHANGED=true + else + echo "ℹ️ No version change detected" + VERSION_CHANGED=false + fi + fi + + # Save version change result to file + echo "$VERSION_CHANGED" > /tmp/version_changed + echo "Version change result saved: $VERSION_CHANGED" + - run: + name: Extract BrowserStack URL from workspace + command: | + # Load the bash.env from workspace to get BROWSERSTACK_URL + source bash.env + + # Save BROWSERSTACK_URL to file + echo "$BROWSERSTACK_URL" > /tmp/browserstack_url + echo "BrowserStack URL saved: $BROWSERSTACK_URL" + - run: + name: Create continuation parameters + command: | + VERSION_CHANGED=$(cat /tmp/version_changed) + BROWSERSTACK_URL_VALUE=$(cat /tmp/browserstack_url) + + if [[ "$VERSION_CHANGED" == "true" ]]; then + echo "Creating continuation parameters with BrowserStack URL: $BROWSERSTACK_URL_VALUE" + + # Create parameters file for continuation + echo "{\"is-release-pipeline\": true, \"browserstack-url\": \"$BROWSERSTACK_URL_VALUE\"}" > /tmp/continuation_params.json + + echo "Continuation parameters created" + else + echo "No version change - skipping parameter creation" + fi + - when: + condition: + or: + - and: + - << pipeline.parameters.dry-run-mode >> + - << pipeline.parameters.simulate-version-change >> + - and: + - not: << pipeline.parameters.dry-run-mode >> + steps: + - continuation/continue: + configuration_path: .circleci/release-config.yml + parameters: /tmp/continuation_params.json + - run: + name: Pipeline complete + command: | + VERSION_CHANGED=$(cat /tmp/version_changed) + if [[ "$VERSION_CHANGED" == "true" ]]; then + echo "🚀 Release pipeline triggered for version change" + else + echo "ℹ️ No version change - pipeline complete" + fi + + manual-build-testflight-deploy: + <<: *environment_common + <<: *macos_common + steps: + - checkout + - run: + name: Check if this is a dry-run + command: | + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Will not actually upload to TestFlight" + echo "export TESTFLIGHT_DRY_RUN=true" >> $BASH_ENV + else + echo "🚀 PRODUCTION MODE: Will upload to TestFlight" + echo "export TESTFLIGHT_DRY_RUN=false" >> $BASH_ENV + fi + - macos/switch-ruby: + version: "3.2" + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: + name: Bundle install + command: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: + name: verify github + command: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts + - run: + name: extract Production XCConfig + command: echo "$PRODUCTION_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Production.xcconfig + - run: + name: Execute bootstrap + command: ./bootstrap.sh + - run: + name: Build and deploy to Testflight (with dry-run support) + no_output_timeout: 35m + command: | + if [[ "$TESTFLIGHT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN MODE: Building app but NOT uploading to TestFlight" + echo "Would run: bundle exec fastlane testflight_live" + echo "✅ Test successful - workflow reached TestFlight deployment step" + else + echo "🚀 PRODUCTION MODE: Building and uploading to TestFlight" + bundle exec fastlane testflight_live + fi + + build-and-deploy-testflight-beta: + <<: *environment_common + <<: *macos_common + steps: + - checkout + - macos/switch-ruby: + version: "3.2" + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: + name: Bundle install + command: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: + name: Verify github + command: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts + - run: + name: extract Staging XCConfig + command: echo "$STAGING_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Staging.xcconfig + - run: + name: Execute bootstrap + command: ./bootstrap.sh + - run: + name: Build and deploy to Testflight + command: bundle exec fastlane testflight_beta + + build-and-deploy-firebase-and-browserstack: + <<: *environment_common + <<: *macos_common + steps: + - checkout + - run: + name: Check if this is a dry-run + command: | + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Will simulate deployments without actual uploads" + echo "export DEPLOYMENT_DRY_RUN=true" >> $BASH_ENV + else + echo "🚀 PRODUCTION MODE: Will perform actual deployments" + echo "export DEPLOYMENT_DRY_RUN=false" >> $BASH_ENV + fi + - macos/switch-ruby: + version: "3.2" + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: + name: Bundle install + command: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: + name: Verify github + command: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts + - run: + name: Extract Staging XCConfig + command: echo "$STAGING_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Staging.xcconfig + - run: + name: Execute bootstrap + command: ./bootstrap.sh + - run: + name: Build the Ecosia app (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating app build" + echo "Would run: bundle exec fastlane build_ecosia_app" + else + echo "🚀 PRODUCTION: Building Ecosia app" + bundle exec fastlane build_ecosia_app + fi + - run: + name: Upload to Firebase (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating Firebase upload" + echo "Would run: bundle exec fastlane upload_to_firebase" + else + echo "🚀 PRODUCTION: Uploading to Firebase" + bundle exec fastlane upload_to_firebase + fi + - run: + name: Upload to Browserstack (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating BrowserStack upload" + echo "Would run: bundle exec fastlane upload_to_browserstack" + # Simulate the BrowserStack URL for downstream jobs + echo "export BROWSERSTACK_URL=https://fake-browserstack-url-for-testing.com" >> $BASH_ENV + else + echo "🚀 PRODUCTION: Uploading to BrowserStack" + bundle exec fastlane upload_to_browserstack + fi + - run: | + printenv BROWSERSTACK_URL + - run: | + cp $BASH_ENV bash.env + - persist_to_workspace: + root: . + paths: + - bash.env + + build-and-deploy-firebase: + <<: *environment_common + <<: *macos_common + steps: + - checkout + - run: + name: Check if this is a dry-run + command: | + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Will simulate Firebase deployment" + echo "export DEPLOYMENT_DRY_RUN=true" >> $BASH_ENV + else + echo "🚀 PRODUCTION MODE: Will perform actual Firebase deployment" + echo "export DEPLOYMENT_DRY_RUN=false" >> $BASH_ENV + fi + - macos/switch-ruby: + version: "3.2" + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: + name: Bundle install + command: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: + name: Verify github + command: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts + - run: + name: Extract Staging XCConfig + command: echo "$STAGING_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Staging.xcconfig + - run: + name: Execute bootstrap + command: ./bootstrap.sh + - run: + name: Build the Ecosia app (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating app build" + echo "Would run: bundle exec fastlane build_ecosia_app" + else + echo "🚀 PRODUCTION: Building Ecosia app" + bundle exec fastlane build_ecosia_app + fi + - run: + name: Upload to Firebase (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating Firebase upload" + echo "Would run: bundle exec fastlane upload_to_firebase" + else + echo "🚀 PRODUCTION: Uploading to Firebase" + bundle exec fastlane upload_to_firebase + fi + + build-and-deploy-browserstack: + <<: *environment_common + <<: *macos_common + steps: + - checkout + - run: + name: Check if this is a dry-run + command: | + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Will simulate BrowserStack deployment" + echo "export DEPLOYMENT_DRY_RUN=true" >> $BASH_ENV + else + echo "🚀 PRODUCTION MODE: Will perform actual BrowserStack deployment" + echo "export DEPLOYMENT_DRY_RUN=false" >> $BASH_ENV + fi + - macos/switch-ruby: + version: "3.2" + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: + name: Bundle install + command: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: + name: Verify github + command: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts + - run: + name: Extract Staging XCConfig + command: echo "$STAGING_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Staging.xcconfig + - run: + name: Execute bootstrap + command: ./bootstrap.sh + - run: + name: Build the Ecosia app (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating app build" + echo "Would run: bundle exec fastlane build_ecosia_app" + else + echo "🚀 PRODUCTION: Building Ecosia app" + bundle exec fastlane build_ecosia_app + fi + - run: + name: Upload to Browserstack (with dry-run support) + command: | + if [[ "$DEPLOYMENT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating BrowserStack upload" + echo "Would run: bundle exec fastlane upload_to_browserstack" + # Simulate the BrowserStack URL for downstream jobs + echo "export BROWSERSTACK_URL=https://fake-browserstack-url-for-testing.com" >> $BASH_ENV + else + echo "🚀 PRODUCTION: Uploading to BrowserStack" + bundle exec fastlane upload_to_browserstack + fi + - run: | + printenv BROWSERSTACK_URL + - run: | + cp $BASH_ENV bash.env + - persist_to_workspace: + root: . + paths: + - bash.env + +orbs: + path-filtering: circleci/path-filtering@0.1.1 + macos: circleci/macos@2 + jira: circleci/jira@2.0 + continuation: circleci/continuation@1.0.0 + +workflows: + # Setup workflow - runs on main branch merges or manual triggers + setup-workflow: + unless: << pipeline.parameters.is-release-pipeline >> + jobs: + # Always run on main branch merges and patch branches + - build-and-deploy-firebase-and-browserstack: + name: Deploy ad-hoc version over Firebase + Upload to Browserstack + filters: + branches: + only: + - main + - /^main-\d+$/ + - /^patch\/.+$/ + context: napps + + # Check if version was bumped and conditionally continue to release pipeline + - check-version-bump: + name: Check for version bump + requires: + - Deploy ad-hoc version over Firebase + Upload to Browserstack + filters: + branches: + only: + - main + - /^main-\d+$/ + - /^patch\/.+$/ + context: napps + + # Beta deployment triggered by tags + beta-deployment: + unless: << pipeline.parameters.is-release-pipeline >> + jobs: + - build-and-deploy-testflight-beta: + name: Deploy beta version over Testflight (tags) + filters: + branches: + ignore: /.*/ + tags: + only: /^beta.*/ + context: napps + + # Manual triggers as separate workflows + manual-testflight-beta: + when: + and: + - << pipeline.parameters.run-testflight-beta >> + - not: << pipeline.parameters.is-release-pipeline >> + jobs: + - build-and-deploy-testflight-beta: + name: Manual Deploy beta version over Testflight + context: napps + + manual-firebase-deploy: + when: + and: + - << pipeline.parameters.run-firebase-deploy >> + - not: << pipeline.parameters.is-release-pipeline >> + jobs: + - build-and-deploy-firebase: + name: Manual Deploy ad-hoc version over Firebase + context: napps + + manual-browserstack-tests: + when: + and: + - << pipeline.parameters.run-browserstack-tests >> + - not: << pipeline.parameters.is-release-pipeline >> + jobs: + - build-and-deploy-browserstack: + name: Manual Upload to Browserstack + context: napps + + manual-release-deploy: + when: + and: + - << pipeline.parameters.run-release-deploy >> + - not: << pipeline.parameters.is-release-pipeline >> + jobs: + - manual-build-testflight-deploy: + name: Manual Deploy release version to TestFlight + context: napps + + # Manual full workflow for testing + manual-full-workflow: + when: + and: + - << pipeline.parameters.dry-run-mode >> + - not: << pipeline.parameters.is-release-pipeline >> + jobs: + # Always run deployment + - build-and-deploy-firebase-and-browserstack: + name: Manual Deploy ad-hoc version over Firebase + Upload to Browserstack + context: napps + + # Check if version was bumped and conditionally continue to release pipeline + - check-version-bump: + name: Manual Check for version bump + requires: + - Manual Deploy ad-hoc version over Firebase + Upload to Browserstack + context: napps diff --git a/.circleci/release-config.yml b/.circleci/release-config.yml new file mode 100644 index 0000000000000..3e65552079ede --- /dev/null +++ b/.circleci/release-config.yml @@ -0,0 +1,163 @@ +version: 2.1 + +# Pipeline parameters for release pipeline - must match main config +parameters: + run-browserstack-tests: + type: boolean + default: false + run-firebase-deploy: + type: boolean + default: false + run-testflight-beta: + type: boolean + default: false + run-release-deploy: + type: boolean + default: false + dry-run-mode: + type: boolean + default: false + description: "Enable dry-run mode to simulate all operations without actual deployments" + simulate-version-change: + type: boolean + default: false + description: "For dry-run mode: simulate version change (true) or no change (false)" + # Internal parameter - set when continuing to release pipeline + is-release-pipeline: + type: boolean + default: false + # BrowserStack URL passed from setup pipeline + browserstack-url: + type: string + default: "" + +environment_common: &environment_common + environment: + CI: true + +macos_common: &macos_common + macos: + xcode: "16.4" + resource_class: m4pro.medium + +jobs: + execute-critical-health-checks-ios: + docker: + - image: cimg/openjdk:17.0 + steps: + - checkout + - run: + name: Check if this is a dry-run + command: | + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Will simulate health checks without actual execution" + echo "export HEALTH_CHECKS_DRY_RUN=true" >> $BASH_ENV + else + echo "🚀 PRODUCTION MODE: Will run actual health checks" + echo "export HEALTH_CHECKS_DRY_RUN=false" >> $BASH_ENV + fi + - run: + name: Set BrowserStack URL from parameter + command: | + echo "export BROWSERSTACK_URL=<< pipeline.parameters.browserstack-url >>" >> $BASH_ENV + - run: + name: Confirm BrowserStack URL + command: | + source $BASH_ENV + echo "BROWSERSTACK_URL is: $BROWSERSTACK_URL" + - run: + name: Clone mobile acceptance tests (with dry-run support) + command: | + if [[ "$HEALTH_CHECKS_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating test repository clone" + echo "Would run: git clone https://github.com/ecosia/mobile-acceptance-testing.git" + else + echo "🚀 PRODUCTION: Cloning mobile acceptance tests" + git clone https://github.com/ecosia/mobile-acceptance-testing.git + fi + - run: + name: Run iOS tests on BrowserStack (with dry-run support) + command: | + source $BASH_ENV + if [[ "$HEALTH_CHECKS_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN: Simulating iOS health checks on BrowserStack" + echo "Would run: mvn test -PiOSCHCTests -DBStackIOSAppUrl=$BROWSERSTACK_URL" + echo "✅ SIMULATED: Health checks completed successfully" + else + echo "🚀 PRODUCTION: Running iOS tests on BrowserStack" + echo "Using App URL: $BROWSERSTACK_URL" + cd mobile-acceptance-testing + mvn test -PiOSCHCTests -DBStackIOSAppUrl="$BROWSERSTACK_URL" || { + echo "Health checks failed, but continuing with deployment..." + exit 0 + } + fi + - jira/notify: + issue_regexp: "([A-Za-z]{2,30}-[0-9]+)" + pipeline_id: << pipeline.id >> + pipeline_number: << pipeline.number >> + + build-testflight-deploy: + <<: *environment_common + <<: *macos_common + steps: + - checkout + - run: + name: Check if this is a dry-run + command: | + if [[ "<< pipeline.parameters.dry-run-mode >>" == "true" ]]; then + echo "🧪 DRY RUN MODE: Will not actually upload to TestFlight" + echo "export TESTFLIGHT_DRY_RUN=true" >> $BASH_ENV + else + echo "🚀 PRODUCTION MODE: Will upload to TestFlight" + echo "export TESTFLIGHT_DRY_RUN=false" >> $BASH_ENV + fi + - macos/switch-ruby: + version: "3.2" + - restore_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + - run: + name: Bundle install + command: bundle check || bundle install --path vendor/bundle + - save_cache: + key: 1-gems-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + - run: + name: verify github + command: for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts + - run: + name: extract Production XCConfig + command: echo "$PRODUCTION_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Production.xcconfig + - run: + name: Execute bootstrap + command: ./bootstrap.sh + - run: + name: Build and deploy to Testflight (with dry-run support) + no_output_timeout: 35m + command: | + if [[ "$TESTFLIGHT_DRY_RUN" == "true" ]]; then + echo "🧪 DRY RUN MODE: Building app but NOT uploading to TestFlight" + echo "Would run: bundle exec fastlane testflight_live" + echo "✅ Test successful - workflow reached TestFlight deployment step" + else + echo "🚀 PRODUCTION MODE: Building and uploading to TestFlight" + bundle exec fastlane testflight_live + fi + +orbs: + macos: circleci/macos@2 + jira: circleci/jira@2.0 + +workflows: + release-pipeline: + when: << pipeline.parameters.is-release-pipeline >> + jobs: + - execute-critical-health-checks-ios: + name: Critical Health Checks (release cut pipeline) + context: napps + - build-testflight-deploy: + name: Auto Deploy release version to TestFlight + requires: + - Critical Health Checks (release cut pipeline) + context: napps diff --git a/.githooks/pre-push b/.githooks/pre-push index 0656c03c7b064..7f049ee99beb8 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,56 +1,58 @@ #!/bin/sh -echo "Starting Swiftlint Check..." -swiftlint --strict -RESULT=$? - -if [ $RESULT -ne 0 ]; then - echo "Violations found. Please fix the issues before pushing." - exit 1 -else - echo "🎉 Hooray. No violation. You may proceed." -fi - -# get the URL of a remote -get_remote_url() { - remote_name=$1 - git remote get-url "$remote_name" -} - -# remote name associated with the restricted URL -find_remote_name_by_url() { - restricted_url="https://github.com/mozilla-mobile/firefox-ios.git" - for remote in $(git remote); do - remote_url=$(get_remote_url "$remote") - if [ "$remote_url" = "$restricted_url" ]; then - echo "$remote" - return - fi - done - echo "" -} - -# current tracked remote and branch for the current local branch -upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u}) -current_tracked_remote=$(echo "$upstream" | cut -d'/' -f1) -current_tracked_branch=$(echo "$upstream" | cut -d'/' -f2) - -echo "Current tracked remote: $current_tracked_remote" -echo "Current tracked branch: $current_tracked_branch" - -restricted_remote=$(find_remote_name_by_url) -echo "Restricted remote: $restricted_remote" - -# Special Case: remote being pushed to from the first argument of the pre-push hook -push_remote_my=$1 -echo "Push remote: $restricted_remote" - -if [ "$current_tracked_branch" = "main" ]; then - # if the push remote is the restricted remote - if [ "$push_remote_my" = "$restricted_remote" ]; then - echo "Direct pushes to the 'main' branch on the specified remote are not allowed." - exit 1 - fi -fi - -exit 0 \ No newline at end of file +## Ecosia: Comment out pre-commit hook. This is handled as a check step on PRs. + +# echo "Starting Swiftlint Check..." +# swiftlint --strict +# RESULT=$? + +# if [ $RESULT -ne 0 ]; then +# echo "Violations found. Please fix the issues before pushing." +# exit 1 +# else +# echo "🎉 Hooray. No violation. You may proceed." +# fi + +# # get the URL of a remote +# get_remote_url() { +# remote_name=$1 +# git remote get-url "$remote_name" +# } + +# # remote name associated with the restricted URL +# find_remote_name_by_url() { +# restricted_url="https://github.com/mozilla-mobile/firefox-ios.git" +# for remote in $(git remote); do +# remote_url=$(get_remote_url "$remote") +# if [ "$remote_url" = "$restricted_url" ]; then +# echo "$remote" +# return +# fi +# done +# echo "" +# } + +# # current tracked remote and branch for the current local branch +# upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u}) +# current_tracked_remote=$(echo "$upstream" | cut -d'/' -f1) +# current_tracked_branch=$(echo "$upstream" | cut -d'/' -f2) + +# echo "Current tracked remote: $current_tracked_remote" +# echo "Current tracked branch: $current_tracked_branch" + +# restricted_remote=$(find_remote_name_by_url) +# echo "Restricted remote: $restricted_remote" + +# # Special Case: remote being pushed to from the first argument of the pre-push hook +# push_remote_my=$1 +# echo "Push remote: $restricted_remote" + +# if [ "$current_tracked_branch" = "main" ]; then +# # if the push remote is the restricted remote +# if [ "$push_remote_my" = "$restricted_remote" ]; then +# echo "Direct pushes to the 'main' branch on the specified remote are not allowed." +# exit 1 +# fi +# fi + +# exit 0 \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 482af23e194d7..0000000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,24 +0,0 @@ -# Global owners, PRs needs an approval from a team member from fxios-eng by default -* @mozilla-mobile/fxios-eng - -# Order is important; the last matching pattern takes the most -# precedence. - -# When someone opens a pull request that only modifies those folders / files, -# those specific persons will be asked for review and not the global owners. -/firefox-ios/Client/Experiments/initial_experiments.json @afurlan-firefox -/firefox-ios/Client/Assets/CC_Script @nbhasin2 @issammani - -# When someone opens a pull request that only modifies those folders / files, -# only @mozilla-mobile/fxios-automation and not the global -# owners will be requested for a review. -/firefox-ios/firefox-ios-tests/Tests/UITests @mozilla-mobile/fxios-automation-1 -/firefox-ios/firefox-ios-tests/Tests/XCUITests @mozilla-mobile/fxios-automation-1 -/focus-ios/focus-ios-tests @mozilla-mobile/fxios-automation-1 -/focus-ios/focus-ios-tests/ClientTests @mozilla-mobile/fxios-eng -bitrise.yml @mozilla-mobile/fxios-automation-1 -.taskcluster.yml @mozilla-mobile/fxios-automation-1 -/taskcluster @mozilla-mobile/fxios-automation-1 -.github/workflows @mozilla-mobile/fxios-automation-1 -/test-fixtures @mozilla-mobile/fxios-automation-1 -/test-fixtures/generate-metrics.sh @mozilla-mobile/fxios-eng \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index e7d30b30adf50..55dd1689262de 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -12,9 +12,7 @@ labels: "Bug 🐞" ### Actual behavior ### Device & build information -* Device: ? -* Build version: ? -* First seen version: ? +* Device: ? +* Build version: ? ### Notes Attachments: - diff --git a/.github/ISSUE_TEMPLATE/---crash-report.md b/.github/ISSUE_TEMPLATE/---crash-report.md deleted file mode 100644 index 702c3f211c0a8..0000000000000 --- a/.github/ISSUE_TEMPLATE/---crash-report.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: "💥 Crash report" -about: Create a report to help us improve -labels: "Crash" - ---- -## Logs -You can help us by sending us either your crash logs or Firefox logs. Instructions on how to do this [here](https://github.com/mozilla-mobile/firefox-ios/wiki/How-to-get-your-device-logs). - -## Steps to reproduce - -### Expected behavior - -### Actual behavior - -### Device & build information -* Device: ? -* Build version: ? - -### Notes -Attachments: diff --git a/.github/ISSUE_TEMPLATE/release_checklist_template.md b/.github/ISSUE_TEMPLATE/release_checklist_template.md index 4c0fd2abefb8d..bb34569a0396c 100644 --- a/.github/ISSUE_TEMPLATE/release_checklist_template.md +++ b/.github/ISSUE_TEMPLATE/release_checklist_template.md @@ -6,22 +6,33 @@ labels: '' assignees: '' --- -/date - -The following lists the actions to be taken by developers. For the full list of action taken by both release management and the developer team, please look at the documentation [here](https://github.com/mozilla-mobile/firefox-ios/wiki/Release-Checklist). +Soft freeze: +Hard freeze: ## Soft Freeze Items +- [ ] Update Version Number in code (Eng task, use `update_version.sh`) +- [ ] Update `Client/Info.plist` MozWhatsNewTopic (Eng task) +- [ ] Create version specific Sentry project +- [ ] Update SENTRY_DSN secret in Bitrise (per release) +- [ ] Add Bitrise trigger for release branch -- [ ] Once release management has created the release branch, notify the team to aim new PRs at the updated fix version by updating the `Current PR Version` topic in the #ios-watercooler channel. -- [ ] Back ports bug fixes commits towards the release branch. Make sure to mark any tickets you [back port](https://github.com/mozilla-mobile/firefox-ios/wiki/Release-Checklist/_edit#back-port) with the proper `fix version` field in the Jira ticket, and mention any particularities QA need to be careful about when testing. -- [ ] Update the PI request. Release management won't update the PI request with comments on what was back ported, so make sure you update the PI request with the latest information as well. Note that builds are preplanned each week to happen on Tuesday, Thursday and Sunday. -- [ ] Update Security Advisories if needed (see Security Advisories [page](https://github.com/mozilla-mobile/firefox-ios/wiki/Security-Advisories). - -### Notes for Hotfixes/Dot releases/Rapid releases +### Soft Freeze Optional +These items should be completed as soon as possible, preferrably on soft code +freeze day. Due to timing issues, they may not be available until hard code +freeze, and must be completed on hard freeze. -For hotfixes, dot releases or rapid releases release management can handle the PI request updates since the number of back ports are normally low. +- [ ] Ensure string export was completed for L10N (if needed) +- [ ] Check for [security advisories](https://github.com/mozilla-mobile/firefox-ios/wiki/Release-Build-Checklist/#security-advisories) +- [ ] File P.I. request ## Hard Freeze Items -- [ ] Fix Release Blockers raised by the QA team. As QA regression tests, they'll open GitHub issues. Watch for new issues and ask Product which could be release blockers. After QA is done testing, you'll get a test report email indicating if the build is green/red. If it's red, there will be a list of critical issues that need to be addressed and back ported. See section about [back ports](https://github.com/mozilla-mobile/firefox-ios/wiki/Release-Checklist/_edit#back-port) for more information. -- [ ] Monitor crash rate through Xcode and Sentry. +- [ ] Tag release in GitHub (Eng task) + - [ ] Link to commit diff between versions + - [ ] Add volunteer contributions +- [ ] Release Notes updated +- [ ] Get App Store screenshots and all locales if necessary +- [ ] Submit build to Apple +- [ ] Release with 7-day rollout + +See [Release build checklist wiki](https://github.com/mozilla-mobile/firefox-ios/wiki/Release-Build-Checklist-Details) for more detailed instructions. diff --git a/.github/actions/evaluate_tests_to_execute/action.yml b/.github/actions/evaluate_tests_to_execute/action.yml new file mode 100644 index 0000000000000..3dc67cc96c1eb --- /dev/null +++ b/.github/actions/evaluate_tests_to_execute/action.yml @@ -0,0 +1,27 @@ +name: 'Evaluate Tests to execute' +description: 'This action contains all the usual steps needed to evaluate the tests to execute based on the files changed' + +outputs: + shared-module-changed: + description: "Flag stating that there have been changes in the shared module" + value: ${{ steps.filter-tests.outputs.shared-module-changed }} + storage-module-changed: + description: "Flag stating that there have been changes in the storage module" + value: ${{ steps.filter-tests.outputs.storage-module-changed }} + client-module-changed: + description: "Flag stating that there have been changes in the client module" + value: ${{ steps.filter-tests.outputs.client-module-changed }} + +runs: + using: "composite" + steps: + - uses: dorny/paths-filter@v2 + id: filter-tests + with: + filters: | + shared-module-changed: + - 'Shared/**' + storage-module-changed: + - 'Storage/**' + client-module-changed: + - 'Client/**' diff --git a/.github/actions/perform_snapshot_tests/action.yml b/.github/actions/perform_snapshot_tests/action.yml new file mode 100644 index 0000000000000..7490ac102f176 --- /dev/null +++ b/.github/actions/perform_snapshot_tests/action.yml @@ -0,0 +1,42 @@ +name: 'Perform Snapshot Tests' +description: 'This action contains all the usual steps needed to perform the snapshot tests' + +inputs: + github-token: + description: 'The GitHub Token' + required: true + +runs: + using: "composite" + steps: + - name: Set up Homebrew and Install Dependencies + shell: bash + run: | + # Update Homebrew to ensure formulae are up to date + brew update + + # Install jq and the latest bash + brew install jq bash + + # Determine Homebrew's installation prefix + HOMEBREW_PREFIX=$(brew --prefix) + + # Add Homebrew's bash to the beginning of PATH + export PATH="$HOMEBREW_PREFIX/bin:$PATH" + + # Verify the installation and Bash version + echo "Bash version:" + bash --version + + echo "jq version:" + jq --version + + - name: Run Snapshot Tests + shell: bash + run: ./perform_snapshot_tests.sh "EcosiaTests/SnapshotTests/snapshot_configuration.json" "EcosiaTests/SnapshotTests/environment.json" "EcosiaTests/Results" "EcosiaSnapshotTests" + + - name: Upload test result as artifact + uses: actions/upload-artifact@v4 + with: + name: test-result + path: 'firefox-ios/EcosiaTests/Results/all_tests.xcresult' \ No newline at end of file diff --git a/.github/actions/perform_unit_tests/action.yml b/.github/actions/perform_unit_tests/action.yml new file mode 100644 index 0000000000000..d43f0d7eb5299 --- /dev/null +++ b/.github/actions/perform_unit_tests/action.yml @@ -0,0 +1,51 @@ +name: 'Perform Unit Tests' +description: 'This action contains all the usual steps needed to perform the unit tests' + +inputs: + github-token: + description: 'The Github Token' + required: true + +runs: + using: "composite" + steps: + + - name: Install xcpretty and prepare output directory + shell: bash + run: | + gem install xcpretty + mkdir -p test_output/xml + + - name: Run tests with xcodebuild and xcpretty + shell: bash + run: | + set -o pipefail && xcodebuild -scheme Ecosia \ + -project firefox-ios/Client.xcodeproj \ + -configuration Testing \ + -enableCodeCoverage NO \ + -destination 'platform=iOS Simulator,name=iPhone 16' \ + -clonedSourcePackagesDirPath firefox-ios/SourcePackages \ + -derivedDataPath DerivedData \ + build-for-testing | xcpretty --color + + set -o pipefail && xcodebuild -scheme Ecosia \ + -project firefox-ios/Client.xcodeproj \ + -configuration Testing \ + -enableCodeCoverage NO \ + -testPlan UnitTest \ + -destination 'platform=iOS Simulator,name=iPhone 16' \ + -resultBundlePath TestResults.xcresult \ + -derivedDataPath DerivedData \ + test-without-building | xcpretty --color --report junit --output test_output/xml/report.junit || true + + - name: Publish Test Report + if: always() + uses: mikepenz/action-junit-report@v5 + with: + report_paths: '**/test_output/xml/report.junit' + github_token: ${{ inputs.github-token }} + check_name: 'iOS Unit Test Results' + require_tests: false + include_passed: true + detailed_summary: true + fail_on_failure: false \ No newline at end of file diff --git a/.github/actions/prepare_environment/action.yml b/.github/actions/prepare_environment/action.yml new file mode 100644 index 0000000000000..17b04fd3dce52 --- /dev/null +++ b/.github/actions/prepare_environment/action.yml @@ -0,0 +1,116 @@ +name: 'Prepare environment' +description: 'This action contains all the usual steps needed to setup the environment and restore caches when available' + +inputs: + core-token: + description: 'The iOS core token' + required: true + +runs: + using: "composite" + steps: + + - name: Xcode Select Version + uses: mobiledevops/xcode-select-version-action@v1 + with: + xcode-select-version: 16.4 + + - name: Check Xcode and Swift Version + shell: bash + run: | + echo "Checking Xcode and Swift version" + xcodebuild -version + swift --version + + - name: Fix SwiftBridging Module Redefinition + shell: bash + run: | + echo "Checking for duplicate SwiftBridging module" + BRIDGING_MODULEMAP="/Applications/Xcode_16.4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/swift/bridging.modulemap" + if [ -f "$BRIDGING_MODULEMAP" ]; then + echo "Backing up and fixing bridging.modulemap" + sudo cp "$BRIDGING_MODULEMAP" "${BRIDGING_MODULEMAP}.bak" + # Comment out the SwiftBridging module definition to prevent redefinition + sudo sed -i.tmp 's/^module SwiftBridging {/\/\* module SwiftBridging {/g' "$BRIDGING_MODULEMAP" + sudo sed -i.tmp 's/^}/} \*\//g' "$BRIDGING_MODULEMAP" + cat "$BRIDGING_MODULEMAP" + else + echo "bridging.modulemap not found at expected location" + fi + + - name: Fix-up private Github URLs + shell: bash + run: find . -type f \( -name '*.pbxproj' -o -name 'Package.swift' -o -name 'Package.resolved' \) -exec sed -i '' "s/https:\/\/github.com\/${GITHUB_REPOSITORY_OWNER}/https:\/\/${GITHUB_ACCESS_TOKEN}@github.com\/${GITHUB_REPOSITORY_OWNER}/g" {} \; + env: + GITHUB_ACCESS_TOKEN: ${{ inputs.core-token }} + + - uses: actions/setup-python@v4 + with: + python-version: '3.12.2' + cache: 'pip' + + - name: Install gems + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.3 + bundler-cache: true + + - name: Install npm + uses: bahmutov/npm-install@v1 + + - name: Restore SPM Cache + id: restore-spm-cache + uses: actions/cache/restore@v3 + with: + path: SourcePackages/ + key: ${{ runner.os }}-spm-cache-${{ hashFiles('firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }} + + - name: Install SPM if cache not found + shell: bash + if: steps.restore-spm-cache.outputs.cache-hit != 'true' + run: xcodebuild -resolvePackageDependencies -clonedSourcePackagesDirPath "firefox-ios/SourcePackages/" + + - name: Save SPM Cache if needed + if: steps.restore-spm-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v3 + with: + path: firefox-ios/SourcePackages/ + key: ${{ runner.os }}-spm-cache-${{ hashFiles('firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }} + + - name: Extract Staging XCConfig + shell: bash + run: echo "$STAGING_XCCONFIG" | base64 --decode > firefox-ios/Client/Configuration/Staging.xcconfig + + - name: List Available Simulator Runtimes + shell: bash + run: | + echo "Available simulator runtimes:" + xcrun simctl list runtimes + + - name: Create and Boot iPhone 16 Simulator + shell: bash + run: | + # Check if simulator already exists + if xcrun simctl list devices | grep -q "iPhone 16"; then + echo "iPhone 16 simulator already exists" + else + echo "Creating iPhone 16 simulator" + # Get the latest available iOS runtime + LATEST_RUNTIME=$(xcrun simctl list runtimes | grep iOS | tail -1 | awk '{print $NF}') + # Get the device type for iPhone 16 + DEVICE_TYPE=$(xcrun simctl list devicetypes | grep "iPhone 16" | head -1 | awk '{print $NF}') + + if [ -n "$LATEST_RUNTIME" ] && [ -n "$DEVICE_TYPE" ]; then + xcrun simctl create "iPhone 16" "$DEVICE_TYPE" "$LATEST_RUNTIME" + else + echo "Error: Could not determine runtime or device type" + exit 1 + fi + fi + + # Boot the simulator + xcrun simctl boot "iPhone 16" || echo "Simulator may already be booted" + + - name: Run bootstrap script + shell: bash + run: ./bootstrap.sh \ No newline at end of file diff --git a/.github/actions/restore_derived_data/action.yml b/.github/actions/restore_derived_data/action.yml new file mode 100644 index 0000000000000..0604395c364fa --- /dev/null +++ b/.github/actions/restore_derived_data/action.yml @@ -0,0 +1,21 @@ +name: 'Restore Derived data' +description: 'This action contains all the logic needed to restore the derived data cache' + +runs: + using: "composite" + steps: + - name: Get Derived Data cache expiration Date + shell: bash + id: check-derived-data-cache-date + run: | + echo "::set-output name=date::$(date -v-5d '+%Y-%m-%d')" + + - name: Restore derived data main branch cache + uses: actions/cache/restore@v3 + with: + path: DerivedData/ + key: ${{ runner.os }}-derived-data-cache-${{ steps.check-derived-data-cache-date.outputs.date }} + + - name: Untar derived data main brach + shell: bash + run: if [ -f DerivedData/dd.tar ]; then tar xvPpf DerivedData/dd.tar; else echo "No cache file"; fi \ No newline at end of file diff --git a/.github/actions/save_derived_data/action.yml b/.github/actions/save_derived_data/action.yml new file mode 100644 index 0000000000000..77e6bb13506a0 --- /dev/null +++ b/.github/actions/save_derived_data/action.yml @@ -0,0 +1,38 @@ +name: 'Cache Derived data' +description: 'This action contains all the logic needed to cache the derived data' + +runs: + using: "composite" + steps: + - name: Get Derived Data cache expiration Date + shell: bash + id: check-derived-data-cache-date + run: | + echo "::set-output name=date::$(date -v-5d '+%Y-%m-%d')" + + - name: Restore derived data main branch cache + id: restore-derived-data-cache + uses: actions/cache/restore@v3 + with: + path: DerivedData/ + key: ${{ runner.os }}-derived-data-cache-${{ steps.check-derived-data-cache-date.outputs.date }} + lookup-only: true + + - name: Tar derived data + if: steps.restore-derived-data-cache.outputs.cache-hit != 'true' + shell: bash + run: mkdir -p DerivedData && tar cfPp DerivedData/dd.tar --format posix BuildDerivedData/ + + - name: Get today date + if: steps.restore-derived-data-cache.outputs.cache-hit != 'true' + shell: bash + id: get-today-date + run: | + echo "::set-output name=today-date::$(date -v-5d '+%Y-%m-%d')" + + - name: Cache derived data main branch + if: steps.restore-derived-data-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v3 + with: + path: DerivedData/ + key: ${{ runner.os }}-derived-data-cache-${{ steps.get-today-date.outputs.today-date }} \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000000..7bd40795b2ded --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,108 @@ +# Ecosia iOS Browser — Copilot Instructions + +## Architecture Overview + +This is a **fork of Mozilla Firefox iOS** with Ecosia-specific customizations layered on top. Code changes touch both Firefox core files and Ecosia-owned modules. Always consider this dual-origin when making changes — minimize modifications to Firefox core to reduce upstream merge conflicts. + +### Key Directories + +| Path | Purpose | +|---|---| +| `firefox-ios/Ecosia/` | **Ecosia Framework** — isolated Ecosia logic (analytics, core models, networking, experiments, UI components). New Ecosia code goes here. | +| `firefox-ios/Client/Ecosia/` | Ecosia code within the Client target (extensions on Firefox classes, NTP, settings, onboarding). Legacy location — prefer the framework when possible. | +| `firefox-ios/Client/` | Firefox Client app — Ecosia modifications are marked with `// Ecosia:` comments. | +| `firefox-ios/EcosiaTests/` | Ecosia unit tests and snapshot tests. | +| `BrowserKit/` | Shared Swift package (Common, Redux, SiteImageView, TabDataStore, WebEngine, etc.). | +| `docs/decisions/` | Architecture Decision Records (ADRs). | + +### Core Ecosia Modules (inside `firefox-ios/Ecosia/`) + +- **Core/** — `User`, `Environment`, `HTTPClient`, `Statistics`, `Referrals`, `SearchesCounter`, `Navigation` +- **Analytics/** — Snowplow-based analytics via `Analytics.shared` (do not reassign outside tests — enforced by SwiftLint) +- **Experiments/Unleash/** — Feature flags via Unleash (`Unleash.start(..., appVersion:)`) +- **Braze/** — Push notification integration (`BrazeService`) +- **UI/** — SwiftUI/UIKit design system, NTP components, settings, onboarding + +### Extension Pattern for Firefox Customization + +Ecosia extends Firefox classes via `+Ecosia` extension files in `firefox-ios/Client/Ecosia/Extensions/` (e.g., `BrowserViewController+Ecosia.swift`, `HomepageViewController+Ecosia.swift`). Use this pattern to add behavior to Firefox classes without modifying their original files. + +### Environment Detection + +Environments are detected by bundle ID in `firefox-ios/Ecosia/Core/Environment/Environment.swift`: +- `com.ecosia.ecosiaapp` → production +- `com.ecosia.ecosiaapp.firefox` → staging +- Any other → debug + +## Commenting Guidelines for Ecosia Code in Firefox + +When modifying Firefox core files for Ecosia customizations: + +1. **One-liner** (`//`) — for introducing new Ecosia code: + ```swift + // Ecosia: Update appversion predicate + let appVersionPredicate = (appVersionString?.contains("Ecosia") ?? false) == true + ``` + +2. **Block comment** (`/* */`) — when commenting out original Firefox code: + ```swift + /* Ecosia: Swap Theme Manager with Ecosia's + lazy var themeManager: ThemeManager = DefaultThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + */ + lazy var themeManager: ThemeManager = EcosiaThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + ``` + +This makes upstream merges easier — the original Firefox code is visible inside the block comment. + +## Build & Development + +1. **Bootstrap**: `sh ./bootstrap.sh` (installs hooks, resolves packages, updates content blockers, creates `Staging.xcconfig`) +2. **Xcode project**: Open `firefox-ios/Client.xcodeproj`, select the **Ecosia** scheme +3. **SwiftLint**: Runs on PR via GitHub Actions (`swiftlint --strict`). Config in `.swiftlint.yml` uses `only_rules` + `baseline` to avoid upstream noise. Lint locally: `swiftlint` +4. **User Scripts** (JS injected into WKWebView): Compiled with webpack. + - Source: `firefox-ios/Client/Frontend/UserContent/UserScripts/` + - Output: `firefox-ios/Client/Assets/` (`AllFramesAtDocumentEnd.js`, etc.) + - Rebuild: `npm run build` from the project root +5. **Unit tests**: Run against the **EcosiaBeta** scheme (`Cmd+U` in Xcode). Test plan: `firefox-ios/EcosiaTests/UnitTest.xctestplan` +6. **CI**: Bitrise for full builds; GitHub Actions for SwiftLint, merge unit tests, and snapshot tests + +## File Header Requirement + +All Swift files must start with the MPL-2.0 header (enforced by SwiftLint `file_header` rule): +```swift +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ +``` + +## Testing + +- Write comprehensive tests for all new Ecosia code in `firefox-ios/EcosiaTests/` +- **Snapshot tests**: Use `SnapshotTestHelper` for UI snapshot testing. Config in `firefox-ios/EcosiaTests/SnapshotTests/snapshot_configuration.json`. See `firefox-ios/Ecosia/Ecosia.docc/SNAPSHOT_TESTING_WIKI.md` +- **Analytics**: `Analytics.shared` must not be reassigned outside tests (SwiftLint custom rule `reassign_analytics_instance`) +- **Mocks**: Place test mocks in `firefox-ios/EcosiaTests/Mocks/` + +## Translations + +- Managed via **Transifex** +- Add new English strings to `firefox-ios/Client/Ecosia/L10N/en.lproj/Ecosia.strings` +- After upstream merges, rebrand Mozilla strings: `python3 ecosify-strings.py firefox-ios` + +## PR & Branch Naming + +- PR title: `[MOB-XXXX] {name of the feature}` (ticket reference from Jira) +- Branch name must include `MOB-XXXX` (e.g., `mob-1234/feature-name`) +- No ticket? Use `NOTICKET` in PR title and `noticket` in branch name + +## Architecture Decision Records (ADRs) + +- Store in `docs/decisions/` using MADR format +- Naming: `NNNN-short-title.md` (e.g., `0001-swiftlint-configuration-for-upstream-fork.md`) +- Update `docs/decisions/README.md` index when adding new ADRs +- Document unsolved issues and considered alternatives + +## Documentation + +- Keep `firefox-ios/Ecosia/Ecosia.docc/Ecosia.md` up to date with architecture changes +- Do **not** update the root `/README.md` — it belongs to Firefox core +- Add `README.md` files to folders that are created or heavily modified during feature work diff --git a/.github/workflows/check-rust-component-dependency.yml b/.github/workflows/check-rust-component-dependency.yml deleted file mode 100644 index 93ce27a36443f..0000000000000 --- a/.github/workflows/check-rust-component-dependency.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Create a PR to build the app using newest a-s version available -on: - schedule: - - cron: '0 15 * * *' - workflow_dispatch: - inputs: - branchName: - description: 'Branch used as target for automation' - required: true - default: 'main' -jobs: - build: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.7] - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r ./test-fixtures/requirements.txt - - - name: Firefox-ios - Modifing Swift Package dependencies - run: | - python ./test-fixtures/update-rust-component-version.py - - - name: Get new A-S tag to be used in the PR info - run: | - cd test-fixtures/ - chmod u+x read-rust-component-tag.sh - echo "rust_version=$(./read-rust-component-tag.sh)" >> $GITHUB_ENV - - - name: Remove temp file created to store the tag info - run: | - cd test-fixtures/ - [ ! -e newest_tag.txt ] || rm newest_tag.txt - - - name: Script to check if branch exists to not commit again - run: |- - branch=$(curl -X GET -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/mozilla-mobile/firefox-ios/branches?per_page=100 | jq -r '.[].name | select(contains("update-spm-new-rust-component-tag-${{ env.rust_version }}"))') - echo $branch - if [ -z "$branch" ]; then echo "BRANCH_CREATED=false" >> $GITHUB_ENV; else echo "BRANCH_CREATED=true" >> $GITHUB_ENV;fi - - - name: Determine PR Version Number - id: versioning - run: | - # This step is used to determine the next version number for the PR title - # The output includes debugging for the piped commands that generate - # the version number and the last line is the version number itself - - output=$(bash test-fixtures/ci/get-next-pr-version) - echo "$output" - next_version=$(echo "$output" | tail -n 1) # get the last line of the output - echo "Next Firefox iOS version is: v${next_version}" - echo "next_app_version=${next_version}" >> $GITHUB_ENV - - - name: Update rust-component version - if: env.BRANCH_CREATED == 'false' - run: |- - git diff - git diff --quiet || (git add firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved firefox-ios/Client.xcodeproj/project.pbxproj focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved focus-ios/Blockzilla.xcodeproj/project.pbxproj) - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - if: env.BRANCH_CREATED == 'false' - with: - commit-message: Auto update SPM with latest rust-component release ${{ env.rust_version }} - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - title: Refactor [v${{ env.next_app_version }}] Auto update SPM with latest rust-component ${{ env.rust_version }} - branch: update-spm-new-rust-component-tag-${{ env.rust_version }} - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/firefox-ios-autofill-playwrite-tests.yml b/.github/workflows/firefox-ios-autofill-playwrite-tests.yml deleted file mode 100644 index f648f274ee992..0000000000000 --- a/.github/workflows/firefox-ios-autofill-playwrite-tests.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Build and Run Autofill Automation -permissions: read-all -on: - workflow_call: - secrets: - SLACK_WEBHOOK_URL: - required: true - workflow_dispatch: {} - pull_request: - paths: - - 'firefox-ios/Client/Assets/CC_Script/**' - push: - branches: ['main'] - paths: - - 'firefox-ios/Client/Assets/CC_Script/**' -jobs: - build: - runs-on: macos-13 - timeout-minutes: 40 - strategy: - matrix: - python-version: [3.9] - xcode: ["15.2"] - continue-on-error: true - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Get the current github action name - run: | - github_action_name="Update credential provider script" - echo "github_action_name=$github_action_name" >> $GITHUB_ENV - - name: Clone repository - run: | - git clone https://github.com/issammani/test-playwright.git - - name: Install node - uses: actions/setup-node@v4 - with: - node-version: 18 - - run: | - echo "Install dependencies and run tests" - npm i - npm install --save concurrently - npm i -D @playwright/test - - cd test-playwright - echo "Install playwright" - npx playwright install - - echo "Run tests" - npm test - - name: Set job log URL - if: always() - run: echo "JOB_LOG_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_ENV - - name: Send Slack notification if tests fail - if: '${{ !cancelled() && !github.event.pull_request.head.repo.fork }}' - id: slack - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: "./test-fixtures/ci/slack-notification-payload-autofill-test.json" - env: - JOB_STATUS: ${{ job.status == 'success' && ':white_check_mark:' || job.status == 'failure' && ':x:' }} - JOB_STATUS_COLOR: ${{ job.status == 'success' && '#36a64f' || job.status == 'failure' && '#FF0000' }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK diff --git a/.github/workflows/firefox-ios-build-contributor-pr.yml b/.github/workflows/firefox-ios-build-contributor-pr.yml deleted file mode 100644 index 8acea6a7f0358..0000000000000 --- a/.github/workflows/firefox-ios-build-contributor-pr.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Build contributor PR -permissions: read-all -on: [pull_request] -jobs: - run-ui: - if: github.event.pull_request.head.repo.full_name != github.repository - runs-on: macos-11 - timeout-minutes: 60 - strategy: - matrix: - python-version: [3.9] - xcode: ["13.1"] - run-config: - - { scheme: 'Fennec_Enterprise_XCUITests', destination: 'platform=iOS Simulator,OS=latest,name=iPhone 11', testplan: 'Smoketest1' } - name: Run UI Smoketests - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Select Xcode ${{ matrix.xcode }} - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Run Boostrap - run: sh ./bootstrap.sh - env: - GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Build and Test - run: xcodebuild clean test -scheme '${{ matrix.run-config['scheme'] }}' -destination '${{ matrix.run-config['destination'] }}' -testPlan '${{ matrix.run-config['testplan'] }}' -resultBundlePath TestResults -derivedDataPath results - - name: Archive Results - if: ${{ always() }} - run: zip -r results.zip TestResults.xcresult - - uses: actions/upload-artifact@v4 - name: Upload XCResult - if: ${{ always() }} - with: - name: xcresult - path: results.zip - retention-days: 90 diff --git a/.github/workflows/firefox-ios-import-strings.yml b/.github/workflows/firefox-ios-import-strings.yml deleted file mode 100644 index bdd7615190052..0000000000000 --- a/.github/workflows/firefox-ios-import-strings.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: "Firefox: import translations" -on: - schedule: - - cron: '0 11 * * 1' - workflow_dispatch: - inputs: - branchName: - description: 'Branch used as target for automation' - required: true - default: 'main' -jobs: - build: - runs-on: macos-13 - strategy: - max-parallel: 4 - matrix: - python-version: [3.9] - xcode: ["15.0.1"] - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - token: ${{ secrets.GITHUB_TOKEN }} - ref: ${{ github.event.inputs.branchName }} - - - name: Select Xcode ${{ matrix.xcode }} - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Determine PR Version Number - id: versioning - run: | - # This step is used to determine the next version number for the PR title - # The output includes debugging for the piped commands that generate - # the version number and the last line is the version number itself - - output=$(bash test-fixtures/ci/get-next-pr-version) - echo "$output" - next_version=$(echo "$output" | tail -n 1) # get the last line of the output - echo "Next version is: v${next_version}" - echo "next_version=${next_version}" >> $GITHUB_ENV - - - name: Get PR info - run: | - current_date=$(date +"%Y-%m-%d") - # Use 'main' when triggered via cron - current_branch=${{ github.event.inputs.branchName || 'main' }} - echo "current_date=$current_date" >> $GITHUB_ENV - if [[ $current_branch == 'main' ]]; then - echo "branch_name=string-import-$current_date" >> $GITHUB_ENV - echo "pr_title=Localize [v${{ env.next_version }}] String import $current_date" >> $GITHUB_ENV - echo "pr_body=This automated PR imports string changes" >> $GITHUB_ENV - else - # version: v105.0 -> v105 - version=${current_branch%??} - echo "branch_name=string-import-$current_branch-$current_date" >> $GITHUB_ENV - echo "pr_title=Localize [$version] String import $current_date" >> $GITHUB_ENV - echo "pr_body=This automated PR imports string changes into branch '$current_branch'" >> $GITHUB_ENV - fi - - - name: Run script to import strings - run: sh ./bootstrap.sh --importLocales - - - name: Update new strings - run: |- - git diff || (git add firefox-ios/Shared/*/*.lproj/* firefox-ios/Shared/*.lproj/* firefox-ios/WidgetKit/*.lproj/* firefox-ios/Client/*/*.lproj/* firefox-ios/Client/*.lproj/*) - git restore firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: ${{ env.pr_title }} - title: "Firefox Localize [v${{ env.next_version }}] String import ${{ env.current_date }}" - branch: ${{ env.branch_name }} - body: ${{ env.pr_body }} diff --git a/.github/workflows/firefox-ios-publish-docc.yml b/.github/workflows/firefox-ios-publish-docc.yml deleted file mode 100644 index 9e233bb97253b..0000000000000 --- a/.github/workflows/firefox-ios-publish-docc.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Deploy Docc -on: - # Runs on pushes where BrowserKit files are modified - push: - paths: - - BrowserKit/Sources/ComponentLibrary/** - workflow_dispatch: {} # adding the workflow_dispatch so it can be triggered manually - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - # Single deploy job since we're just deploying - deploy: - environment: - # Must be set to this for deploying to GitHub Pages - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: macos-13 - steps: - - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.0' - - name: Checkout 🛎️ - uses: actions/checkout@v4 - - name: Build DocC - run: | - xcodebuild docbuild -project firefox-ios/Client.xcodeproj \ - -scheme ComponentLibrary \ - -derivedDataPath /tmp/docbuild \ - -destination 'generic/platform=iOS'; - xcrun docc process-archive transform-for-static-hosting \ - /tmp/docbuild/Build/Products/Debug-iphoneos/ComponentLibrary.doccarchive \ - --hosting-base-path "firefox-ios" \ - --output-path docs; - echo "" > docs/index.html; - echo "Building DocC step done" - - name: Upload artifact - uses: actions/upload-pages-artifact@v1 - with: - # Upload only docs directory - path: 'docs' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 diff --git a/.github/workflows/firefox-ios-stale.yml b/.github/workflows/firefox-ios-stale.yml deleted file mode 100644 index 0bd91ea178746..0000000000000 --- a/.github/workflows/firefox-ios-stale.yml +++ /dev/null @@ -1,65 +0,0 @@ -# Close Stale Issues -# Warns and then closes issues and PRs that have had no activity for a specified amount of time. -# https://github.com/actions/stale - -name: Close Stale Issues -on: - schedule: - - cron: "0 0 * * *" # Once per day at midnight - workflow_dispatch: {} - -jobs: - stale: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: actions/stale@v9 - with: - ### GENERAL ### - # Max number of operations per run - operations-per-run: 1000 - - # Change the order used to fetch the issues and pull requests from GitHub. Going with older ones. - ascending: true - - ### ISSUES ### - # Idle number of days before marking issues stale, set `-1` to disable - # 350 days (a little less than a year) for now, can be evaluated later if needs to be lowered more - days-before-issue-stale: 350 - - # Idle number of days before closing stale issues - days-before-issue-close: 30 - - # Comment on the staled issues - stale-issue-message: "This issue has been automatically marked as stale. Has the issue been fixed, or does it still require the community's attention? Please leave any comment to keep this issue opened. It will be closed automatically if no further update occurs in the next 30 days. Thank you for your contributions!" - - # Comment on the closed stale issues - close-issue-message: "Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue linking to this one, with up-to-date information. Thank you!" - - # Reason to use when closing issues - close-issue-reason: not_planned - - # Labels on issues exempted from stale - exempt-issue-labels: "do not stale,Feature-Request" - - # Label to apply on staled issues - stale-issue-label: "stale" - - ### PULL REQUESTS ### - # Idle number of days before marking PRs stale - # We need to review PRs faster than 2 weeks. If there's no action on a PR for this period of time we should close it. - days-before-pr-stale: 14 - - # Idle number of days before closing stale PRs - days-before-pr-close: 7 - - # Comment on the staled PRs - stale-pr-message: "This PR has been automatically marked as stale. Please leave any comment to keep this PR opened. It will be closed automatically if no further update occurs in the next 7 days. Thank you for your contributions!" - - # Label to apply on staled PRs - stale-pr-label: "stale" - - # Labels on PRs exempted from stale - exempt-pr-labels: "do not stale,weekly-release" diff --git a/.github/workflows/firefox-ios-sync-acorn-icons.yml b/.github/workflows/firefox-ios-sync-acorn-icons.yml deleted file mode 100644 index 448b3b7cef284..0000000000000 --- a/.github/workflows/firefox-ios-sync-acorn-icons.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Sync Icons from Acorn repo - -on: - workflow_dispatch: - schedule: - - cron: '0 12 * * SUN' - -jobs: - download_icons_if_needed: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install requests - - - name: Download Acorn icons - run: | - python sync_acorn_icons.py - - - name: Check changes - run: | - if [[ $(git status --porcelain) ]]; then - echo "changes_detected=true" >> $GITHUB_ENV - fi - - - name: Commit if needed - if: ${{ env.changes_detected }} - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git add latest_acorn_release.json firefox-ios/Client/Assets/Images.xcassets/ BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift - git commit -m "Updated Icons from Acorn repo" - - - name: Create Pull Request - if: ${{ env.changes_detected }} - uses: peter-evans/create-pull-request@v6 - with: - author: github-actions[bot] - committer: GitHub - title: Update Firefox icons from Acorn repo - branch: update-firefox-project-with-acorn-icons - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/firefox-ios-ui-tests-previous-os.yml b/.github/workflows/firefox-ios-ui-tests-previous-os.yml deleted file mode 100644 index 31f9f7a7ab97b..0000000000000 --- a/.github/workflows/firefox-ios-ui-tests-previous-os.yml +++ /dev/null @@ -1,284 +0,0 @@ -name: "Firefox UI Tests for iOS 15 & 16" - -on: - workflow_dispatch: {} -env: - browser: firefox-ios - xcode_version: 15.4 - ios_version: 17.5 - ios_simulator_default: iPhone 15 - xcodebuild_scheme: Fennec - xcodebuild_target: Client - test_results_directory: /Users/runner/tmp - -jobs: - compile: - name: Compile - runs-on: macos-14 - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Compile source code - id: compile - run: | - xcodebuild \ - -resolvePackageDependencies \ - -onlyUsePackageVersionsFromResolvedFile - xcodebuild \ - build-for-testing \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' \ - COMPILER_INDEX_STORE_ENABLE=NO CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO ARCHS="arm64" - working-directory: ${{ env.browser }} - - name: Compress Derived Data - id: compress-dd - run: | - tar czf derived-data.tar.gz ~/DerivedData/Build/Products - - name: Save Derived Data - id: upload-derived-data - uses: actions/upload-artifact@v4.3.4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - path: ./derived-data.tar.gz - retention-days: 2 - - run-smoketests: - name: Run smoke tests - runs-on: macos-14 - needs: compile - strategy: - fail-fast: false - matrix: - include: - - ios_version: 16.4 - ios_simulator: iPhone 14 - - ios_version: 15.5 - ios_simulator: iPhone 13 - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - npm install -g junit-report-merger@7.0.0 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo rm -fr /Applications/Xcode_16* - sudo rm -fr /Applications/Xcode_14* - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Create iOS ${{ matrix.ios_version }} simulator - id: setup-simulator - run: | - sudo xcodes runtimes install "iOS ${{ matrix.ios_version }}" - xcrun simctl list devices iphone - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - - name: Decompress derived data - id: decompress-dd - run: | - tar xzf derived-data.tar.gz -C / - - name: Run Smoketest1 - id: run-smoketest1 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan Smoketest1 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest1 \ - | tee xcodebuild-smoketest1.log | xcpretty -r junit --output ./junit-smoketest1.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run Smoketest2 - id: run-smoketest2 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan Smoketest2 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest2 \ - | tee xcodebuild-smoketest2.log | xcpretty -r junit --output ./junit-smoketest2.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run Smoketest3 - id: run-smoketest3 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan Smoketest3 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest3 \ - | tee xcodebuild-smoketest3.log | xcpretty -r junit --output ./junit-smoketest3.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run Smoketest4 - id: run-smoketest4 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan Smoketest4 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest4 \ - | tee xcodebuild-smoketest4.log | xcpretty -r junit --output ./junit-smoketest4.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Determine pass/fail status - id: passfail - run: | - echo "Smoketest1 status: "${{ steps.run-smoketest1.outcome }} - echo "Smoketest2 status: "${{ steps.run-smoketest2.outcome }} - echo "Smoketest3 status: "${{ steps.run-smoketest3.outcome }} - echo "Smoketest4 status: "${{ steps.run-smoketest4.outcome }} - if [[ ${{ steps.run-smoketest1.outcome }} != 'success' - || ${{ steps.run-smoketest2.outcome }} != 'success' - || ${{ steps.run-smoketest3.outcome }} != 'success' - || ${{ steps.run-smoketest4.outcome }} != 'success' ]]; then - exit 1 - else - exit 0 - fi - continue-on-error: true - - name: Print test report - id: test-report - run: | - jrm combined.xml junit-*.xml - python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --github --${{ env.browser }} ./combined.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --slack --${{ env.browser }} ./combined.xml ./slack.json - mv ./combined.xml "junit-smoketests-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-`date +"%Y-%m-%d"`.xml" - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-smoketest*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild-smoketest*.log - retention-days: 90 - - name: Return fail status if a test fails - run: | - exit ${{ steps.passfail.outcome == 'success' && '0' || '1' }} - run-fullfunctionaltests: - name: Run full functional tests - runs-on: macos-14 - if: false - needs: run-smoketests - strategy: - fail-fast: false - matrix: - include: - - ios_version: 16.4 - ios_simulator: iPhone 14 - - ios_version: 15.5 - ios_simulator: iPhone 13 - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - with: - repository: mozilla-mobile/firefox-ios - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - - name: Decompress derived data - id: decompress-dd - run: | - tar xzf derived-data.tar.gz -C / - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo rm -fr /Applications/Xcode_16* - sudo rm -fr /Applications/Xcode_14* - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Create iOS ${{ matrix.ios_version }} simulator - id: setup-simulator - run: | - sudo xcodes runtimes install "iOS ${{ matrix.ios_version }}" - xcrun simctl list devices iphone - - name: Run tests - id: run-tests - run: | - xcrun simctl boot '${{ matrix.ios_simulator }}' - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan FullFunctionalTestPlan \ - -resultBundlePath ${{ env.test_results_directory }}/results \ - | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Print test report - id: test-report - run: | - python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./build/reports/junit.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json - mv ./build/reports/junit.xml "junit-fullfunctional-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-`date +"%Y-%m-%d"`.xml" - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log - retention-days: 90 - - name: Return fail status if a test fails - run: | - exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file diff --git a/.github/workflows/firefox-ios-ui-tests.yml b/.github/workflows/firefox-ios-ui-tests.yml deleted file mode 100644 index 8a3f1d1c333f8..0000000000000 --- a/.github/workflows/firefox-ios-ui-tests.yml +++ /dev/null @@ -1,288 +0,0 @@ -name: "Firefox UI Tests" - -on: - workflow_dispatch: {} - schedule: - - cron: "0 2 * * *" - -env: - browser: firefox-ios - xcode_version: 15.4 - ios_version: 17.5 - ios_simulator_default: iPhone 15 - xcodebuild_scheme: Fennec - xcodebuild_target: Client - test_results_directory: /Users/runner/tmp - -jobs: - compile: - name: Compile - runs-on: macos-14 - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Compile source code - id: compile - run: | - xcodebuild \ - -resolvePackageDependencies \ - -onlyUsePackageVersionsFromResolvedFile - xcodebuild \ - build-for-testing \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' \ - COMPILER_INDEX_STORE_ENABLE=NO CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO ARCHS="arm64" - working-directory: ${{ env.browser }} - - name: Compress Derived Data - id: compress-dd - run: | - tar czf derived-data.tar.gz ~/DerivedData/Build/Products - - name: Save Derived Data - id: upload-derived-data - uses: actions/upload-artifact@v4.3.4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - path: ./derived-data.tar.gz - retention-days: 2 - - run-smoketests: - name: Run smoke tests - runs-on: macos-14 - needs: compile - strategy: - fail-fast: false - matrix: - ios_simulator: ['iPhone 15 Plus', 'iPad Pro 13-inch (M4)'] - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - npm install -g junit-report-merger@7.0.0 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - - name: Decompress derived data - id: decompress-dd - run: | - tar xzf derived-data.tar.gz -C / - - name: Run Smoketest1 - id: run-smoketest1 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan Smoketest1 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest1 \ - | tee xcodebuild-smoketest1.log | xcpretty -r junit --output ./junit-smoketest1.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run Smoketest2 - id: run-smoketest2 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan Smoketest2 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest2 \ - | tee xcodebuild-smoketest2.log | xcpretty -r junit --output ./junit-smoketest2.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run Smoketest3 - id: run-smoketest3 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan Smoketest3 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest3 \ - | tee xcodebuild-smoketest3.log | xcpretty -r junit --output ./junit-smoketest3.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run Smoketest4 - id: run-smoketest4 - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan Smoketest4 \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketest4 \ - | tee xcodebuild-smoketest4.log | xcpretty -r junit --output ./junit-smoketest4.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Determine pass/fail status - id: passfail - run: | - echo "Smoketest1 status: "${{ steps.run-smoketest1.outcome }} - echo "Smoketest2 status: "${{ steps.run-smoketest2.outcome }} - echo "Smoketest3 status: "${{ steps.run-smoketest3.outcome }} - echo "Smoketest4 status: "${{ steps.run-smoketest4.outcome }} - if [[ ${{ steps.run-smoketest1.outcome }} != 'success' - || ${{ steps.run-smoketest2.outcome }} != 'success' - || ${{ steps.run-smoketest3.outcome }} != 'success' - || ${{ steps.run-smoketest4.outcome }} != 'success' ]]; then - exit 1 - else - exit 0 - fi - continue-on-error: true - - name: Print test report - id: test-report - run: | - jrm combined.xml junit-*.xml - python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --github --${{ env.browser }} ./combined.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --slack --${{ env.browser }} ./combined.xml ./slack.json - mv ./combined.xml "junit-smoketests-${{ matrix.ios_simulator }}-`date +"%Y-%m-%d"`.xml" - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-smoketests-*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild-smoketest*.log - retention-days: 90 - - name: Report to Slack - id: slack - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - pass_fail: ${{ steps.passfail.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: Smoketests - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Return fail status if a test fails - run: | - exit ${{ steps.passfail.outcome == 'success' && '0' || '1' }} - - run-fullfunctionaltests: - name: Run full functional tests - runs-on: macos-14 - needs: run-smoketests - if: false - strategy: - fail-fast: false - matrix: - ios_simulator: ['iPhone 15 Pro Max', 'iPad Pro (12.9 inch) (6th generation)'] - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - - name: Decompress derived data - id: decompress-dd - run: | - tar xzf derived-data.tar.gz -C / - - name: Run tests - id: run-tests - run: | - xcrun simctl boot '${{ matrix.ios_simulator }}' - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan FullFunctionalTestPlan \ - -resultBundlePath ${{ env.test_results_directory }}/results \ - | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Print test report - id: test-report - run: | - python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./build/reports/junit.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json - mv ./build/reports/junit.xml "junit-fullfunctional-${{ matrix.ios_simulator }}-`date +"%Y-%m-%d"`.xml" - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log - retention-days: 90 - - name: Report to Slack - id: slack - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: FullFunctionalTestPlan - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Return fail status if a test fails - run: | - exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file diff --git a/.github/workflows/firefox-ios-update-effective-tld-names-file.yml b/.github/workflows/firefox-ios-update-effective-tld-names-file.yml deleted file mode 100644 index d68576f2475ad..0000000000000 --- a/.github/workflows/firefox-ios-update-effective-tld-names-file.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Create a PR if there are changes in effective_tld_names file -on: - schedule: - - cron: '0 6 1,15 * *' - workflow_dispatch: - inputs: - branchName: - description: 'Branch used as target for automation' - required: true - default: 'main' -jobs: - build: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.9] - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - token: ${{ secrets.GITHUB_TOKEN }} - ref: ${{ github.event.inputs.branchName }} - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Compare file in repo with file upstream - run: | - echo "Fetch latest file version" - - curl -o tmp.dat "https://raw.githubusercontent.com/publicsuffix/list/master/public_suffix_list.dat" - - file_in_repo=firefox-ios/Shared/effective_tld_names.dat - - files_diff=$(cmp --silent tmp.dat $file_in_repo && echo '### SUCCESS: Files Are Identical! ###' || echo '### WARNING: Files Are Different! ###') - - echo $files_diff - echo "current_date=$current_date" >> $GITHUB_ENV - - if [[ "$files_diff" == *"Different"* ]]; then - echo "Files are different" - echo "create_pr=true" >> $GITHUB_ENV - cp tmp.dat firefox-ios/Shared/effective_tld_names.dat - echo "branch_name=refactor-update-effective-tld-names" >> $GITHUB_ENV - echo "pr_body=This automated PR updates the effective_tld_names file" >> $GITHUB_ENV - rm tmp.dat - fi - - - name: Determine PR Version Number - id: versioning - run: | - # This step is used to determine the next version number for the PR title - # The output includes debugging for the piped commands that generate - # the version number and the last line is the version number itself - - output=$(bash test-fixtures/ci/get-next-pr-version) - echo "$output" - next_version=$(echo "$output" | tail -n 1) # get the last line of the output - echo "Next version is: v${next_version}" - echo "next_version=${next_version}" >> $GITHUB_ENV - - - name: Add Modified File to PR - if: ${{ env.create_pr }} - run: |- - git diff || (git add firefox-ios/Shared/effective_tld_names.dat) - - - name: Create Pull Request - if: ${{ env.create_pr }} - uses: peter-evans/create-pull-request@v6 - with: - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: ${{ env.pr_title }} - title: "Refactor [v${{ env.next_version }}] Update effective_tld_names file ${{ env.current_date }}" - branch: ${{ env.branch_name }} - body: ${{ env.pr_body }} diff --git a/.github/workflows/firefox-ios-update-nimbus-experiments.yml b/.github/workflows/firefox-ios-update-nimbus-experiments.yml deleted file mode 100644 index 56f45782d7df8..0000000000000 --- a/.github/workflows/firefox-ios-update-nimbus-experiments.yml +++ /dev/null @@ -1,49 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/ - -name: "Firefox Update Nimbus Experiments" - -on: - schedule: - - cron: '*/30 * * * *' - workflow_dispatch: {} - -jobs: - update-nimbus-experiments: - name: "Update Nimbus Experiments" - runs-on: ubuntu-latest - steps: - - name: "Checkout Main Branch" - uses: actions/checkout@v4 - with: - path: firefox-ios - ref: main - fetch-depth: 0 - - name: "Update Experiments JSON" - id: update-experiments-json - uses: mozilla-mobile/update-experiments@v3 - with: - repo-path: firefox-ios - output-path: firefox-ios/Client/Experiments/initial_experiments.json - experimenter-url: https://experimenter.services.mozilla.com/api/v6/experiments-first-run/ - app-name: firefox_ios - branch: automation/update-nimbus-experiments - - name: Create Pull Request - id: create-pull-request - uses: peter-evans/create-pull-request@v6 - if: steps.update-experiments-json.outputs.changed == 1 && steps.update-experiments-json.outputs.changed-branch >= 1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - path: firefox-ios - branch: automation/update-nimbus-experiments - commit-message: "update initial_experiments.json based on the current first-run experiments in experimenter" - title: "Refactor [vXXX] Update initial experiments JSON for Nimbus" - body: "This (automated) PR updates the initial_experiments.json on the `main` branch" - delete-branch: true - labels: Needs Code Review - - name: Enable Pull Request Automerge - if: steps.create-pull-request.outputs.pull-request-operation == 'created' - run: gh pr merge --squash "${{ steps.create-pull-request.outputs.pull-request-number }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/firefox-ios-update-uri.yml b/.github/workflows/firefox-ios-update-uri.yml deleted file mode 100644 index c2ba3a04ee6b3..0000000000000 --- a/.github/workflows/firefox-ios-update-uri.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Update URIs and Create PR -on: - schedule: - - cron: '0 0 1 * *' # Runs at 00:00 on the first day of every month - workflow_dispatch: - inputs: - branchName: - description: 'Branch used as target for automation' - required: true - default: 'main' -jobs: - build: - runs-on: ubuntu-latest # using ubuntu as a lightweight environment - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - token: ${{ secrets.GITHUB_TOKEN }} - ref: ${{ github.event.inputs.branchName }} - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.9' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - # include pyarrow for pandas 3.0 compatibility - pip install pandas pyarrow requests - - - name: Run URI update script - run: python test-fixtures/ci/uri_update.py - - - name: Check for changes - id: git-check - run: | - if git diff --quiet; then - echo "No changes detected, skipping PR creation." - echo "changes_detected=false" >> $GITHUB_ENV - else - echo "Changes detected, proceeding to create PR." - git diff - git config --global user.name 'github-actions[bot]' - git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - git commit --allow-empty -m "empty commit" # add empty commit so when squashed the history will show PR title - git add firefox-ios/Shared/Extensions/URLExtensions.swift - echo "changes_detected=true" >> $GITHUB_ENV - datetime=$(date +%Y%m%d%H%M%S) - echo "date=$datetime" >> $GITHUB_ENV - fi - - - name: Determine PR Version Number - id: versioning - run: | - # This step is used to determine the next version number for the PR title - # The output includes debugging for the piped commands that generate - # the version number and the last line is the version number itself - - output=$(bash test-fixtures/ci/get-next-pr-version) - echo "$output" - next_version=$(echo "$output" | tail -n 1) # get the last line of the output - echo "Next version is: v${next_version}" - echo "next_version=${next_version}" >> $GITHUB_ENV - - - name: Create Pull Request - if: env.changes_detected == 'true' - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: Automated URI update on ${{ github.event.inputs.branchName || 'main' }} ${{ env.date }} - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - title: Refactor [v${{ env.next_version }}] Automated URI update on ${{ github.event.inputs.branchName || 'main' }} ${{ env.date }} - branch: update-uri-schemes-${{ env.date }} - body: This automated PR updates the URIs. diff --git a/.github/workflows/firefox-ios-update.yml b/.github/workflows/firefox-ios-update.yml deleted file mode 100644 index ce687afefd831..0000000000000 --- a/.github/workflows/firefox-ios-update.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Bitrise Xcode Check and Update - -on: - schedule: - - cron: '0 */6 * * *' - -jobs: - build: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.7] - steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r ./test-fixtures/requirements.txt - - name: Modify bitrise.yml - run: | - python ./test-fixtures/update.py - - name: Commit and push if bitrise.yml changed - run: |- - git diff - git config --global user.email "firefox-test-engineering@mozilla.com" - git config --global user.name "Firefox Test Engineering" - git diff --quiet || (git add bitrise.yml) - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - commit-message: Auto Update Bitrise.YML - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - title: Update Bitrise NewXcodeVersions Workflow Xcode Stack - branch: update-br-new-xcode-version - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/firefox-ios-update_credential_provider_script.yml b/.github/workflows/firefox-ios-update_credential_provider_script.yml deleted file mode 100644 index 7bb894e045142..0000000000000 --- a/.github/workflows/firefox-ios-update_credential_provider_script.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Update credential provider script - -on: - schedule: - - cron: '0 0 * * *' - workflow_dispatch: # adding the workflow_dispatch so it can be triggered manually - -env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - -jobs: - build: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.7] - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - name: Get the current github action name - run: | - github_action_name="Update credential provider script" - echo "github_action_name=$github_action_name" >> $GITHUB_ENV - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r ./test-fixtures/requirements.txt - - name: Modify credential provider script - run: | - python firefox-ios/Client/Assets/CC_Script/CC_Python_Update.py - - name: Commit and push credential provider changed - run: |- - git diff - git diff --quiet || (git add .) - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - commit-message: Refactor - auto update credential provider script - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - title: Refactor - auto update credential provider script - branch: update-cred-provider-script - token: ${{ secrets.GITHUB_TOKEN }} - - name: Set job log URL - if: always() - run: echo "JOB_LOG_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_ENV - - name: Send Slack to notifiy if github action fails - if: '!cancelled()' - id: slack - uses: slackapi/slack-github-action@v1.26.0 - env: - JOB_STATUS: ${{ job.status == 'success' && ':white_check_mark:' || job.status == 'failure' && ':x:' }} - JOB_STATUS_COLOR: ${{ job.status == 'success' && '#36a64f' || job.status == 'failure' && '#FF0000' }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - with: - payload-file-path: "./test-fixtures/ci/slack-notification-payload-autofill.json" - call-firefox-ios-autofill-playwrite-tests: - uses: ./.github/workflows/firefox-ios-autofill-playwrite-tests.yml - secrets: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/firefox-ios-update_remote_settings_data_script.yml b/.github/workflows/firefox-ios-update_remote_settings_data_script.yml deleted file mode 100644 index 37daf338adabe..0000000000000 --- a/.github/workflows/firefox-ios-update_remote_settings_data_script.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Fetch Remote Settings - -# Remote settings are updated every Tuesday and Thursday - -on: - schedule: - - cron: '0 0 * * 2' # Every Tuesday at midnight - - cron: '0 0 * * 4' # Every Thursday at midnight - workflow_dispatch: # Allows manual triggering of the workflow - -env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - -jobs: - fetch-remote-settings: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: [3.7] - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get the current GitHub Action name - run: | - github_action_name="Fetch Remote Settings" - echo "github_action_name=$github_action_name" >> $GITHUB_ENV - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install requests - - - name: Fetch remote settings and update files - run: python ./firefox-ios/Client/Assets/RemoteSettingsData/Update_Remote_Settings.py - - - name: Add changes - run: | - git diff - git diff --quiet || git add firefox-ios/Client/Assets/RemoteSettingsData/RemotePasswordRules.json - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - commit-message: "Refactor - auto update remote settings" - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - title: "Refactor - auto update remote settings" - branch: "auto-update-remote-settings" - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set job log URL - if: always() - run: echo "JOB_LOG_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_ENV - - - name: Send Slack to notifiy if github action fails - if: '!cancelled()' - id: slack - uses: slackapi/slack-github-action@v1.26.0 - env: - JOB_STATUS: ${{ job.status == 'success' && ':white_check_mark:' || job.status == 'failure' && ':x:' }} - JOB_STATUS_COLOR: ${{ job.status == 'success' && '#36a64f' || job.status == 'failure' && '#FF0000' }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - with: - payload-file-path: "./test-fixtures/ci/slack-notification-payload-remote-settings-fetch.json" \ No newline at end of file diff --git a/.github/workflows/focus-ios-import-strings.yml b/.github/workflows/focus-ios-import-strings.yml deleted file mode 100644 index dc83c07b72bdc..0000000000000 --- a/.github/workflows/focus-ios-import-strings.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: "Focus: import translations" - -permissions: - contents: write - pull-requests: write - -on: - schedule: - - cron: '0 10 * * 1' - -jobs: - build: - runs-on: macos-13 - strategy: - max-parallel: 4 - matrix: - python-version: [3.9] - xcode: ["15.0.1"] - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - token: ${{ secrets.GITHUB_TOKEN }} - - name: Select Xcode ${{ matrix.xcode }} - run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - name: Get the current date for PR title - run: echo "current_date=$(date +"%m-%d-%Y")" >> $GITHUB_ENV - - name: Run script to import strings - run: sh ./focus-ios/focus-ios-tests/tools/import-strings.sh - - name: Update new strings - run: |- - git diff || (git add focus-ios/Blockzilla/*/*.lproj/* focus-ios/Blockzilla/*.lproj/* focus-ios/Blockzilla.xcodeproj/project.pbxproj) - git restore focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: GitHub - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: focus-string-import-${{ env.current_date }} - title: "focus-string-import-${{ env.current_date }}" - branch: focus-string-import-${{ env.current_date }} - body: "This (automated) PR import string changes" - labels: Needs Landing diff --git a/.github/workflows/focus-ios-ui-tests-previous-os.yml b/.github/workflows/focus-ios-ui-tests-previous-os.yml deleted file mode 100644 index bbb477a94faa6..0000000000000 --- a/.github/workflows/focus-ios-ui-tests-previous-os.yml +++ /dev/null @@ -1,169 +0,0 @@ -name: Focus iOS 15 & 16 tests -on: - workflow_dispatch: - schedule: - - cron: "0 7 * * 1,3,5" - -env: - browser: focus-ios - xcode_version: 15.4 - test_results_directory: /Users/runner/tmp - -jobs: - Focus-iOS-Tests: - name: Focus iOS - runs-on: macos-14 - strategy: - fail-fast: false - matrix: - include: - - ios_version: 16.4 - ios_simulator: iPhone 14 - - ios_version: 15.5 - ios_simulator: iPhone 13 - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Install packages - run: | - pip3 install virtualenv pipenv - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo rm -fr /Applications/Xcode_16* - sudo rm -fr /Applications/Xcode_14* - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Create iOS ${{ matrix.ios_version }} simulator - id: setup-simulator - run: | - sudo xcodes runtimes install "iOS ${{ matrix.ios_version }}" - xcrun simctl list devices iphone - - name: Build Focus - id: compile - run: | - xcodebuild build-for-testing \ - -project Blockzilla.xcodeproj -scheme Focus \ - -configuration FocusDebug \ - CODE_SIGNING_ALLOWED=NO -destination \ - 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' - working-directory: ${{ env.browser }} - - name: Run smoke tests - id: run-smoketests - run: | - xcodebuild test-without-building \ - -project Blockzilla.xcodeproj -scheme Focus \ - -configuration FocusDebug \ - CODE_SIGNING_ALLOWED=NO \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan SmokeTest \ - -resultBundlePath ${{ env.test_results_directory }}/results-smoketests \ - | tee xcodebuild-smoketests.log | xcpretty -r junit --output ./junit-smoketests.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Run full functional tests - id: run-fullfunctionaltests - run: | - xcodebuild test-without-building \ - -project Blockzilla.xcodeproj -scheme Focus \ - -configuration FocusDebug \ - CODE_SIGNING_ALLOWED=NO \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ matrix.ios_version }}' \ - -testPlan FullFunctionalTests \ - -resultBundlePath ${{ env.test_results_directory }}/results-fullfunctionaltests \ - | tee xcodebuild-fullfunctionaltests.log | xcpretty -r junit --output ./junit-fullfunctionaltests.xml && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Determine pass/fail status - id: passfail - run: | - echo "Smoke Tests status: "${{ steps.run-smoketests.outcome }} - echo "Full Functional Tests status: "${{ steps.run-fullfunctionaltests.outcome }} - if [[ ${{ steps.run-smoketests.outcome }} != 'success' - || ${{ steps.run-fullfunctionaltests.outcome }} != 'success' ]]; then - exit 1 - else - exit 0 - fi - continue-on-error: true - - name: Print test report - id: test-report - run: | - # Smoke Tests - python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --github --${{ env.browser }} ./junit-smoketests.xml ./github-smoketests.md - echo "# Smoke Tests " >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - cat github-smoketests.md >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --slack --${{ env.browser }} ./junit-smoketests.xml ./slack-smoketests.json - mv ./junit-smoketests.xml "junit-focus-ios-smoketests-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-`date +"%Y-%m-%d"`.xml" - - # Full Functional Tests - python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./junit-fullfunctionaltests.xml ./github-fullfunctionaltests.md - echo "# Full Functional Tests " >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - cat github-fullfunctionaltests.md >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./junit-fullfunctionaltests.xml ./slack-fullfunctionaltests.json - mv ./junit-fullfunctionaltests.xml "junit-focus-ios-fullfunctionaltests-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-`date +"%Y-%m-%d"`.xml" - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-focus-ios-*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-${{ matrix.ios_simulator }}-${{ matrix.ios_version }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild-*.log - retention-days: 90 - - name: Report to Slack (SmokeTests) - id: slack-smoketests - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack-smoketests.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - ios_version: ${{ matrix.ios_version }} - pass_fail: ${{ steps.run-smoketests.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: SmokeTests - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Report to Slack (FullFunctionalTests) - id: slack-fullfunctionaltests - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack-fullfunctionaltests.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - ios_version: ${{ matrix.ios_version }} - pass_fail: ${{ steps.run-fullfunctionaltests.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: FullFunctionalTests - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Return fail status if a test fails - run: | - exit ${{ steps.passfail.outcome == 'success' && '0' || '1' }} \ No newline at end of file diff --git a/.github/workflows/focus-ios-ui-tests.yml b/.github/workflows/focus-ios-ui-tests.yml deleted file mode 100644 index 3d1c041091a5f..0000000000000 --- a/.github/workflows/focus-ios-ui-tests.yml +++ /dev/null @@ -1,134 +0,0 @@ -name: "Focus UI Tests" - -on: - workflow_dispatch: {} - schedule: - - cron: "0 1 * * *" - -env: - browser: focus-ios - xcode_version: 15.4 - ios_version: 17.5 - ios_simulator_default: iPhone 15 - xcodebuild_scheme: Focus - xcodebuild_target: XCUITest - test_results_directory: /Users/runner/tmp - -jobs: - compile: - name: Compile - runs-on: macos-14 - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Compile source code - id: compile - run: | - xcodebuild \ - build-for-testing \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' \ - COMPILER_INDEX_STORE_ENABLE=NO CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO ARCH="arm64" - working-directory: ${{ env.browser }} - - name: Save Derived Data - id: upload-derived-data - uses: actions/upload-artifact@v4.3.3 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - path: ~/DerivedData/Build/Products - retention-days: 2 - run-tests: - name: Run tests - runs-on: macos-14 - needs: compile - strategy: - fail-fast: false - matrix: - xcodebuild_test_plan: ['SmokeTest'] # , 'FullFunctionalTests'] - ios_simulator: [ 'iPhone 15', 'iPad Pro (12.9-inch) (6th generation)'] - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - - name: Setup Xcode - id: xcode - run: | - sudo rm -rf /Applications/Xcode.app - sudo xcode-select -s /Applications/Xcode_${{ env.xcode_version }}.app/Contents/Developer - xcodebuild -version - ./checkout.sh - ./bootstrap.sh --force - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - path: ~/DerivedData/Build/Products - - name: Run tests - id: run-tests - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan ${{ matrix.xcodebuild_test_plan }} \ - -resultBundlePath ${{ env.test_results_directory }}/results \ - | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Print test report - id: test-report - run: | - python ../test-fixtures/ci/convert_junit_to_markdown.py ${{ matrix.xcodebuild_test_plan == 'SmokeTest' && '--smoke' || '' }} --github --${{ env.browser }} ./build/reports/junit.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py ${{ matrix.xcodebuild_test_plan == 'SmokeTest' && '--smoke' || '' }} --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json - working-directory: ${{ env.browser }} - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-${{ matrix.xcodebuild_test_plan }}-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log - retention-days: 90 - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-${{ matrix.xcodebuild_test_plan }}-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/build/reports/junit.xml - retention-days: 90 - - name: Report to Slack - id: slack - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_SLACK_TOKEN }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: ${{ matrix.xcodebuild_test_plan }} - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Return fail status if a test fails - run: | - exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file diff --git a/.github/workflows/focus-ios-update-nimbus-experiments.yml b/.github/workflows/focus-ios-update-nimbus-experiments.yml deleted file mode 100644 index a9174b2b5fe58..0000000000000 --- a/.github/workflows/focus-ios-update-nimbus-experiments.yml +++ /dev/null @@ -1,49 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/ - -name: "Focus Update Nimbus Experiments" - -on: - schedule: - - cron: '*/30 * * * *' - workflow_dispatch: {} - -jobs: - update-nimbus-experiments: - name: "Update Nimbus Experiments" - runs-on: ubuntu-latest - steps: - - name: "Checkout Main Branch" - uses: actions/checkout@v4 - with: - path: focus-ios - ref: main - fetch-depth: 0 - - name: "Update Experiments JSON" - id: update-experiments-json - uses: mozilla-mobile/update-experiments@v3 - with: - repo-path: focus-ios - output-path: focus-ios/Blockzilla/Nimbus/initial_experiments.json - experimenter-url: https://experimenter.services.mozilla.com/api/v6/experiments-first-run/ - app-name: focus_ios - branch: automation/update-nimbus-experiments - - name: Create Pull Request - id: create-pull-request - uses: peter-evans/create-pull-request@v6 - if: steps.update-experiments-json.outputs.changed == 1 && steps.update-experiments-json.outputs.changed-branch >= 1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - path: focus-ios - branch: automation/update-nimbus-experiments - commit-message: "Automated: update initial_experiments.json based on the current first-run experiments in experimenter" - title: "Update initial experiments JSON for Nimbus" - body: "This (automated) PR updates the initial_experiments.json on the `main` branch" - delete-branch: true - labels: Needs Code Review - - name: Enable Pull Request Automerge - if: steps.create-pull-request.outputs.pull-request-operation == 'created' - run: gh pr merge --squash "${{ steps.create-pull-request.outputs.pull-request-number }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/glean-probe-scraper.yml b/.github/workflows/glean-probe-scraper.yml deleted file mode 100644 index e845bad1144c8..0000000000000 --- a/.github/workflows/glean-probe-scraper.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Glean probe-scraper -on: - push: - branches: [main] - pull_request: - branches: [main] -jobs: - glean-probe-scraper: - uses: mozilla/probe-scraper/.github/workflows/glean.yaml@main diff --git a/.github/workflows/merge_tests.yml b/.github/workflows/merge_tests.yml new file mode 100644 index 0000000000000..c7b4c93a69dc1 --- /dev/null +++ b/.github/workflows/merge_tests.yml @@ -0,0 +1,25 @@ +name: Merge Unit Tests + +on: + pull_request: + branches: [main, 'patch/**'] + +jobs: + execute_merge_tests: + runs-on: macos-15 + name: Execute Merge Tests + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare environment + uses: ./.github/actions/prepare_environment + with: + core-token: ${{ secrets.IOS_CORE_TOKEN }} + + - name: Perform Unit Tests and Publish Test reports + id: perform-tests + uses: ./.github/actions/perform_unit_tests + with: + github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/snapshot_tests.yml b/.github/workflows/snapshot_tests.yml new file mode 100644 index 0000000000000..14dc2a0f98352 --- /dev/null +++ b/.github/workflows/snapshot_tests.yml @@ -0,0 +1,36 @@ +name: Snapshot Tests + +on: + pull_request: + branches: [main, 'patch/**'] + paths: + - 'firefox-ios/Client/Configuration/Common.xcconfig' + +jobs: + execute_snapshot_tests: + runs-on: macos-15 + name: Execute Snapshot Tests + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + lfs: true + submodules: true + + - name: Check for MARKETING_VERSION change + id: check_marketing_version + run: sh ./check_marketing_version.sh + + - name: Prepare environment + if : ( steps.check_marketing_version.outputs.skipnext != 'true' ) + uses: ./.github/actions/prepare_environment + with: + core-token: ${{ secrets.IOS_CORE_TOKEN }} + + - name: Perform Snapshot Tests and Publish Test reports + if : ( steps.check_marketing_version.outputs.skipnext != 'true' ) + id: perform-snapshot-tests + uses: ./.github/actions/perform_snapshot_tests + with: + github-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/swift_lint.yml b/.github/workflows/swift_lint.yml new file mode 100644 index 0000000000000..900824309015d --- /dev/null +++ b/.github/workflows/swift_lint.yml @@ -0,0 +1,16 @@ +name: SwiftLint +on: + pull_request: + branches: [main, 'patch/**'] +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/realm/swiftlint:0.59.1 + + steps: + - uses: actions/checkout@v4 + + - name: SwiftLint + run: | + swiftlint --reporter github-actions-logging --strict \ No newline at end of file diff --git a/.github/workflows/update_readme_current_tech_stack.yml b/.github/workflows/update_readme_current_tech_stack.yml deleted file mode 100644 index 515c9e50d2011..0000000000000 --- a/.github/workflows/update_readme_current_tech_stack.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Update README with current tech stack - -on: - workflow_dispatch: - schedule: - - cron: '0 12 * * SUN' - -jobs: - update_readme_with_current_stack: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Update README - run: | - sh update_readme_project_stack.sh - - - name: Check changes - run: | - if [[ $(git status --porcelain) ]]; then - echo "changes_detected=true" >> $GITHUB_ENV - fi - - - name: Commit if needed - if: ${{ env.changes_detected }} - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git add README.md - git commit -m "Update README with latest tech stack" - - - name: Create Pull Request - if: ${{ env.changes_detected }} - uses: peter-evans/create-pull-request@v6 - with: - author: github-actions[bot] - committer: GitHub - title: Update README with recent tech stack - branch: update-readme-with-tech-stack - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/upload_release_notes_to_appstore.yml b/.github/workflows/upload_release_notes_to_appstore.yml new file mode 100644 index 0000000000000..f77ee94ed566a --- /dev/null +++ b/.github/workflows/upload_release_notes_to_appstore.yml @@ -0,0 +1,30 @@ +name: Release Notes update + +on: + push: + paths: + - 'fastlane/metadata/**/release_notes.txt' + - '!fastlane/metadata/en-US/**/release_notes.txt' + branches: [ main, 'patch/**' ] + +jobs: + upload_release_notes_to_appstore: + runs-on: ubuntu-latest + name: Upload release notes to AppStore + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install gems + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.3.0 + bundler-cache: true + + - name: Fastlane upload release notes + run: bundle exec fastlane upload_release_notes + env: + FASTLANE_APPSTORE_CONNECT_KEY_ID: ${{ secrets.FASTLANE_APPSTORE_CONNECT_KEY_ID }} + FASTLANE_APPSTORE_CONNECT_KEY_ISSUER_ID: ${{ secrets.FASTLANE_APPSTORE_CONNECT_KEY_ISSUER_ID }} + FASTLANE_APPSTORE_CONNECT_KEY_CONTENT: ${{ secrets.FASTLANE_APPSTORE_CONNECT_KEY_CONTENT }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5808b46d80c90..eee1dab0dbc28 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build/ !taskcluster/kinds/build .build/ + *.pbxuser !default.pbxuser *.mode1v3 @@ -135,3 +136,21 @@ ContentBlockingLists/disconnect-* focus-ios/shavar-* firefox-ios/shavar-* shavar-* + +# bundler +vendor/bundle +.bundle/config + +# transifex +/tx + +# Ecosia: Additional configs +Staging.xcconfig +Production.xcconfig +.github/workflows/firefox-*.yml +firefox-ios/SourcePackages +test_output +.vscode +firefox-ios/EcosiaTests/Results +firefox-ios/EcosiaTests/SnapshotTests/SnapshotArtifacts/* +firefox-ios/TestResults.xcresult diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..c7fb038a59547 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "firefox-ios/EcosiaTests/SnapshotTests/SnapshotArtifacts"] + path = firefox-ios/EcosiaTests/SnapshotTests/SnapshotArtifacts + url = git@github.com:ecosia/ios-snapshot-tests-files.git diff --git a/.swiftlint.yml b/.swiftlint.yml index f24c1f9155fa4..ce43a5a2f54df 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,5 +1,6 @@ only_rules: # Only enforce these rules, ignore all others - - line_length + # Ecosia: disabled + # - line_length - blanket_disable_command - class_delegate_protocol - closure_spacing @@ -30,15 +31,16 @@ only_rules: # Only enforce these rules, ignore all others - mark - no_space_in_method_call - ns_number_init_as_function_reference - - operator_whitespace + - function_name_whitespace - orphaned_doc_comment - private_over_fileprivate - protocol_property_accessors_order - redundant_discardable_let - redundant_objc_attribute - - redundant_optional_initialization + - implicit_optional_initialization - redundant_string_enum_value - - redundant_void_return + # Ecosia: disabled + # - redundant_void_return - return_arrow_whitespace - statement_position - switch_case_alignment @@ -57,7 +59,8 @@ only_rules: # Only enforce these rules, ignore all others - void_return - file_header - redundant_type_annotation - - attributes + # Ecosia: disabled + # - attributes - closing_brace - closure_end_indentation - contains_over_filter_count @@ -80,10 +83,13 @@ only_rules: # Only enforce these rules, ignore all others - overridden_super_call - vertical_parameter_alignment_on_call - vertical_whitespace_closing_braces - - vertical_whitespace_opening_braces + # Ecosia: disabled + # - vertical_whitespace_opening_braces - yoda_condition - accessibility_label_for_image - accessibility_trait_for_button + # Ecosia: enabled + - custom_rules # These rules were originally opted into. Disabling for now to get # Swiftlint up and running. @@ -99,6 +105,23 @@ line_length: ignores_urls: true ignores_interpolated_strings: true +custom_rules: + reassign_analytics_instance: + included: + - ".*\\.swift" + excluded: + - "EcosiaTests/*" + regex: "(Analytics.shared =)" + capture_group: 0 + match_kinds: + - comment + - identifier + message: "We should not re-assign Analytics shared instance outside tests." + severity: error + no_hiding_in_strings: + regex: "(Analytics.shared =)" + match_kinds: string + file_header: required_string: | // This Source Code Form is subject to the terms of the Mozilla Public @@ -114,6 +137,8 @@ excluded: # paths to ignore during linting. Takes precedence over `included`. - .build/ - firefox-ios/Client/Assets/Search/get_supported_locales.swift - firefox-ios/Client/Generated + # Ecosia: Include persisted generated + - firefox-ios/Client/Ecosia/PersistedGenerated - firefox-ios/fastlane/ - firefox-ios/FxA - firefox-ios/FxAClient @@ -137,6 +162,12 @@ excluded: # paths to ignore during linting. Takes precedence over `included`. - firefox-ios/Build/Intermediates.noindex/Client.build/Fennec-iphoneos/WidgetKitExtension.build/DerivedSources/IntentDefinitionGenerated/WidgetIntents/* # Temporarly ignore focus-ios folder - focus-ios/ + # Ecosia: Exclude Swift Packages + - firefox-ios/SourcePackages + # Ecosia: Remove Metrics.swift as part of Glean + - Storage/Generated/Metrics.swift + # Ecosia: Exclude Tuist-generated bundle files + - firefox-ios/Derived/Sources included: - /Users/vagrant/git @@ -145,3 +176,6 @@ included: # reporter: "json" # reporter type (xcode, json, csv, checkstyle) reporter: "xcode" # reporter type (xcode, json, csv, checkstyle) + +baseline: swiftlint_baseline.json + diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000000000..755f15af34709 --- /dev/null +++ b/.tx/config @@ -0,0 +1,36 @@ +[main] +host = https://www.transifex.com + +[o:ecosia:p:ecosia-ios-search-app:r:release_notestxt] +source_file = fastlane/metadata/en-US/release_notes.txt +file_filter = fastlane/metadata//release_notes.txt +source_lang = en +type = TXT +trans.fr = fastlane/metadata/fr-FR/release_notes.txt +trans.it = fastlane/metadata/it/release_notes.txt +trans.nl = fastlane/metadata/nl-NL/release_notes.txt +trans.de = fastlane/metadata/de-DE/release_notes.txt +trans.es = fastlane/metadata/es-ES/release_notes.txt + +[o:ecosia:p:ecosia-ios-search-app:r:ecosiastrings] +source_file = Client/Ecosia/L10N/en.lproj/Ecosia.strings +file_filter = Client/Ecosia/L10N/.lproj/Ecosia.strings +source_lang = en +type = STRINGS +trans.de = Client/Ecosia/L10N/de.lproj/Ecosia.strings +trans.es = Client/Ecosia/L10N/es.lproj/Ecosia.strings +trans.fr = Client/Ecosia/L10N/fr.lproj/Ecosia.strings +trans.it = Client/Ecosia/L10N/it.lproj/Ecosia.strings +trans.nl = Client/Ecosia/L10N/nl.lproj/Ecosia.strings + +[o:ecosia:p:ecosia-ios-search-app:r:pluralsstringsdict] +source_file = Client/Ecosia/L10N/en.lproj/Plurals.stringsdict +file_filter = Client/Ecosia/L10N/.lproj/Plurals.strings +source_lang = en +type = STRINGSDICT +trans.de = Client/Ecosia/L10N/de.lproj/Plurals.stringsdict +trans.es = Client/Ecosia/L10N/es.lproj/Plurals.stringsdict +trans.fr = Client/Ecosia/L10N/fr.lproj/Plurals.stringsdict +trans.it = Client/Ecosia/L10N/it.lproj/Plurals.stringsdict +trans.nl = Client/Ecosia/L10N/nl.lproj/Plurals.stringsdict + diff --git a/.tx/integration_config.yml b/.tx/integration_config.yml new file mode 100644 index 0000000000000..992c8b1354192 --- /dev/null +++ b/.tx/integration_config.yml @@ -0,0 +1,13 @@ +filters: + + - filter_type: file + source_language: en + source_file: firefox-ios/Ecosia/L10N/en.lproj/Ecosia.strings + file_format: STRINGS + translation_files_expression: 'firefox-ios/Ecosia/L10N/.lproj/Ecosia.strings' + + - filter_type: file + source_language: en + source_file: firefox-ios/Ecosia/L10N/en.lproj/Plurals.stringsdict + file_format: STRINGSDICT + translation_files_expression: 'firefox-ios/Ecosia/L10N/.lproj/Plurals.stringsdict' diff --git a/.tx/release_notes_integration_config.yml b/.tx/release_notes_integration_config.yml new file mode 100644 index 0000000000000..0da8c19bb747f --- /dev/null +++ b/.tx/release_notes_integration_config.yml @@ -0,0 +1,23 @@ +settings: + language_mapping: + ar: ar-SA + da: da + de: de-DE + en_AU: en-AU + en_CA: en-CA + en_GB: en-GB + en_US: en-US + es: es-ES + es_MX: es-MX + fr: fr-FR + it: it + ja: ja + nl: nl-NL + +filters: + + - filter_type: file + source_language: en + source_file: fastlane/metadata/en-US/release_notes.txt + file_format: TXT + translation_files_expression: 'fastlane/metadata//release_notes.txt' \ No newline at end of file diff --git a/BrowserKit/Package.resolved b/BrowserKit/Package.resolved index 3e8c4e0791173..35593322ced44 100644 --- a/BrowserKit/Package.resolved +++ b/BrowserKit/Package.resolved @@ -1,5 +1,32 @@ { "pins" : [ + { + "identity" : "a-star", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Dev1an/A-Star", + "state" : { + "revision" : "036256f9a8d1dda44085a2b92fa58199446a8339", + "version" : "3.0.0-beta-1" + } + }, + { + "identity" : "auth0.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/Auth0.swift", + "state" : { + "revision" : "259b5d5f303d49203a6cc8415b56100d8b7584ce", + "version" : "2.15.1" + } + }, + { + "identity" : "braze-swift-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/braze-inc/braze-swift-sdk", + "state" : { + "revision" : "6c7bcbd58e36b4776f235260398fa1817244160d", + "version" : "11.9.0" + } + }, { "identity" : "dip", "kind" : "remoteSourceControl", @@ -27,6 +54,42 @@ "revision" : "7674c93e79ee5aac17681acace324761325e7346" } }, + { + "identity" : "glean-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mozilla/glean-swift", + "state" : { + "revision" : "51794f8b74c55ebde6d1502b641eacc19176e139", + "version" : "61.2.0" + } + }, + { + "identity" : "ios_sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/adjust/ios_sdk.git", + "state" : { + "revision" : "f7a0ad4a9f99fcbfc4c73dcad557ea3c86c5aaf6", + "version" : "4.37.0" + } + }, + { + "identity" : "jwtdecode.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/JWTDecode.swift.git", + "state" : { + "revision" : "36a5ce735a61c4bc119593f43ce2c027b4ca7392", + "version" : "3.3.0" + } + }, + { + "identity" : "kif", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kif-framework/KIF.git", + "state" : { + "revision" : "6c3ff27d9449eab614dae63e571596e4982a5205", + "version" : "3.8.9" + } + }, { "identity" : "kingfisher", "kind" : "remoteSourceControl", @@ -36,6 +99,42 @@ "version" : "7.12.0" } }, + { + "identity" : "lottie-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-ios.git", + "state" : { + "revision" : "f522990668c2f9132323a2e68d924c7dcb9130b4", + "version" : "4.4.0" + } + }, + { + "identity" : "mappamundi", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mozilla-mobile/MappaMundi.git", + "state" : { + "branch" : "master", + "revision" : "f56a6e483163a761adc8cd25c337db0ed1eac524" + } + }, + { + "identity" : "rust-components-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ecosia/rust-components-swift/", + "state" : { + "branch" : "133.0.0_Glean_removed", + "revision" : "e23b9086b2ce905e1b3187248c965c123b780fff" + } + }, + { + "identity" : "sdwebimage", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SDWebImage/SDWebImage.git", + "state" : { + "revision" : "10d06f6a33bafae8c164fbfd1f03391f6d4692b3", + "version" : "5.20.0" + } + }, { "identity" : "sentry-cocoa", "kind" : "remoteSourceControl", @@ -45,6 +144,105 @@ "version" : "8.36.0" } }, + { + "identity" : "simplekeychain", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/SimpleKeychain.git", + "state" : { + "revision" : "776c4a6db74d5c6c143974be91c383680d468630", + "version" : "1.3.0" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "e74fe2a978d1216c3602b129447c7301573cc2d8", + "version" : "5.7.0" + } + }, + { + "identity" : "snowplow-ios-tracker", + "kind" : "remoteSourceControl", + "location" : "https://github.com/snowplow/snowplow-ios-tracker", + "state" : { + "revision" : "c105d9f8b875f2442512c3c8b1c8bbe9c209a302", + "version" : "6.0.9" + } + }, + { + "identity" : "swift-asn1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-asn1.git", + "state" : { + "revision" : "ae33e5941bb88d88538d0a6b19ca0b01e6c76dcf", + "version" : "1.3.1" + } + }, + { + "identity" : "swift-certificates", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-certificates.git", + "state" : { + "revision" : "bc566f88842b3b8001717326d935c2d113af5741", + "version" : "1.2.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "f2f3774fd116a305136b6866e5e7cb7dff39d8f2", + "version" : "3.10.1" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", + "version" : "1.3.3" + } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b", + "version" : "1.18.7" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" + } + }, + { + "identity" : "swiftdraw", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swhitty/SwiftDraw", + "state" : { + "revision" : "a5c680f07b33f4cc9a451491b417b8119de23c6e", + "version" : "0.17.0" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup", + "state" : { + "revision" : "f707b8680cddb96dc1855632340a572ef37bbb98", + "version" : "2.5.3" + } + }, { "identity" : "swiftybeaver", "kind" : "remoteSourceControl", @@ -53,6 +251,24 @@ "revision" : "1080914828ef1c9ca9cd2bad50667b3d847dabff", "version" : "2.0.0" } + }, + { + "identity" : "viewinspector", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nalexn/ViewInspector.git", + "state" : { + "revision" : "788e7879d38a839c4e348ab0762dcc0364e646a2", + "version" : "0.10.1" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "b444594f79844b0d6d76d70fbfb3f7f71728f938", + "version" : "1.5.1" + } } ], "version" : 2 diff --git a/BrowserKit/Package.swift b/BrowserKit/Package.swift index f7517670fc313..365eef30ef2fb 100644 --- a/BrowserKit/Package.swift +++ b/BrowserKit/Package.swift @@ -55,7 +55,7 @@ let package = Package( exact: "2.0.0"), .package( url: "https://github.com/getsentry/sentry-cocoa.git", - exact: "8.21.0"), + exact: "8.36.0"), .package( url: "https://github.com/nbhasin2/GCDWebServer.git", branch: "master"), @@ -89,7 +89,7 @@ let package = Package( name: "Common", dependencies: ["Dip", "SwiftyBeaver", - .product(name: "Sentry", package: "sentry-cocoa")], + .product(name: "Sentry-Dynamic", package: "sentry-cocoa")], swiftSettings: [.unsafeFlags(["-enable-testing"])]), .testTarget( name: "CommonTests", diff --git a/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift b/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift index a48d3e2697cd2..272193325b878 100644 --- a/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift +++ b/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift @@ -39,15 +39,25 @@ public struct StandardImageIdentifiers { public struct Large { public static let addToHomescreen = "addToHomescreenLarge" public static let appendUpLeft = "appendUpLeftLarge" + /* Ecosia: Update Menu icon public static let appMenu = "appMenuLarge" + */ + public static let appMenu = "nav-menu" public static let arrowClockwise = "arrowClockwiseLarge" public static let avatarCircle = "avatarCircleLarge" public static let back = "backLarge" + /* Ecosia: Update bookmark images public static let bookmark = "bookmarkLarge" public static let bookmarkFill = "bookmarkFillLarge" public static let bookmarkHalf = "bookmarkHalfLarge" public static let bookmarkSlash = "bookmarkSlashLarge" public static let bookmarkTrayFill = "bookmarkTrayFillLarge" + */ + public static let bookmark = "bookmarksEmpty" + public static let bookmarkFill = "bookmarkFill" + public static let bookmarkHalf = "bookmarkFill" + public static let bookmarkSlash = "bookmarkFill" + public static let bookmarkTrayFill = "bookmarksEmpty" public static let checkmark = "checkmarkLarge" public static let chevronDown = "chevronDownLarge" public static let chevronLeft = "chevronLeftLarge" @@ -83,10 +93,16 @@ public struct StandardImageIdentifiers { public static let lightningFill = "lightningFillLarge" public static let link = "linkLarge" public static let location = "locationLarge" + /* Ecosia: Update lcok image everywhere on the app public static let lock = "lockLarge" public static let lockFill = "lockFillLarge" public static let lockSlash = "lockSlashLarge" public static let lockSlashFill = "lockSlashFillLarge" + */ + public static let lock = "secureLock" + public static let lockFill = "secureLock" + public static let lockSlash = "unsecureLock" + public static let lockSlashFill = "unsecureLock" public static let login = "loginLarge" public static let logoFirefox = "logoFirefoxLarge" public static let nightMode = "nightModeLarge" @@ -98,17 +114,24 @@ public struct StandardImageIdentifiers { public static let pageZoomFilled = "pageZoomFillLarge" public static let pin = "pinLarge" public static let pinSlash = "pinSlashLarge" + public static let pinSlashFill = "pinSlashFillLarge" public static let plus = "plusLarge" public static let price = "priceLarge" public static let print = "printLarge" + /* Ecosia: Update privateMode image everywhere on the app public static let privateMode = "privateModeLarge" + */ + public static let privateMode = "privateSearch" public static let privateModeCircleFill = "privateModeCircleFillLarge" public static let qrCode = "qrCodeLarge" public static let quality = "qualityLarge" public static let readerView = "readerViewLarge" public static let readerViewFill = "readerViewFillLarge" public static let readingList = "readingListLarge" + /* Ecosia: Update `readingListAdd image everywhere on the app public static let readingListAdd = "readingListAddLarge" + */ + public static let readingListAdd = "addToReadingListUpdate" public static let readingListSlashFill = "readingListSlashFillLarge" public static let save = "saveLarge" public static let search = "searchLarge" diff --git a/BrowserKit/Sources/Common/Extensions/URLExtension.swift b/BrowserKit/Sources/Common/Extensions/URLExtension.swift index a4377b852a147..3a3b9ca8ec316 100644 --- a/BrowserKit/Sources/Common/Extensions/URLExtension.swift +++ b/BrowserKit/Sources/Common/Extensions/URLExtension.swift @@ -78,7 +78,9 @@ extension URL { return host } - var normalizedHostAndPath: String? { + // Ecosia: Make it public + // var normalizedHostAndPath: String? { + public var normalizedHostAndPath: String? { return normalizedHost.flatMap { $0 + self.path } } diff --git a/BrowserKit/Sources/Common/Logger/LoggerCategory.swift b/BrowserKit/Sources/Common/Logger/LoggerCategory.swift index f39a6e549c28f..90b26f647f6cf 100644 --- a/BrowserKit/Sources/Common/Logger/LoggerCategory.swift +++ b/BrowserKit/Sources/Common/Logger/LoggerCategory.swift @@ -39,6 +39,9 @@ public enum LoggerCategory: String { /// Related to the application lifecycle. case lifecycle + /// Related to the main menu. + case mainMenu + /// Related to redux library or integration case redux @@ -62,4 +65,10 @@ public enum LoggerCategory: String { /// Remote settings case remoteSettings + + /// Password Generator + case passwordGenerator + + /// Related to wallpaper functionality, like fetching metadata, images, etc + case wallpaper } diff --git a/BrowserKit/Sources/Common/Theming/DarkTheme.swift b/BrowserKit/Sources/Common/Theming/DarkTheme.swift index 8ac10f01d6adb..698fc861f99e3 100644 --- a/BrowserKit/Sources/Common/Theming/DarkTheme.swift +++ b/BrowserKit/Sources/Common/Theming/DarkTheme.swift @@ -6,7 +6,10 @@ import UIKit public struct DarkTheme: Theme { public var type: ThemeType = .dark + /* Ecosia: Use Ecosia color palette subclass public var colors: ThemeColourPalette = DarkColourPalette() + */ + public var colors: EcosiaThemeColourPalette = DarkColourPalette() public init() {} } @@ -17,12 +20,22 @@ public struct DarkTheme: Theme { /// and a regular dark theme. public struct NightModeTheme: Theme { public var type: ThemeType = .nightMode + /* Ecosia: Use Ecosia color palette subclass public var colors: ThemeColourPalette = DarkColourPalette() + */ + public var colors: EcosiaThemeColourPalette = DarkColourPalette() public init() {} } +/* Ecosia: Use Ecosia color palette subclass private struct DarkColourPalette: ThemeColourPalette { + */ +private struct DarkColourPalette: EcosiaThemeColourPalette { + var ecosia: EcosiaSemanticColors { + FakeEcosiaSemanticColors() + } + // MARK: - Layers var layer1: UIColor = FXColors.DarkGrey60 var layer2: UIColor = FXColors.DarkGrey30 @@ -120,4 +133,7 @@ private struct DarkColourPalette: ThemeColourPalette { // MARK: - Shadow var shadowDefault: UIColor = FXColors.DarkGrey90.withAlphaComponent(0.16) + + // MARK: - Ecosia Legacy Colors + var layer6: UIColor = FXColors.DarkGrey60 } diff --git a/BrowserKit/Sources/Common/Theming/DefaultThemeManager.swift b/BrowserKit/Sources/Common/Theming/DefaultThemeManager.swift index 3f08300d5e299..6694c98e50248 100644 --- a/BrowserKit/Sources/Common/Theming/DefaultThemeManager.swift +++ b/BrowserKit/Sources/Common/Theming/DefaultThemeManager.swift @@ -165,10 +165,12 @@ public final class DefaultThemeManager: ThemeManager, Notifiable { } private func applyThemeChanges(for window: WindowUUID, using newTheme: ThemeType) { + /* Ecosia: Remove `overrideUserInterfaceStyle` window set // Overwrite the user interface style on the window attached to our scene // once we have multiple scenes we need to update all of them let style = self.getCurrentTheme(for: window).type.getInterfaceStyle() self.windows[window]?.overrideUserInterfaceStyle = style + */ notifyCurrentThemeDidChange(for: window) } diff --git a/BrowserKit/Sources/Common/Theming/EcosiaThemeColourPalette.swift b/BrowserKit/Sources/Common/Theming/EcosiaThemeColourPalette.swift new file mode 100644 index 0000000000000..9b65761acc35d --- /dev/null +++ b/BrowserKit/Sources/Common/Theming/EcosiaThemeColourPalette.swift @@ -0,0 +1,124 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +// This file is owned by Ecosia, it is only part of BrowserKit.Common since it needs to be used inside it. +// It contains all of Ecosia official semantic color tokens referenced in the link below. Do not add a color that is not mapped there! +// https://www.figma.com/design/8T2rTBVwynJKSdY6MQo5PQ/%E2%9A%9B%EF%B8%8F--Foundations?node-id=2237-3418&t=UKHtrxcc9UtOihsm-0 +// They are adopted by `EcosiaLightTheme` and `EcosiaDarkTheme` and should use `EcosiaColorPrimitive`. +public protocol EcosiaSemanticColors { + // MARK: - Background + var backgroundNeutralInverse: UIColor { get } + var backgroundFeatured: UIColor { get } + var backgroundGradient: UIColor { get } + var backgroundPrimary: UIColor { get } + var backgroundPrimaryDecorative: UIColor { get } + var backgroundSecondary: UIColor { get } + var backgroundTertiary: UIColor { get } + var backgroundQuaternary: UIColor { get } + var backgroundElevation1: UIColor { get } + var backgroundElevation2: UIColor { get } + var backgroundRoleNegative: UIColor { get } + + // MARK: - Border + var borderDecorative: UIColor { get } + var borderNegative: UIColor { get } + + // MARK: - Brand + var brandFeatured: UIColor { get } + var brandPrimary: UIColor { get } + var brandImpact: UIColor { get } + + // MARK: - Button + var buttonBackgroundFeatured: UIColor { get } + var buttonBackgroundFeaturedActive: UIColor { get } + var buttonBackgroundFeaturedHover: UIColor { get } + var buttonBackgroundPrimary: UIColor { get } + var buttonBackgroundPrimaryActive: UIColor { get } + var buttonBackgroundSecondary: UIColor { get } + var buttonBackgroundSecondaryActive: UIColor { get } + var buttonBackgroundSecondaryHover: UIColor { get } + var buttonBackgroundTransparentActive: UIColor { get } + var buttonContentSecondary: UIColor { get } + var buttonContentSecondaryStatic: UIColor { get } + + // MARK: - Link + var linkPrimary: UIColor { get } + + // MARK: - Icon + var iconDecorative: UIColor { get } + var iconInverseStrong: UIColor { get } + + // MARK: - Segmented Control + var segmentedControlBackgroundActive: UIColor { get } + var segmentedControlBackgroundRest: UIColor { get } + + // MARK: - State + var highlighter: UIColor { get } + var stateDisabled: UIColor { get } + var stateError: UIColor { get } + + // MARK: - Switch + var switchKnobActive: UIColor { get } + var switchKnobDisabled: UIColor { get } + + // MARK: - Text + var textPrimary: UIColor { get } + var textInversePrimary: UIColor { get } + var textSecondary: UIColor { get } + var textStaticDark: UIColor { get } + var textStaticLight: UIColor { get } +} + +public protocol EcosiaThemeColourPalette: ThemeColourPalette { + var ecosia: EcosiaSemanticColors { get } +} + +/// Serves to make Firefox themes conform the new protocol. +/// Should never end up in production UI! +class FakeEcosiaSemanticColors: EcosiaSemanticColors { + var backgroundNeutralInverse: UIColor = .systemGray + var backgroundFeatured: UIColor = .systemGray + var backgroundGradient: UIColor = .systemGray + var backgroundPrimary: UIColor = .systemGray + var backgroundPrimaryDecorative: UIColor = .systemGray + var backgroundSecondary: UIColor = .systemGray + var backgroundTertiary: UIColor = .systemGray + var backgroundQuaternary: UIColor = .systemGray + var backgroundElevation1: UIColor = .systemGray + var backgroundElevation2: UIColor = .systemGray + var backgroundRoleNegative: UIColor = .systemGray + var borderDecorative: UIColor = .systemGray + var brandImpact: UIColor = .systemGray + var brandFeatured: UIColor = .systemGray + var brandPrimary: UIColor = .systemGray + var buttonBackgroundFeatured: UIColor = .systemGray + var buttonBackgroundFeaturedActive: UIColor = .systemGray + var buttonBackgroundFeaturedHover: UIColor = .systemGray + var buttonBackgroundPrimary: UIColor = .systemGray + var buttonBackgroundPrimaryActive: UIColor = .systemGray + var buttonBackgroundSecondary: UIColor = .systemGray + var buttonBackgroundSecondaryActive: UIColor = .systemGray + var buttonBackgroundSecondaryHover: UIColor = .systemGray + var buttonBackgroundTransparentActive: UIColor = .systemGray + var buttonContentSecondary: UIColor = .systemGray + var buttonContentSecondaryStatic: UIColor = .systemGray + var borderNegative: UIColor = .systemGray + var highlighter: UIColor = .systemGray + var linkPrimary: UIColor = .systemGray + var iconDecorative: UIColor = .systemGray + var iconInverseStrong: UIColor = .systemGray + var segmentedControlBackgroundActive: UIColor = .systemGray + var segmentedControlBackgroundRest: UIColor = .systemGray + var stateDisabled: UIColor = .systemGray + var stateError: UIColor = .systemGray + var switchKnobActive: UIColor = .systemGray + var switchKnobDisabled: UIColor = .systemGray + var textPrimary: UIColor = .systemGray + var textInversePrimary: UIColor = .systemGray + var textSecondary: UIColor = .systemGray + var textStaticDark: UIColor = .systemGray + var textStaticLight: UIColor = .systemGray +} diff --git a/BrowserKit/Sources/Common/Theming/LightTheme.swift b/BrowserKit/Sources/Common/Theming/LightTheme.swift index c357295b9407a..b8a28bf7a332c 100644 --- a/BrowserKit/Sources/Common/Theming/LightTheme.swift +++ b/BrowserKit/Sources/Common/Theming/LightTheme.swift @@ -6,12 +6,22 @@ import UIKit public struct LightTheme: Theme { public var type: ThemeType = .light + /* Ecosia: Use Ecosia color palette subclass public var colors: ThemeColourPalette = LightColourPalette() + */ + public var colors: EcosiaThemeColourPalette = LightColourPalette() public init() {} } +/* Ecosia: Use Ecosia color palette subclass private struct LightColourPalette: ThemeColourPalette { + */ +private struct LightColourPalette: EcosiaThemeColourPalette { + var ecosia: EcosiaSemanticColors { + FakeEcosiaSemanticColors() + } + // MARK: - Layers var layer1: UIColor = FXColors.LightGrey10 var layer2: UIColor = FXColors.White @@ -109,4 +119,7 @@ private struct LightColourPalette: ThemeColourPalette { // MARK: - Shadow var shadowDefault: UIColor = FXColors.DarkGrey40.withAlphaComponent(0.16) + + // MARK: - Ecosia Legacy Colors + var layer6: UIColor = FXColors.White } diff --git a/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift b/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift index 91c4fafcd48d6..f0bccac000a8f 100644 --- a/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift +++ b/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift @@ -6,12 +6,22 @@ import UIKit public struct PrivateModeTheme: Theme { public var type: ThemeType = .privateMode + /* Ecosia: Use Ecosia color palette subclass public var colors: ThemeColourPalette = PrivateModeColorPalette() + */ + public var colors: EcosiaThemeColourPalette = PrivateModeColorPalette() public init() {} } +/* Ecosia: Use Ecosia color palette subclass private struct PrivateModeColorPalette: ThemeColourPalette { + */ +private struct PrivateModeColorPalette: EcosiaThemeColourPalette { + var ecosia: EcosiaSemanticColors { + FakeEcosiaSemanticColors() + } + // MARK: - Layers var layer1: UIColor = FXColors.Violet90 var layer2: UIColor = FXColors.Violet90 @@ -111,4 +121,7 @@ private struct PrivateModeColorPalette: ThemeColourPalette { // MARK: - Shadow var shadowDefault: UIColor = FXColors.DarkGrey90.withAlphaComponent(0.16) + + // MARK: - Ecosia Legacy Colors + var layer6: UIColor = FXColors.DarkGrey60 } diff --git a/BrowserKit/Sources/Common/Theming/Theme.swift b/BrowserKit/Sources/Common/Theming/Theme.swift index 2c7db6d67f69e..71a13c1eae21a 100644 --- a/BrowserKit/Sources/Common/Theming/Theme.swift +++ b/BrowserKit/Sources/Common/Theming/Theme.swift @@ -9,5 +9,8 @@ import Foundation /// dark mode) and fonts for the application. public protocol Theme { var type: ThemeType { get } + /* Ecosia: Use Ecosia color palette subclass var colors: ThemeColourPalette { get } + */ + var colors: EcosiaThemeColourPalette { get } } diff --git a/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift b/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift index 915566c898b2f..b193141b166a9 100644 --- a/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift +++ b/BrowserKit/Sources/ComponentLibrary/Buttons/ResizableButton.swift @@ -19,6 +19,13 @@ open class ResizableButton: UIButton { } } + // Ecosia: Add support to custom font + public var customFont: UIFont? { + didSet { + updateFontConfiguration() + } + } + override public init(frame: CGRect) { super.init(frame: frame) commonInit() @@ -44,8 +51,13 @@ open class ResizableButton: UIButton { } let size = title.sizeThatFits(CGSize(width: availableWidth, height: .greatestFiniteMagnitude)) + /* Ecosia: Update Size calculation return CGSize(width: size.width + widthContentInset, height: size.height + heightContentInset) + */ + let boundingBox = title.text?.boundingRect(with: size, options: .usesLineFragmentOrigin, context: nil) ?? .zero + return CGSize(width: boundingBox.width + widthContentInset, + height: boundingBox.height + heightContentInset) } override public func layoutSubviews() { @@ -63,6 +75,24 @@ open class ResizableButton: UIButton { configuration = UIButton.Configuration.plain() configuration?.titleLineBreakMode = .byWordWrapping updateContentInsets() + // Ecosia: Add support to custom font + updateFontConfiguration() + } + + // Ecosia: Add support to custom font + private func updateFontConfiguration() { + guard let font = customFont else { return } + + configuration?.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in + var outgoing = incoming + // Respect Dynamic Type scaling if the original font does + if incoming.font?.fontDescriptor.symbolicTraits.contains(.traitBold) ?? false { + outgoing.font = UIFontMetrics.default.scaledFont(for: font) + } else { + outgoing.font = font + } + return outgoing + } } private func updateContentInsets() { diff --git a/BrowserKit/Sources/ComponentLibrary/Buttons/ThemedSwitch.swift b/BrowserKit/Sources/ComponentLibrary/Buttons/ThemedSwitch.swift index 64cdd37742f77..49d5e8e7b919d 100644 --- a/BrowserKit/Sources/ComponentLibrary/Buttons/ThemedSwitch.swift +++ b/BrowserKit/Sources/ComponentLibrary/Buttons/ThemedSwitch.swift @@ -5,6 +5,8 @@ import Common import UIKit +// Ecosia: Prefer `EcosiaThemedSwitch` instead! + /// The themed `UISwitch` used in the ``PaddedSwitch`` public class ThemedSwitch: UISwitch, ThemeApplicable { private var enabledOnColor: UIColor? diff --git a/BrowserKit/Sources/ComponentLibrary/ContextualHintView/ContextualHintView.swift b/BrowserKit/Sources/ComponentLibrary/ContextualHintView/ContextualHintView.swift index 31d0ef35e6331..b6f63b46c7304 100644 --- a/BrowserKit/Sources/ComponentLibrary/ContextualHintView/ContextualHintView.swift +++ b/BrowserKit/Sources/ComponentLibrary/ContextualHintView/ContextualHintView.swift @@ -38,12 +38,14 @@ public class ContextualHintView: UIView, ThemeApplicable { label.font = FXFontStyles.Regular.headline.scaledFont() label.textAlignment = .left label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true } private lazy var descriptionLabel: UILabel = .build { label in label.font = FXFontStyles.Regular.body.scaledFont() label.textAlignment = .left label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true } private lazy var actionButton: LinkButton = .build { button in @@ -92,7 +94,8 @@ public class ContextualHintView: UIView, ThemeApplicable { closeButton.accessibilityLabel = viewModel.closeButtonA11yLabel descriptionLabel.text = viewModel.description - layer.addSublayer(gradient) + // Ecosia: Remove gradient + // layer.addSublayer(gradient) addSubview(scrollView) addSubview(closeButton) @@ -161,6 +164,12 @@ public class ContextualHintView: UIView, ThemeApplicable { closeButton.tintColor = theme.colors.textOnDark titleLabel.textColor = theme.colors.textOnDark descriptionLabel.textColor = theme.colors.textOnDark + // Ecosia: Update custom background theming + if theme.type == .light { + backgroundColor = UIColor(red: 0.153, green: 0.322, blue: 0.263, alpha: 1) + } else { + backgroundColor = UIColor(rgb: 0xAFE9B0) + } gradient.colors = theme.colors.layerGradient.cgColors if viewModel.isActionType { diff --git a/BrowserKit/Sources/ComponentLibrary/Headers/HeaderView.swift b/BrowserKit/Sources/ComponentLibrary/Headers/HeaderView.swift index 4ad96f23751a1..96eb01dc22ef4 100644 --- a/BrowserKit/Sources/ComponentLibrary/Headers/HeaderView.swift +++ b/BrowserKit/Sources/ComponentLibrary/Headers/HeaderView.swift @@ -9,10 +9,11 @@ import SiteImageView public final class HeaderView: UIView, ThemeApplicable { private struct UX { static let headerLinesLimit: Int = 2 - static let siteDomainLabelsVerticalSpacing: CGFloat = 12 + static let siteDomainLabelsVerticalSpacing: CGFloat = 13 + static let siteDomainLabelsHorizontalSpacing: CGFloat = 12 static let largeFaviconImageSize: CGFloat = 48 static let favIconImageSize: CGFloat = 32 - static let smallFaviconImageSize: CGFloat = 20 + static let smallFaviconImageSize: CGFloat = 24 static let maskFaviconImageSize: CGFloat = 32 static let horizontalMargin: CGFloat = 16 static let headerLabelDistance: CGFloat = 2 @@ -90,10 +91,12 @@ public final class HeaderView: UIView, ThemeApplicable { warningIconView.removeConstraints(warningIconView.constraints) iconMask.removeConstraints(iconMask.constraints) viewConstraints.removeAll() + let favIconPadding = (UX.maskFaviconImageSize / 2) - (UX.smallFaviconImageSize / 2) + let favIconLeadingConstant = isWebsiteIcon ? UX.horizontalMargin : UX.horizontalMargin + favIconPadding viewConstraints.append(contentsOf: [ favicon.leadingAnchor.constraint( equalTo: self.leadingAnchor, - constant: UX.horizontalMargin + constant: favIconLeadingConstant ), headerLabelsContainer.topAnchor.constraint( @@ -105,8 +108,8 @@ public final class HeaderView: UIView, ThemeApplicable { constant: -UX.siteDomainLabelsVerticalSpacing ), headerLabelsContainer.leadingAnchor.constraint( - equalTo: favicon.trailingAnchor, - constant: UX.siteDomainLabelsVerticalSpacing + equalTo: isWebsiteIcon ? favicon.trailingAnchor : iconMask.trailingAnchor, + constant: UX.siteDomainLabelsHorizontalSpacing ), headerLabelsContainer.trailingAnchor.constraint( equalTo: warningIconView.leadingAnchor, @@ -246,7 +249,10 @@ public final class HeaderView: UIView, ThemeApplicable { titleLabel.textColor = theme.colors.textPrimary self.tintColor = theme.colors.layer2 closeButton.setImage(buttonImage, for: .normal) + /* Ecosia: Update close button background color closeButton.backgroundColor = theme.colors.layer2 + */ + closeButton.backgroundColor = theme.colors.ecosia.buttonBackgroundSecondary horizontalLine.backgroundColor = theme.colors.borderPrimary } } diff --git a/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift b/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift index 906973f0a7e18..4ff5092be026b 100644 --- a/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift +++ b/BrowserKit/Sources/ComponentLibrary/Headers/NavigationHeaderView.swift @@ -9,7 +9,7 @@ public final class NavigationHeaderView: UIView { private struct UX { static let closeButtonSize: CGFloat = 30 static let imageMargins: CGFloat = 10 - static let baseDistance: CGFloat = 20 + static let baseDistance: CGFloat = 21 static let horizontalMargin: CGFloat = 16 static let separatorHeight: CGFloat = 1 static let largeFaviconImageSize: CGFloat = 48 @@ -98,11 +98,16 @@ public final class NavigationHeaderView: UIView { public func setupAccessibility(closeButtonA11yLabel: String, closeButtonA11yId: String, + titleA11yId: String? = nil, backButtonA11yLabel: String, backButtonA11yId: String) { let closeButtonViewModel = CloseButtonViewModel(a11yLabel: closeButtonA11yLabel, a11yIdentifier: closeButtonA11yId) closeButton.configure(viewModel: closeButtonViewModel) + if let titleA11yId { + titleLabel.isAccessibilityElement = true + titleLabel.accessibilityIdentifier = titleA11yId + } backButton.accessibilityIdentifier = backButtonA11yId backButton.accessibilityLabel = backButtonA11yLabel } @@ -140,7 +145,10 @@ public final class NavigationHeaderView: UIView { let buttonImage = UIImage(named: StandardImageIdentifiers.Medium.cross)? .withTintColor(theme.colors.iconSecondary) closeButton.setImage(buttonImage, for: .normal) + /* Ecosia: Update close button background color closeButton.backgroundColor = theme.colors.layer2 + */ + closeButton.backgroundColor = theme.colors.ecosia.buttonBackgroundSecondary backButton.tintColor = theme.colors.iconAccent backButton.setTitleColor(theme.colors.textAccent, for: .normal) horizontalLine.backgroundColor = theme.colors.borderPrimary diff --git a/BrowserKit/Sources/MenuKit/MenuCell.swift b/BrowserKit/Sources/MenuKit/MenuCell.swift index 80af2bad3b2a6..cc17c6d4be3dc 100644 --- a/BrowserKit/Sources/MenuKit/MenuCell.swift +++ b/BrowserKit/Sources/MenuKit/MenuCell.swift @@ -8,10 +8,11 @@ import UIKit public class MenuCell: UITableViewCell, ReusableCell, ThemeApplicable { private struct UX { - static let contentMargin: CGFloat = 10 + static let contentMargin: CGFloat = 12 static let iconSize: CGFloat = 24 static let largeIconSize: CGFloat = 48 - static let contentSpacing: CGFloat = 2 + static let contentSpacing: CGFloat = 3 + static let noDescriptionContentSpacing: CGFloat = 0 } private var separatorInsetSize: CGFloat { @@ -21,7 +22,7 @@ public class MenuCell: UITableViewCell, ReusableCell, ThemeApplicable { // MARK: - UI Elements private var titleLabel: UILabel = .build { label in label.font = FXFontStyles.Regular.body.scaledFont() - label.numberOfLines = 2 + label.numberOfLines = 0 } private var descriptionLabel: UILabel = .build { label in @@ -33,7 +34,6 @@ public class MenuCell: UITableViewCell, ReusableCell, ThemeApplicable { private var contentStackView: UIStackView = .build { stackView in stackView.axis = .vertical stackView.distribution = .fillProportionally - stackView.spacing = UX.contentSpacing } private var accessoryArrowView: UIImageView = .build() @@ -54,10 +54,12 @@ public class MenuCell: UITableViewCell, ReusableCell, ThemeApplicable { self.model = model self.titleLabel.text = model.title self.descriptionLabel.text = model.description + self.contentStackView.spacing = model.description != nil ? UX.contentSpacing : UX.noDescriptionContentSpacing self.icon.image = UIImage(named: model.iconName)?.withRenderingMode(.alwaysTemplate) self.accessoryArrowView.image = UIImage(named: StandardImageIdentifiers.Large.chevronRight)?.withRenderingMode(.alwaysTemplate) self.isAccessibilityElement = true + self.isUserInteractionEnabled = !model.isEnabled ? false : true self.accessibilityIdentifier = model.a11yId self.accessibilityLabel = model.a11yLabel self.accessibilityHint = model.a11yHint diff --git a/BrowserKit/Sources/MenuKit/MenuDetailView.swift b/BrowserKit/Sources/MenuKit/MenuDetailView.swift index dbcb873e7caf5..dec2fb30374a8 100644 --- a/BrowserKit/Sources/MenuKit/MenuDetailView.swift +++ b/BrowserKit/Sources/MenuKit/MenuDetailView.swift @@ -10,7 +10,8 @@ public final class MenuDetailView: UIView, MenuTableViewDataDelegate, ThemeApplicable { private struct UX { static let headerLineOffset: CGFloat = 35 - static let headerTopMargin: CGFloat = 10 + static let headerTopMargin: CGFloat = 15 + static let horizontalTableViewMargin: CGFloat = 4 } // MARK: - UI Elements @@ -41,17 +42,19 @@ public final class MenuDetailView: UIView, tableView.topAnchor.constraint(equalTo: detailHeaderView.bottomAnchor), tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor), - tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor), - tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor) + tableView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: -UX.horizontalTableViewMargin), + tableView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: UX.horizontalTableViewMargin) ]) } public func setupAccessibilityIdentifiers(closeButtonA11yLabel: String, closeButtonA11yId: String, + titleA11yId: String? = nil, backButtonA11yLabel: String, backButtonA11yId: String) { detailHeaderView.setupAccessibility(closeButtonA11yLabel: closeButtonA11yLabel, closeButtonA11yId: closeButtonA11yId, + titleA11yId: titleA11yId, backButtonA11yLabel: backButtonA11yLabel, backButtonA11yId: backButtonA11yId) } diff --git a/BrowserKit/Sources/MenuKit/MenuMainView.swift b/BrowserKit/Sources/MenuKit/MenuMainView.swift index 70ed0f41f9ddf..53d8d2d37cfb4 100644 --- a/BrowserKit/Sources/MenuKit/MenuMainView.swift +++ b/BrowserKit/Sources/MenuKit/MenuMainView.swift @@ -9,7 +9,8 @@ import ComponentLibrary public final class MenuMainView: UIView, MenuTableViewDataDelegate, ThemeApplicable { private struct UX { - static let headerTopMargin: CGFloat = 10 + static let headerTopMargin: CGFloat = 15 + static let horizontalTableViewMargin: CGFloat = 4 } // MARK: - UI Elements diff --git a/BrowserKit/Sources/MenuKit/MenuTableView.swift b/BrowserKit/Sources/MenuKit/MenuTableView.swift index af0b071d84235..49c24c2ddc4bb 100644 --- a/BrowserKit/Sources/MenuKit/MenuTableView.swift +++ b/BrowserKit/Sources/MenuKit/MenuTableView.swift @@ -14,7 +14,9 @@ class MenuTableView: UIView, UITableViewDelegate, UITableViewDataSource, ThemeApplicable { private struct UX { - static let topPadding: CGFloat = 10 + static let topPadding: CGFloat = 12 + static let tableViewMargin: CGFloat = 16 + static let distanceBetweenSections: CGFloat = 32 } private var tableView: UITableView @@ -25,6 +27,8 @@ class MenuTableView: UIView, override init(frame: CGRect) { tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.layoutMargins = UIEdgeInsets(top: 0, left: UX.tableViewMargin, bottom: 0, right: UX.tableViewMargin) + tableView.sectionFooterHeight = 0 menuData = [] super.init(frame: .zero) setupView() @@ -69,7 +73,7 @@ class MenuTableView: UIView, _ tableView: UITableView, heightForHeaderInSection section: Int ) -> CGFloat { - return section == 0 ? UX.topPadding : UITableView.automaticDimension + return section == 0 ? UX.topPadding : UX.distanceBetweenSections } func tableView( @@ -134,5 +138,6 @@ class MenuTableView: UIView, self.theme = theme backgroundColor = .clear tableView.backgroundColor = .clear + tableView.separatorColor = theme.colors.borderPrimary } } diff --git a/BrowserKit/Sources/SiteImageView/Entities/SiteResource.swift b/BrowserKit/Sources/SiteImageView/Entities/SiteResource.swift index 2fdefb30dbd28..42c4c5e77737d 100644 --- a/BrowserKit/Sources/SiteImageView/Entities/SiteResource.swift +++ b/BrowserKit/Sources/SiteImageView/Entities/SiteResource.swift @@ -5,7 +5,7 @@ import Foundation /// The source of a favicon or hero image. -public enum SiteResource: Codable { +public enum SiteResource: Codable, Hashable { /// An image that may be downloaded over the network. /// - Parameter url: The URL of the image. case remoteURL(url: URL) @@ -13,4 +13,14 @@ public enum SiteResource: Codable { /// - Parameter name: The name of the image. /// - Parameter forRemoteResource: The URL from which this bundled image was obtained. Can be cached for future requests. case bundleAsset(name: String, forRemoteResource: URL) + + public func hash(into hasher: inout Hasher) { + switch self { + case let .remoteURL(url): + hasher.combine(url) + case let .bundleAsset(name, forRemoteResource): + hasher.combine(name) + hasher.combine(forRemoteResource) + } + } } diff --git a/BrowserKit/Sources/SiteImageView/FaviconImageView.swift b/BrowserKit/Sources/SiteImageView/FaviconImageView.swift index 409a3862b46a0..c5e33f0fd1a07 100644 --- a/BrowserKit/Sources/SiteImageView/FaviconImageView.swift +++ b/BrowserKit/Sources/SiteImageView/FaviconImageView.swift @@ -60,10 +60,15 @@ public class FaviconImageView: UIImageView, SiteImageView { // MARK: - SiteImageView func setURL(_ viewModel: FaviconImageViewModel) { + /* Ecosia: Make sure url is set even if cell is re-used with same url (was causing MOB-3212) guard let siteURLString = viewModel.siteURLString, let siteURL = URL(string: siteURLString, invalidCharacters: false), canMakeRequest(with: siteURLString) else { return } + */ + guard let siteURLString = viewModel.siteURLString, + let siteURL = URL(string: siteURLString, invalidCharacters: false) + else { return } // If a new request is being made on an existing image it is likely a cell or view being reused. // Continuing to display the previous image in this case would never be desired so reset to nil diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift index 8d57da7c0cca7..d88be980f7c47 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift @@ -36,6 +36,10 @@ public class BrowserAddressToolbar: UIView, private var isUnifiedSearchEnabled = false private var droppableUrl: URL? + /// A cache of `ToolbarButton` instances keyed by their accessibility identifier (`a11yId`). + /// This improves performance by reusing buttons instead of creating new instances. + private(set) var cachedButtonReferences = [String: ToolbarButton]() + private lazy var toolbarContainerView: UIView = .build() private lazy var navigationActionStack: UIStackView = .build() @@ -221,6 +225,7 @@ public class BrowserAddressToolbar: UIView, setNeedsLayout() } + // MARK: - Toolbar Actions and Layout Updates internal func updateActions(state: AddressToolbarState) { // Browser actions updateActionStack(stackView: browserActionStack, toolbarElements: state.browserActions) @@ -245,10 +250,28 @@ public class BrowserAddressToolbar: UIView, widthAnchor.priority = .defaultHigh } + /// Retrieves a `ToolbarButton` for the given `ToolbarElement`. + /// If a cached button exists for the element's accessibility identifier, it returns the cached button. + /// Otherwise, it creates a new button, caches it, and then returns it. + /// - Parameter toolbarElement: The `ToolbarElement` for which to retrieve the button. + /// - Returns: A `ToolbarButton` instance configured for the given `ToolbarElement`. + func getToolbarButton(for toolbarElement: ToolbarElement) -> ToolbarButton { + let button: ToolbarButton + if let cachedButton = cachedButtonReferences[toolbarElement.a11yId] { + button = cachedButton + } else { + button = toolbarElement.numberOfTabs != nil ? TabNumberButton() : ToolbarButton() + cachedButtonReferences[toolbarElement.a11yId] = button + } + + return button + } + private func updateActionStack(stackView: UIStackView, toolbarElements: [ToolbarElement]) { stackView.removeAllArrangedViews() + toolbarElements.forEach { toolbarElement in - let button = toolbarElement.numberOfTabs != nil ? TabNumberButton() : ToolbarButton() + let button = getToolbarButton(for: toolbarElement) button.configure(element: toolbarElement) stackView.addArrangedSubview(button) diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift index ef24a2db13ec3..3ed8da6d3418d 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift @@ -16,6 +16,7 @@ final class LocationView: UIView, static let gradientViewWidth: CGFloat = 40 static let iconContainerCornerRadius: CGFloat = 8 static let lockIconImageViewSize = CGSize(width: 40, height: 24) + static let iconContainerNoLockLeadingSpace: CGFloat = 16 } private var urlAbsolutePath: String? @@ -25,6 +26,7 @@ final class LocationView: UIView, private weak var delegate: LocationViewDelegate? private var isUnifiedSearchEnabled = false private var lockIconImageName: String? + private var lockIconNeedsTheming = false private var safeListedURLImageName: String? private var isEditing = false @@ -247,7 +249,10 @@ final class LocationView: UIView, removeContainerIcons() iconContainerStackView.addArrangedSubview(lockIconButton) updateURLTextFieldLeadingConstraint() - iconContainerStackViewLeadingConstraint?.constant = 0 + + let leadingConstraint = lockIconImageName == nil ? UX.iconContainerNoLockLeadingSpace : 0.0 + + iconContainerStackViewLeadingConstraint?.constant = leadingConstraint updateGradient() } @@ -320,6 +325,7 @@ final class LocationView: UIView, // MARK: - `lockIconButton` Configuration private func configureLockIconButton(_ state: LocationViewState) { lockIconImageName = state.lockIconImageName + lockIconNeedsTheming = state.lockIconNeedsTheming safeListedURLImageName = state.safeListedURLImageName guard lockIconImageName != nil else { updateWidthForLockIcon(0) @@ -336,13 +342,23 @@ final class LocationView: UIView, var lockImage: UIImage? if let safeListedURLImageName { - lockImage = UIImage(named: lockIconImageName)?.withTintColor(lockIconImageColor) + lockImage = UIImage(named: lockIconImageName) + + if lockIconNeedsTheming { + lockImage = lockImage?.withTintColor(lockIconImageColor) + } + if let dotImage = UIImage(named: safeListedURLImageName)?.withTintColor(safeListedURLImageColor) { let image = lockImage!.overlayWith(image: dotImage, modifier: 0.4, origin: CGPoint(x: 13.5, y: 13)) lockIconButton.setImage(image, for: .normal) } } else { - lockImage = UIImage(named: lockIconImageName)?.withRenderingMode(.alwaysTemplate) + lockImage = UIImage(named: lockIconImageName) + + if lockIconNeedsTheming { + lockImage = lockImage?.withRenderingMode(.alwaysTemplate) + } + lockIconButton.setImage(lockImage, for: .normal) } } @@ -449,11 +465,14 @@ final class LocationView: UIView, gradientLayer.colors = colors.layerGradientURL.cgColors.reversed() searchEngineContentView.applyTheme(theme: theme) iconContainerBackgroundView.backgroundColor = colors.layerSearch - lockIconButton.tintColor = colors.iconPrimary lockIconButton.backgroundColor = colors.layerSearch urlTextField.applyTheme(theme: theme) - lockIconImageColor = colors.iconPrimary safeListedURLImageColor = colors.iconAccentBlue + + if lockIconNeedsTheming { + lockIconButton.tintColor = colors.iconPrimary + lockIconImageColor = colors.iconPrimary + } setLockIconImage() } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift index a28744b1a85ce..014dee7241b5d 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift @@ -16,6 +16,7 @@ public struct LocationViewState { public let searchEngineImage: UIImage? public let lockIconImageName: String? + public let lockIconNeedsTheming: Bool public let safeListedURLImageName: String? public let url: URL? public let droppableUrl: URL? @@ -36,6 +37,7 @@ public struct LocationViewState { urlTextFieldA11yId: String, searchEngineImage: UIImage?, lockIconImageName: String?, + lockIconNeedsTheming: Bool, safeListedURLImageName: String?, url: URL?, droppableUrl: URL?, @@ -55,6 +57,7 @@ public struct LocationViewState { self.urlTextFieldA11yId = urlTextFieldA11yId self.searchEngineImage = searchEngineImage self.lockIconImageName = lockIconImageName + self.lockIconNeedsTheming = lockIconNeedsTheming self.safeListedURLImageName = safeListedURLImageName self.url = url self.droppableUrl = droppableUrl diff --git a/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift b/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift index 694ff19a2a44c..c890428bed893 100644 --- a/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift +++ b/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift @@ -47,7 +47,8 @@ class ToolbarButton: UIButton, ThemeApplicable { let image = imageConfiguredForRTL(for: element) let action = UIAction(title: element.a11yLabel, image: image, - handler: { _ in + handler: { [weak self] _ in + guard let self else { return } element.onSelected?(self) }) @@ -57,6 +58,12 @@ class ToolbarButton: UIButton, ThemeApplicable { accessibilityIdentifier = element.a11yId accessibilityLabel = element.a11yLabel accessibilityHint = element.a11yHint + // Remove all existing actions for .touchUpInside before adding the new one + // This ensures that we do not accumulate multiple actions for the same event, + // which can cause the action to be called multiple times when the button is tapped. + // By removing all existing actions first, we guarantee that only the new action + // will be associated with the .touchUpInside event. + removeTarget(nil, action: nil, for: .touchUpInside) addAction(action, for: .touchUpInside) showsLargeContentViewer = true @@ -69,6 +76,12 @@ class ToolbarButton: UIButton, ThemeApplicable { if let maskImageName = element.maskImageName { addMaskIcon(maskImageName: maskImageName) } + } else { + // Remove badge & mask icons + imageView?.subviews.forEach { view in + guard view as? UIImageView != nil else { return } + view.removeFromSuperview() + } } layoutIfNeeded() } diff --git a/BrowserKit/Sources/WebEngine/WKWebview/WKEngineSession.swift b/BrowserKit/Sources/WebEngine/WKWebview/WKEngineSession.swift index 17f7fc0604300..00e8b2113688c 100644 --- a/BrowserKit/Sources/WebEngine/WKWebview/WKEngineSession.swift +++ b/BrowserKit/Sources/WebEngine/WKWebview/WKEngineSession.swift @@ -4,7 +4,7 @@ import Common import Foundation -import WebKit +@preconcurrency import WebKit class WKEngineSession: NSObject, EngineSession, diff --git a/BrowserKit/Tests/SiteImageViewTests/ImageProcessing/ImageHandlerTests.swift b/BrowserKit/Tests/SiteImageViewTests/ImageProcessing/ImageHandlerTests.swift index 117730ce5abb0..8119e66dd5e56 100644 --- a/BrowserKit/Tests/SiteImageViewTests/ImageProcessing/ImageHandlerTests.swift +++ b/BrowserKit/Tests/SiteImageViewTests/ImageProcessing/ImageHandlerTests.swift @@ -63,60 +63,61 @@ final class ImageHandlerTests: XCTestCase { XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) } - func testFavicon_whenSiteResourceNil_imageIsInBundle_noCachedImage_returnsBundleImage() async { - // provide this site url, since the cache key is "google" and default favicons are store with cacheKey as name - // in bundle - let siteURL = URL(string: "https://www.google.com")! - let subject = createSubject() - let model = SiteImageModel(id: UUID(), imageType: .favicon, siteURL: siteURL) - let image = await subject.fetchFavicon(imageModel: model) - - let siteImageBundle = Bundle.allBundles.first { - return $0.bundleIdentifier?.contains("SiteImageView-resources") ?? false - }! - let expectedImage = UIImage(named: "google", in: siteImageBundle, with: nil) - XCTAssertEqual(expectedImage, image) - XCTAssertEqual(siteImageCache.cacheImageCalled, 0) - XCTAssertEqual(faviconFetcher.fetchImageSucceedCalled, 0) - XCTAssertEqual(faviconFetcher.fetchImageFailedCalled, 0) - XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) - } - - func testFavicon_whenSiteResourceNil_imageIsInBundle_cachedImagePresent_returnsBundleImage() async { - let siteURL = URL(string: "https://www.google.com")! - siteImageCache.image = UIImage() - let subject = createSubject() - let model = SiteImageModel(id: UUID(), imageType: .favicon, siteURL: siteURL) - let image = await subject.fetchFavicon(imageModel: model) - - let siteImageBundle = Bundle.allBundles.first { - return $0.bundleIdentifier?.contains("SiteImageView-resources") ?? false - }! - let expectedImage = UIImage(named: "google", in: siteImageBundle, with: nil) - XCTAssertEqual(expectedImage, image) - XCTAssertEqual(siteImageCache.cacheImageCalled, 0) - XCTAssertEqual(faviconFetcher.fetchImageSucceedCalled, 0) - XCTAssertEqual(faviconFetcher.fetchImageFailedCalled, 0) - XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) - } - - func testFavicon_whenSiteResourceIsInBundle_returnsBundleImage() async { - let siteURL = URL(string: "https://www.facebook.com")! - let subject = createSubject() - let resource: SiteResource = .bundleAsset(name: "facebook", forRemoteResource: siteURL) - let model = SiteImageModel(id: UUID(), imageType: .favicon, siteURL: siteURL, siteResource: resource) - let image = await subject.fetchFavicon(imageModel: model) - - let siteImageBundle = Bundle.allBundles.first { - return $0.bundleIdentifier?.contains("SiteImageView-resources") ?? false - }! - let expectedImage = UIImage(named: "facebook", in: siteImageBundle, with: nil) - XCTAssertEqual(expectedImage, image) - XCTAssertEqual(siteImageCache.cacheImageCalled, 0) - XCTAssertEqual(faviconFetcher.fetchImageSucceedCalled, 0) - XCTAssertEqual(faviconFetcher.fetchImageFailedCalled, 0) - XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) - } + // TODO: FXIOS-10642, loading images from the bundle this way doesn't work in XCode 16 +// func testFavicon_whenSiteResourceNil_imageIsInBundle_noCachedImage_returnsBundleImage() async { +// // provide this site url, since the cache key is "google" and default favicons are store with cacheKey as name +// // in bundle +// let siteURL = URL(string: "https://www.google.com")! +// let subject = createSubject() +// let model = SiteImageModel(id: UUID(), imageType: .favicon, siteURL: siteURL) +// let image = await subject.fetchFavicon(imageModel: model) +// +// let siteImageBundle = Bundle.allBundles.first { +// return $0.bundleIdentifier?.contains("SiteImageView-resources") ?? false +// }! +// let expectedImage = UIImage(named: "google", in: siteImageBundle, with: nil) +// XCTAssertEqual(expectedImage, image) +// XCTAssertEqual(siteImageCache.cacheImageCalled, 0) +// XCTAssertEqual(faviconFetcher.fetchImageSucceedCalled, 0) +// XCTAssertEqual(faviconFetcher.fetchImageFailedCalled, 0) +// XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) +// } +// +// func testFavicon_whenSiteResourceNil_imageIsInBundle_cachedImagePresent_returnsBundleImage() async { +// let siteURL = URL(string: "https://www.google.com")! +// siteImageCache.image = UIImage() +// let subject = createSubject() +// let model = SiteImageModel(id: UUID(), imageType: .favicon, siteURL: siteURL) +// let image = await subject.fetchFavicon(imageModel: model) +// +// let siteImageBundle = Bundle.allBundles.first { +// return $0.bundleIdentifier?.contains("SiteImageView-resources") ?? false +// }! +// let expectedImage = UIImage(named: "google", in: siteImageBundle, with: nil) +// XCTAssertEqual(expectedImage, image) +// XCTAssertEqual(siteImageCache.cacheImageCalled, 0) +// XCTAssertEqual(faviconFetcher.fetchImageSucceedCalled, 0) +// XCTAssertEqual(faviconFetcher.fetchImageFailedCalled, 0) +// XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) +// } +// +// func testFavicon_whenSiteResourceIsInBundle_returnsBundleImage() async { +// let siteURL = URL(string: "https://www.facebook.com")! +// let subject = createSubject() +// let resource: SiteResource = .bundleAsset(name: "facebook", forRemoteResource: siteURL) +// let model = SiteImageModel(id: UUID(), imageType: .favicon, siteURL: siteURL, siteResource: resource) +// let image = await subject.fetchFavicon(imageModel: model) +// +// let siteImageBundle = Bundle.allBundles.first { +// return $0.bundleIdentifier?.contains("SiteImageView-resources") ?? false +// }! +// let expectedImage = UIImage(named: "facebook", in: siteImageBundle, with: nil) +// XCTAssertEqual(expectedImage, image) +// XCTAssertEqual(siteImageCache.cacheImageCalled, 0) +// XCTAssertEqual(faviconFetcher.fetchImageSucceedCalled, 0) +// XCTAssertEqual(faviconFetcher.fetchImageFailedCalled, 0) +// XCTAssertEqual(letterImageGenerator.generateLetterImageCalled, 0) +// } func testFavicon_whenNoImages_returnsFallbackLetterFavicon_forHardcodedFaviconURL() async { let subject = createSubject() diff --git a/BrowserKit/Tests/ToolbarKitTests/BrowserAddressToolbarTests.swift b/BrowserKit/Tests/ToolbarKitTests/BrowserAddressToolbarTests.swift new file mode 100644 index 0000000000000..704551cb3dfc7 --- /dev/null +++ b/BrowserKit/Tests/ToolbarKitTests/BrowserAddressToolbarTests.swift @@ -0,0 +1,86 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import ToolbarKit + +final class BrowserAddressToolbarTests: XCTestCase { + private var sut: BrowserAddressToolbar? + private var toolbarElement: ToolbarElement? + private var toolbarElement2: ToolbarElement? + + override func setUp() { + super.setUp() + sut = BrowserAddressToolbar() + + toolbarElement = ToolbarElement( + iconName: "icon", + isEnabled: true, + isSelected: false, + a11yLabel: "Test Button", + a11yHint: nil, + a11yId: "a11yID-1", + a11yCustomActionName: nil, + a11yCustomAction: nil, + hasLongPressAction: false, + onSelected: nil, + onLongPress: nil + ) + + toolbarElement2 = ToolbarElement( + iconName: "icon2", + isEnabled: true, + isSelected: false, + a11yLabel: "Test Button2", + a11yHint: nil, + a11yId: "a11yID-2", + a11yCustomActionName: nil, + a11yCustomAction: nil, + hasLongPressAction: false, + onSelected: nil, + onLongPress: nil + ) + } + + override func tearDown() { + sut = nil + toolbarElement = nil + toolbarElement2 = nil + super.tearDown() + } + + func testGetToolbarButton_CreatesAndReturnsTheCachedButton() { + // First call to getToolbarButton should create a new button + guard let toolbarElement else { return } + let button1 = sut?.getToolbarButton(for: toolbarElement) + XCTAssertNotNil(button1, "Button should not be nil.") + + // Second call to getToolbarButton should return the cached button + let button2 = sut?.getToolbarButton(for: toolbarElement) + XCTAssertNotNil(button2, "Button should not be nil.") + + // Verify that the same button instance is returned + XCTAssertEqual(button1, button2, "The button should be cached and reused.") + + // Verify the cache count + XCTAssertEqual(sut?.cachedButtonReferences.count, 1, "Cache should contain one button.") + } + + func testGetToolbarButton_CreatesNewButtonForDifferentElements() { + guard let toolbarElement, let toolbarElement2 else { return } + // First call to getToolbarButton should create a new button for the first element + let button1 = sut?.getToolbarButton(for: toolbarElement) + XCTAssertNotNil(button1, "Button should not be nil.") + + // First call to getToolbarButton should create a new button for the second element + let button2 = sut?.getToolbarButton(for: toolbarElement2) + XCTAssertNotNil(button2, "Button should not be nil.") + + // Verify that different button instances are returned for different elements + XCTAssertNotEqual(button1, button2, "Different button instances should be created for different elements.") + + // Verify the cache count + XCTAssertEqual(sut?.cachedButtonReferences.count, 2, "Cache should contain two buttons.") + } +} diff --git a/Gemfile b/Gemfile index eab52f7235a5f..cc0c3841c898d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,10 @@ source 'https://rubygems.org' -gem 'danger', :git => 'git@github.com:danger/danger.git', :branch => 'master' -gem 'danger-swiftlint' \ No newline at end of file +gem 'fastlane' +gem 'base64' + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) + +# gem 'danger', :git => 'git@github.com:danger/danger.git', :branch => 'master' +# gem 'danger-swiftlint' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000000..e303d739f88fb --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,253 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + artifactory (3.0.17) + atomos (0.1.3) + aws-eventstream (1.3.2) + aws-partitions (1.1072.0) + aws-sdk-core (3.220.2) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.99.0) + aws-sdk-core (~> 3, >= 3.216.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.182.0) + aws-sdk-core (~> 3, >= 3.216.0) + aws-sdk-kms (~> 1) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.11.0) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + base64 (0.3.0) + claide (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander (4.6.0) + highline (~> 2.0.0) + declarative (0.0.20) + digest-crc (0.7.0) + rake (>= 12.0.0, < 14.0.0) + domain_name (0.6.20240107) + dotenv (2.8.1) + emoji_regex (3.2.3) + excon (0.112.0) + faraday (1.10.4) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) + http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.1) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.1.1) + multipart-post (~> 2.0) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.1) + faraday (~> 1.0) + fastimage (2.4.0) + fastlane (2.227.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 (~> 1.2) + 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) + fastlane-sirp (>= 1.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) + 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, < 1.0.0) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.5) + 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.4.0) + xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) + fastlane-plugin-browserstack (0.3.4) + rest-client (~> 2.0, >= 2.0.2) + fastlane-plugin-firebase_app_distribution (0.10.1) + google-apis-firebaseappdistribution_v1 (~> 0.3.0) + google-apis-firebaseappdistribution_v1alpha (~> 0.2.0) + fastlane-plugin-xcconfig (2.1.0) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + gh_inspector (1.1.3) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.a) + rexml + google-apis-firebaseappdistribution_v1 (0.3.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-firebaseappdistribution_v1alpha (0.2.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-iamcredentials_v1 (0.17.0) + 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.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.8.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.5.0) + google-cloud-storage (1.47.0) + addressable (~> 2.8) + digest-crc (~> 0.4) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) + mini_mime (~> 1.0) + googleauth (1.8.1) + faraday (>= 0.17.3, < 3.a) + jwt (>= 1.4, < 3.0) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-accept (1.7.0) + http-cookie (1.0.8) + domain_name (~> 0.5) + httpclient (2.9.0) + mutex_m + jmespath (1.6.2) + json (2.10.2) + jwt (2.10.2) + base64 + logger (1.7.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0819) + mini_magick (4.13.2) + mini_mime (1.1.5) + multi_json (1.17.0) + multipart-post (2.4.1) + mutex_m (0.3.0) + nanaimo (0.4.0) + naturally (2.2.1) + netrc (0.11.0) + nkf (0.2.0) + optparse (0.6.0) + os (1.1.4) + plist (3.7.2) + public_suffix (6.0.2) + rake (13.2.1) + representable (3.2.0) + declarative (< 0.1.0) + trailblazer-option (>= 0.1.1, < 0.2.0) + uber (< 0.2.0) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + retriable (3.1.2) + rexml (3.4.1) + rouge (3.28.0) + ruby2_keywords (0.0.5) + rubyzip (2.4.1) + security (0.1.5) + signet (0.20.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.10) + CFPropertyList + naturally + sysrandom (1.0.5) + terminal-notifier (2.0.0) + 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.2) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + uber (0.1.0) + unicode-display_width (2.6.0) + word_wrap (1.0.0) + xcodeproj (1.27.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.0) + rouge (~> 3.28.0) + xcpretty-travis-formatter (1.0.1) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + arm64-darwin-22 + universal-darwin-23 + x86_64-darwin-20 + x86_64-linux + +DEPENDENCIES + base64 + fastlane + fastlane-plugin-browserstack + fastlane-plugin-firebase_app_distribution + fastlane-plugin-xcconfig + +BUNDLED WITH + 2.3.4 diff --git a/PULL_REQUEST_TEMPLATE b/PULL_REQUEST_TEMPLATE index 6d4edd9f60d15..d438916ce8265 100644 --- a/PULL_REQUEST_TEMPLATE +++ b/PULL_REQUEST_TEMPLATE @@ -1,16 +1,23 @@ -## :scroll: Tickets -[Jira ticket](https://mozilla-hub.atlassian.net/browse/FXIOS-TODO) -[Github issue](https://github.com/mozilla-mobile/firefox-ios/issues/TODO) + -## :bulb: Description - +[MOB-####] -## :pencil: Checklist -You have to check all boxes before merging -- [ ] Filled in the above information (tickets numbers and description of your work) -- [ ] Updated the PR name to follow our [PR naming guidelines](https://github.com/mozilla-mobile/firefox-ios/wiki/Pull-Request-Naming-Guide) -- [ ] Wrote unit tests and/or ensured the tests suite is passing -- [ ] When working on UI, I checked and implemented accessibility (minimum Dynamic Text and VoiceOver) -- [ ] If needed, I updated documentation / comments for complex code and public methods -- [ ] If needed, added a backport comment (example `@Mergifyio backport release/v120`) +## Context +## Approach + +## Other + +## Before merging + +### Checklist + +- [ ] I performed some relevant testing on a real device and/or simulator for both iPhone and iPad +- [ ] I wrote Unit Tests that confirm the expected behaviour +- [ ] I updated only the [english localization source files](Client/Ecosia/L10N/es.lproj) if needed +- [ ] I added the `// Ecosia:` helper comments where needed +- [ ] I made sure that any change to the Analytics events included in PR won't alter current analytics (e.g. new users, upgrading users) +- [ ] I included documentation updates to the coding standards or Confluence doc, when needed \ No newline at end of file diff --git a/Package.resolved b/Package.resolved index a04d5934d0b7f..4fd05d0aa70dd 100644 --- a/Package.resolved +++ b/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nerdishbynature/octokit.swift", "state" : { - "revision" : "f762f1566f7cd0e683b9329f169c28ab6ef993cc", - "version" : "0.12.0" + "revision" : "44192458beb89180c3a3a1245d904aacac8be0ae", + "version" : "0.13.0" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nerdishbynature/RequestKit.git", "state" : { - "revision" : "8b0258ea2a4345cbcac90509b764faacea12efb0", - "version" : "3.2.1" + "revision" : "e4d905fed938807e36d87f28375f88b7c1c26840", + "version" : "3.3.0" } }, { @@ -41,8 +41,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/danger/swift.git", "state" : { - "revision" : "21b074b1292286c9e90688fffa33d64d492f0520", - "version" : "3.16.0" + "revision" : "e43a19617fc905ef303b1af2f0449b99fc6ea0f2", + "version" : "3.20.2" + } + }, + { + "identity" : "swiftformat", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nicklockwood/SwiftFormat", + "state" : { + "revision" : "c4d0bc2ec8aa41d4fe622771935247d74ff2e718", + "version" : "0.55.1" } }, { @@ -56,4 +65,4 @@ } ], "version" : 2 -} +} \ No newline at end of file diff --git a/Package.swift b/Package.swift index 7e3ebf04013e3..aa0382eb50dde 100644 --- a/Package.swift +++ b/Package.swift @@ -9,7 +9,7 @@ let package = Package( .library(name: "DangerDeps", type: .dynamic, targets: ["DangerDependencies"]), // dev ], dependencies: [ - .package(url: "https://github.com/danger/swift.git", exact: "3.16.0"), // dev + .package(url: "https://github.com/danger/swift.git", exact: "3.20.2"), // dev .package(url: "https://github.com/f-meloni/danger-swift-coverage", exact: "1.2.1") // dev ], targets: [ diff --git a/PrivacyInfo.xcprivacy b/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000000..52cd65f527491 --- /dev/null +++ b/PrivacyInfo.xcprivacy @@ -0,0 +1,26 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + 1C8F.1 + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + + diff --git a/README.md b/README.md index 9b5590994d504..e6b3fad20a392 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Head to our Ecosia's [README](firefox-ios/Ecosia/Ecosia.docc/Ecosia.md) to know more. + # Firefox for iOS and Focus iOS Download [Firefox iOS](https://apps.apple.com/app/firefox-web-browser/id989804926) and [Focus iOS](https://itunes.apple.com/app/id1055677337) on the App Store. diff --git a/SampleBrowser/SampleBrowser/Assets.xcassets/syncLarge.imageset/Contents.json b/SampleBrowser/SampleBrowser/Assets.xcassets/syncLarge.imageset/Contents.json index e46920ee78ccd..1e3c997a91d6c 100644 --- a/SampleBrowser/SampleBrowser/Assets.xcassets/syncLarge.imageset/Contents.json +++ b/SampleBrowser/SampleBrowser/Assets.xcassets/syncLarge.imageset/Contents.json @@ -10,6 +10,7 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } } diff --git a/bitrise.yml b/bitrise.yml index 96039f261eb79..5d56a98b6eb75 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -46,16 +46,6 @@ workflows: run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}' - restore-spm-cache@1: is_always_run: true - - git::https://github.com/DamienBitrise/bitrise-test-plan-sharder.git@master: - title: Bitrise Test Plan Sharder - inputs: - - xcode_project: Client.xcodeproj - - target: Client - - shards: '4' - - scheme: Fennec - - debug_mode: 'true' - - test_path: '' - - file_type: ".swift" - script@1.1: title: Build for Testing inputs: @@ -65,7 +55,7 @@ workflows: mkdir DerivedData xcodebuild -resolvePackageDependencies -onlyUsePackageVersionsFromResolvedFile - xcodebuild "-project" "/Users/vagrant/git/firefox-ios/Client.xcodeproj" "-scheme" "Fennec" -configuration "Fennec_Testing" "CODE_SIGNING_ALLOWED=NO" -sdk "iphonesimulator" "-destination" "platform=iOS Simulator,name=iPhone 15,OS=17.5" "COMPILER_INDEX_STORE_ENABLE=NO" "build-for-testing" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO" -derivedDataPath "/Users/vagrant/git/DerivedData" | xcpretty | tee xcodebuild_fennec.log + xcodebuild "-project" "/Users/vagrant/git/firefox-ios/Client.xcodeproj" "-scheme" "Fennec" -configuration "Fennec_Testing" "CODE_SIGNING_ALLOWED=NO" -sdk "iphonesimulator" "-destination" "platform=iOS Simulator,name=iPhone 16,OS=18.2" "COMPILER_INDEX_STORE_ENABLE=NO" "build-for-testing" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO" -derivedDataPath "/Users/vagrant/git/DerivedData" | xcpretty | tee xcodebuild_fennec.log ls /Users/vagrant/git/DerivedData/Build/Products ls /Users/vagrant/git/DerivedData @@ -73,9 +63,9 @@ workflows: run_if: '{{getenv "RUN_UI_TESTS" | eq "Run_UI_Tests"}}' timeout: 600 inputs: - - destination: platform=iOS Simulator,name=iPhone 15,OS=17.5 + - destination: platform=iOS Simulator,name=iPhone 16,OS=18.2 - xcodebuild_options: '"COMPILER_INDEX_STORE_ENABLE=NO" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO"' - - xctestrun: "/Users/vagrant/git/DerivedData/Build/Products/Fennec_UnitTest_iphonesimulator17.5-arm64.xctestrun" + - xctestrun: "/Users/vagrant/git/DerivedData/Build/Products/Fennec_UnitTest_iphonesimulator18.2-arm64.xctestrun" - maximum_test_repetitions: 2 - save-spm-cache@1: is_always_run: true @@ -215,10 +205,10 @@ workflows: run_if: '{{getenv "RUN_UI_TESTS" | eq "Run_UI_Tests"}}' timeout: 1200 inputs: - - destination: platform=iOS Simulator,name=iPhone 15,OS=17.5 + - destination: platform=iOS Simulator,name=iPhone 16,OS=18.2 - xcodebuild_options: '"-derivedDataPath" "/Users/vagrant/git/DerivedData" "COMPILER_INDEX_STORE_ENABLE=NO" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO"' - - xctestrun: "Users/vagrant/git/DerivedData/Build/Products/Fennec_Smoketest${SHARD}_iphonesimulator17.5-arm64.xctestrun" - - deploy-to-bitrise-io@2.1.1: + - xctestrun: "Users/vagrant/git/DerivedData/Build/Products/Fennec_Smoketest${SHARD}_iphonesimulator18.2-arm64.xctestrun" + - deploy-to-bitrise-io@2.9.2: run_if: '{{getenv "RUN_UI_TESTS" | eq "Run_UI_Tests"}}' inputs: - pipeline_intermediate_files: '$BITRISE_XCRESULT_PATH:BITRISE_UITESTS_${SHARD}_XCRESULT_PATH' @@ -743,13 +733,13 @@ workflows: inputs: - xcodebuild_options: CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - scheme: Fennec - - simulator_device: iPhone 15 + - simulator_device: iPhone 16 - xcode-test@4.5: run_if: '{{getenv "NEW_XCODE_VERSION" | eq "New_Version_Found" | or (getenv "RUN_ALL_STEPS" | eq "Run_All_Steps")}}' inputs: - scheme: Fennec - - destination: platform=iOS Simulator,name=iPhone 15,OS=17.5 - - deploy-to-bitrise-io@2.2: {} + - destination: platform=iOS Simulator,name=iPhone 16,OS=18.2 + - deploy-to-bitrise-io@2.9.2: {} - cache-push@2.4: {} - slack@3.1: run_if: '{{getenv "NEW_XCODE_VERSION" | eq "New_Version_Found" | or (getenv "RUN_ALL_STEPS" | eq "Run_All_Steps")}}' @@ -761,7 +751,7 @@ workflows: description: This Workflow is to build the app using latest xcode available in Bitrise meta: bitrise.io: - stack: osx-xcode-16.0.x + stack: osx-xcode-16.2.x-edge machine_type_id: g2-m1.8core L10nBuild: steps: @@ -1213,7 +1203,7 @@ workflows: inputs: - project_path: firefox-ios/Client.xcodeproj - scheme: Fennec - - destination: platform=iOS Simulator,name=iPhone 15,OS=17.5 + - destination: platform=iOS Simulator,name=iPhone 16,OS=18.2 - test_plan: PerformanceTestPlan - script@1.1: is_always_run: true @@ -1602,14 +1592,14 @@ workflows: echo "-- build-for-testing --" mkdir DerivedData #xcodebuild -resolvePackageDependencies -onlyUsePackageVersionsFromResolvedFile - xcodebuild "-project" "/Users/vagrant/git/focus-ios/Blockzilla.xcodeproj" "-scheme" "Focus" -configuration "FocusDebug" "CODE_SIGNING_ALLOWED=NO" "-destination" "platform=iOS Simulator,name=iPhone 15,OS=17.5" "COMPILER_INDEX_STORE_ENABLE=NO" "build-for-testing" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO" -derivedDataPath "/Users/vagrant/git/DerivedData" | xcpretty | tee xcodebuild_test.log + xcodebuild "-project" "/Users/vagrant/git/focus-ios/Blockzilla.xcodeproj" "-scheme" "Focus" -configuration "FocusDebug" "CODE_SIGNING_ALLOWED=NO" "-destination" "platform=iOS Simulator,name=iPhone 16,OS=18.2" "COMPILER_INDEX_STORE_ENABLE=NO" "build-for-testing" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO" -derivedDataPath "/Users/vagrant/git/DerivedData" | xcpretty | tee xcodebuild_test.log - script@1.1: title: Build for Testing Klar inputs: - content: | set -euxo pipefail echo "-- build-for-testing --" - xcodebuild "-project" "/Users/vagrant/git/focus-ios/Blockzilla.xcodeproj" "-scheme" "Klar" -configuration "KlarDebug" "CODE_SIGNING_ALLOWED=NO" "-destination" "platform=iOS Simulator,name=iPhone 15,OS=17.5" "COMPILER_INDEX_STORE_ENABLE=NO" "build-for-testing" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO" -derivedDataPath "/Users/vagrant/git/DerivedData" | xcpretty | tee xcodebuild_test.log + xcodebuild "-project" "/Users/vagrant/git/focus-ios/Blockzilla.xcodeproj" "-scheme" "Klar" -configuration "KlarDebug" "CODE_SIGNING_ALLOWED=NO" "-destination" "platform=iOS Simulator,name=iPhone 16,OS=18.2" "COMPILER_INDEX_STORE_ENABLE=NO" "build-for-testing" "CODE_SIGN_IDENTITY=" "CODE_SIGNING_REQUIRED=NO" "CODE_SIGNING_ALLOWED=NO" -derivedDataPath "/Users/vagrant/git/DerivedData" | xcpretty | tee xcodebuild_test.log - script@1.1: title: Compress Derived Data is_always_run: true @@ -1676,7 +1666,7 @@ workflows: mv ./Users/vagrant/git/* . ls -la echo "-- test-without-building --" - xcodebuild -resultBundlePath "xcodebuild.xcresult" -derivedDataPath "/Users/vagrant/git/DerivedData" -destination "platform=iOS Simulator,name=iPhone 15,OS=17.5" test-without-building -xctestrun "/Users/vagrant/git/DerivedData/Build/Products/Focus_SmokeTest_iphonesimulator17.5-arm64.xctestrun" + xcodebuild -resultBundlePath "xcodebuild.xcresult" -derivedDataPath "/Users/vagrant/git/DerivedData" -destination "platform=iOS Simulator,name=iPhone 16,OS=18.2" test-without-building -xctestrun "/Users/vagrant/git/DerivedData/Build/Products/Focus_SmokeTest_iphonesimulator18.2-arm64.xctestrun" - custom-test-results-export@0: inputs: - search_pattern: "$BITRISE_SOURCE_DIR/xcodebuild.xcresult" @@ -1706,7 +1696,7 @@ workflows: mv ./Users/vagrant/git/* . ls -la echo "-- test-without-building --" - xcodebuild -resultBundlePath "xcodebuild.xcresult" -derivedDataPath "/Users/vagrant/git/DerivedData" -destination "platform=iOS Simulator,name=iPhone 15,OS=17.5" test-without-building -xctestrun "/Users/vagrant/git/DerivedData/Build/Products/Klar_UnitTests_iphonesimulator17.5-arm64.xctestrun" + xcodebuild -resultBundlePath "xcodebuild.xcresult" -derivedDataPath "/Users/vagrant/git/DerivedData" -destination "platform=iOS Simulator,name=iPhone 16,OS=18.2" test-without-building -xctestrun "/Users/vagrant/git/DerivedData/Build/Products/Klar_UnitTests_iphonesimulator18.2-arm64.xctestrun" - custom-test-results-export@0: inputs: - search_pattern: "$BITRISE_SOURCE_DIR/xcodebuild.xcresult" @@ -1734,7 +1724,7 @@ workflows: mv ./Users/vagrant/git/* . ls -la echo "-- test-without-building --" - xcodebuild -resultBundlePath "xcodebuild.xcresult" -derivedDataPath "/Users/vagrant/git/DerivedData" -destination "platform=iOS Simulator,name=iPhone 15,OS=17.5" test-without-building -xctestrun "/Users/vagrant/git/DerivedData/Build/Products/Focus_UnitTests_iphonesimulator17.5-arm64.xctestrun" + xcodebuild -resultBundlePath "xcodebuild.xcresult" -derivedDataPath "/Users/vagrant/git/DerivedData" -destination "platform=iOS Simulator,name=iPhone 16,OS=18.2" test-without-building -xctestrun "/Users/vagrant/git/DerivedData/Build/Products/Focus_UnitTests_iphonesimulator18.2-arm64.xctestrun" - custom-test-results-export@0: inputs: - search_pattern: "$BITRISE_SOURCE_DIR/xcodebuild.xcresult" @@ -2116,10 +2106,10 @@ app: BITRISE_NIGHTLY_VERSION: '9000' - opts: is_expand: false - BITRISE_RELEASE_VERSION: '133.0' + BITRISE_RELEASE_VERSION: '133.4' - opts: is_expand: false - BITRISE_BETA_VERSION: '133.0' + BITRISE_BETA_VERSION: '133.4' trigger_map: - push_branch: main @@ -2137,5 +2127,5 @@ trigger_map: meta: bitrise.io: - stack: osx-xcode-15.4.x + stack: osx-xcode-16.2.x-edge machine_type_id: g2-m1.8core diff --git a/bootstrap.sh b/bootstrap.sh index 1760055e10e93..45bde9bf23e94 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -49,3 +49,15 @@ chmod +x .git/hooks/* # Run and update content blocker ./content_blocker_update.sh + +# Ecosia: Create Staging.xcconfig if not existing +file_path="firefox-ios/Client/Configuration/Staging.xcconfig" + +# Check if the file exists +if [ ! -f "$file_path" ]; then + # If the file doesn't exist, create it using the touch command + touch "$file_path" + echo "File $file_path created." +else + echo "File $file_path already exists." +fi \ No newline at end of file diff --git a/check_marketing_version.sh b/check_marketing_version.sh new file mode 100644 index 0000000000000..bbc37e4206dc0 --- /dev/null +++ b/check_marketing_version.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Check if MARKETING_VERSION has changed +# The cut -d ' ' -f3 takes the output from grep command as input. +# For example, let's assume that the Common.xcconfig file contains the following line: +# MARKETING_VERSION = 100.2.44 +# grep will return -> MARKETING_VERSION = 100.2.44 +# The cut command will then extract the third field from the input, using a space (' ') as the delimiter. +# Output: 100.2.44 + +#!/bin/bash + +# Get the current branch's MARKETING_VERSION +CURRENT_VERSION=$(grep 'MARKETING_VERSION' firefox-ios/Client/Configuration/Common.xcconfig | cut -d ' ' -f3) + +if [ -n "$CIRCLECI" ]; then + # CircleCI: Compare against the previous commit on the same branch + echo "Running on CircleCI, checking against $CIRCLE_BRANCH~1" + + # Check if there is a previous commit available + if git rev-parse "$CIRCLE_BRANCH~1" >/dev/null 2>&1; then + OLD_VERSION=$(git show "$CIRCLE_BRANCH~1:firefox-ios/Client/Configuration/Common.xcconfig" | grep 'MARKETING_VERSION' | cut -d ' ' -f3) + else + echo "No previous commit found on $CIRCLE_BRANCH. Assuming the current version." + OLD_VERSION=$CURRENT_VERSION + fi + +elif [ -n "$GITHUB_ACTIONS" ]; then + # GitHub Actions: Compare against the main branch + echo "Running on GitHub Actions, checking against the main branch" + + # Fetch the main branch + git fetch origin main || { echo "Failed to fetch main branch"; exit 1; } + + # Get the MARKETING_VERSION from the main branch + OLD_VERSION=$(git show origin/main:firefox-ios/Client/Configuration/Common.xcconfig | grep 'MARKETING_VERSION' | cut -d ' ' -f3) + if [ $? -ne 0 ]; then + echo "Failed to retrieve MARKETING_VERSION from main branch" + exit 1 + fi +else + echo "Not running in a CI environment. Exiting..." + exit 0 +fi + +# Compare versions +if [ "$CURRENT_VERSION" = "$OLD_VERSION" ]; then + echo "MARKETING_VERSION has not changed. Exiting..." + + # Detect CI environment and exit appropriately + if [ -n "$CIRCLECI" ]; then + circleci-agent step halt + elif [ -n "$GITHUB_ACTIONS" ]; then + echo "skipnext=true" >> $GITHUB_OUTPUT + else + exit 0 + fi +else + echo "MARKETING_VERSION has changed from $OLD_VERSION to $CURRENT_VERSION" + exit 0 +fi \ No newline at end of file diff --git a/checkout.sh b/checkout.sh index 96c3851824481..e9905e3a2fb39 100755 --- a/checkout.sh +++ b/checkout.sh @@ -18,4 +18,6 @@ curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/mozilla/ cd .. # Make sure we are at the root of the repo rm -rf shavar-prod-lists && git clone https://github.com/mozilla-services/shavar-prod-lists.git && git -C shavar-prod-lists checkout $SHAVAR_COMMIT_HASH -(cd BrowserKit && swift run) +cd BrowserKit +swift run || true +swift run diff --git a/content_blocker_update.sh b/content_blocker_update.sh index 00b3f8b55c44b..84ffc1ecf27ad 100755 --- a/content_blocker_update.sh +++ b/content_blocker_update.sh @@ -1,4 +1,5 @@ #!/bin/sh +set -e # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -14,4 +15,6 @@ npm run build # Clone shavar prod list rm -rf shavar-prod-lists && git clone https://github.com/mozilla-services/shavar-prod-lists.git && git -C shavar-prod-lists checkout $SHAVAR_COMMIT_HASH -(cd BrowserKit && swift run) +cd BrowserKit +swift run || true +swift run diff --git a/docs/CHANGES_SUMMARY.md b/docs/CHANGES_SUMMARY.md new file mode 100644 index 0000000000000..a0e5eb2d2e395 --- /dev/null +++ b/docs/CHANGES_SUMMARY.md @@ -0,0 +1,259 @@ +# Ecosia iOS Browser - Changes Summary (Last 9 Months) + +> **Date Range:** May 7, 2025 – February 5, 2026 +> **Repository:** [ecosia/ios-browser](https://github.com/ecosia/ios-browser) (Fork of firefox-ios) +> **Total Commits:** 67 +> **Total Unique Files Changed:** 621 + +This document summarizes the files and folders that were modified in the Ecosia iOS Browser over the last 9 months. As a fork of firefox-ios, this project customizes the user experience while inheriting the core browser functionality from Mozilla's Firefox for iOS. + +--- + +## Overview by Top-Level Folders + +| Folder | Files Changed | +|--------|--------------| +| `firefox-ios/` | 577 | +| `fastlane/` | 18 | +| `.github/` | 7 | +| `BrowserKit/` | 6 | +| `.circleci/` | 3 | +| `docs/` | 2 | +| Root config files | 8 | + +--- + +## 📊 Detailed Folder Analysis + +### firefox-ios/ Subfolder Breakdown + +| Subfolder | Files Changed | Description | +|-----------|---------------|-------------| +| `Client/` | 344 | Main app code including Ecosia customizations | +| `Ecosia/` | 159 | Core Ecosia module (shared library code) | +| `EcosiaTests/` | 52 | Ecosia-specific test coverage | +| `firefox-ios-tests/` | 11 | Firefox test modifications | +| `Client.xcodeproj/` | 5 | Project configuration | +| `WidgetKit/` | 3 | Widget customizations | +| `Shared/` | 2 | Shared utilities | +| `Storage/` | 1 | Storage modifications | + +### firefox-ios/Client/ Subfolder Breakdown + +| Subfolder | Files | Status | +|-----------|-------|--------| +| `Assets/` | 202 | ✅ Modified (App icons, UI assets) | +| `Ecosia/` | 88 | ✅ Modified (Ecosia integration layer) | +| `Frontend/` | 37 | ✅ Modified (Browser, Home, Settings) | +| `Configuration/` | 9 | ✅ Modified (Build configs) | +| `Coordinators/` | 3 | ✅ Modified (Navigation) | +| `TabManagement/` | 3 | ✅ Modified (Tab handling) | +| `Application/` | 1 | ✅ Modified (AppDelegate) | +| `ContentBlocker/` | 0 | ❌ Untouched | +| `Entitlements/` | 0 | ❌ Untouched | +| `Experiments/` | 0 | ❌ Untouched | +| `Extensions/` | 0 | ❌ Untouched | +| `FeatureFlags/` | 0 | ❌ Untouched | +| `Helpers/` | 0 | ❌ Untouched | +| `Nimbus/` | 0 | ❌ Untouched | +| `Protocols/` | 0 | ❌ Untouched | +| `Redux/` | 0 | ❌ Untouched | +| `Telemetry/` | 0 | ❌ Untouched | +| `Utils/` | 0 | ❌ Untouched | + +### firefox-ios/Client/Frontend/ Breakdown (37 files modified) + +| Subfolder | Files | Key Changes | +|-----------|-------|-------------| +| `Browser/` | 15 | BrowserViewController, MainMenuActionHelper, TabDisplayManager, Search | +| `Home/` | 9 | HomepageViewModel, LegacyHomepageViewController, LogoHeader | +| `Settings/` | 7 | AppSettingsTableViewController, Clearables, ThemeSettings | +| `Library/` | 3 | LegacyBookmarksPanel, LibraryViewController | +| `Components/` | 1 | ContentContainer | +| `Theme/` | 2 | photon-colors, ThemedTableViewCells | + +### firefox-ios/Client/Ecosia/ Breakdown (88 files) + +| Subfolder | Files | Purpose | +|-----------|-------|---------| +| `UI/` | 62 | NTP components, Assets, Onboarding, Theme | +| `Extensions/` | 10 | AppSettings, BrowserViewController, Tab extensions | +| `Account/` | 6 | Auth flow, Tab management for invisible auth | +| `Settings/` | 3 | EcosiaSettings, EcosiaDebugSettings | +| `Frontend/` | 2 | Home section customizations | +| `Experiments/` | 1 | SeedCounterNTPExperiment | +| `Network/` | 1 | Network customizations | +| `Bookmarks/` | 1 | Bookmark handling | +| `PersistedGenerated/` | 1 | Generated persistence | +| `RecoveredFromUpgrade/` | 1 | Upgrade recovery | + +### firefox-ios/Ecosia/ Breakdown (159 files - Core Module) + +| Subfolder | Files | Purpose | +|-----------|-------|---------| +| `UI/` | 103 | Account views, NTP components, Common assets | +| `Core/` | 49 | Services, Cookie handlers, Feature management | +| `Account/Auth/` | 24 | Auth0, Authentication service, Credentials | +| `L10N/` | 7 | Localization strings (de, en, es, fr, it, nl) | +| `Analytics/` | 4 | Analytics integration | +| `Experiments/` | 3 | Feature flags (AI Search, Default Browser) | +| `Braze/` | 2 | Push notification integration | +| `Helpers/` | 6 | Utility functions | +| `Extensions/` | 4 | Swift extensions | +| `Entitlements/` | 2 | App capabilities | + +#### firefox-ios/Ecosia/UI/ Details (103 files) + +| Subfolder | Files | Description | +|-----------|-------|-------------| +| `Common.xcassets/` | 43 | Account icons, AI assets, general images | +| `Account/` | 25 | Progress avatar, signed in/out views, seed view | +| `NTP/Header/` | 3 | AI Search button, Account nav button | +| `Settings/` | 3 | Default browser settings | +| `FeedbackView/` | 8 | User feedback UI | +| `DesignSystem/` | 6 | Colors, typography | +| `Components/` | 2 | Reusable UI components | +| `Toast/` | 1 | Toast notifications | + +#### firefox-ios/Ecosia/Core/ Details (49 files) + +| Subfolder | Files | Description | +|-----------|-------|-------------| +| `MMP/` | 12 | Mobile measurement partner integration | +| `FeatureManagement/` | 11 | Unleash feature flags | +| `Cookie/` | 8 | AI Overviews, Auth, Unleash cookie handlers | +| `HTTPClient/` | 6 | Network layer | +| `Accounts/Service/` | 5 | Account balance, visits API | +| `Referrals/` | 5 | Referral system | +| `Bookmarks/` | 5 | Bookmark syncing | +| `Statistics/` | 4 | Impact statistics | +| `Environment/` | 3 | Environment configuration | +| `Navigation/` | 2 | URL interception, auth redirects | +| `News/` | 2 | Ecosia news integration | +| `Pages/` | 2 | Page utilities | +| `Tabs/` | 2 | Tab utilities | + +--- + +### firefox-ios/EcosiaTests/ Breakdown (52 files) + +| Subfolder | Files | Description | +|-----------|-------|-------------| +| `Core/` | 14 | Unleash, Cookie handlers, Environment tests | +| `SnapshotTests/` | 13 | Visual regression tests | +| `Account/Auth/` | 11 | Authentication flow tests | +| `UI/Account/` | 10 | Account UI component tests | +| `Mocks/` | 8 | Mock objects for testing | +| `ClimateImpactCounter/` | 2 | Seed counter tests | +| `Analytics/` | 2 | Analytics event tests | +| `IntegrationTests/` | 1 | Integration test suite | + +### BrowserKit/ Breakdown (6 files modified) + +| Subfolder | Files | Status | +|-----------|-------|--------| +| `Sources/Common/Theming/` | 1 | ✅ Modified (EcosiaThemeColourPalette) | +| `Sources/ComponentLibrary/Buttons/` | 1 | ✅ Modified (ResizableButton) | +| `Sources/ComponentLibrary/Headers/` | 2 | ✅ Modified (HeaderView, NavigationHeaderView) | +| `Package.resolved` | 1 | ✅ Modified (dependency updates) | +| `Sources/ContentBlockingGenerator/` | 0 | ❌ Untouched | +| `Sources/ExecutableContentBlockingGenerator/` | 0 | ❌ Untouched | +| `Sources/MenuKit/` | 0 | ❌ Untouched | +| `Sources/Redux/` | 0 | ❌ Untouched | +| `Sources/SiteImageView/` | 0 | ❌ Untouched | +| `Sources/TabDataStore/` | 0 | ❌ Untouched | +| `Sources/ToolbarKit/` | 0 | ❌ Untouched | +| `Sources/WebEngine/` | 0 | ❌ Untouched | +| `Tests/*` | 0 | ❌ Untouched (all test directories) | + +--- + +## 🗂️ Untouched Firefox Folders + +The following Firefox-inherited folders were **not modified** during this period: + +### firefox-ios/Client/ - Untouched Subfolders +- `ContentBlocker/` - Content blocking rules +- `Entitlements/` - App entitlements +- `Experiments/` - Firefox experiments (Ecosia uses own) +- `Extensions/` - App extensions +- `FeatureFlags/` - Firefox feature flags +- `Helpers/` - General helper utilities +- `Nimbus/` - Firefox Nimbus integration +- `Protocols/` - Protocol definitions +- `Redux/` - State management +- `Telemetry/` - Firefox telemetry +- `Utils/` - Utility functions + +### Other Untouched firefox-ios/ Folders +- `Account/` - Firefox account (Ecosia has own) +- `CredentialProvider/` - Password autofill extension +- `Extensions/` - Share, Notification extensions +- `FxA/` - Firefox Accounts +- `Providers/` - Data providers +- `Push/` - Push notifications (Firefox) +- `RustFxA/` - Rust Firefox Accounts +- `Storage/` - Local storage +- `Sync/` - Firefox Sync +- `ThirdParty/` - Third-party libraries + +--- + +## 🚀 CI/CD & Configuration + +### .circleci/ (3 files) +- `config.yml` - CircleCI configuration updates (M4 Pro machine, test configurations) + +### .github/workflows/ (7 files) +- `merge_tests.yml` - PR merge test workflow +- `snapshot_tests.yml` - Visual regression tests +- `swift_lint.yml` - SwiftLint checks +- `upload_release_notes_to_appstore.yml` - App Store automation +- `pr_agent.yml` (removed) - Qodo AI agent removal + +### fastlane/ (18 files) +- `metadata/[locale]/release_notes.txt` - Localized release notes + - Languages: ar, da, de-DE, en-AU, en-CA, en-GB, en-US, es-ES, es-MX, fr-FR, it, ja, nl-NL + +### Root Configuration +- `.swiftlint.yml` - SwiftLint configuration +- `swiftlint_baseline.json` - SwiftLint baseline (new) +- `.tx/release_notes_integration_config.yml` - Transifex configuration +- `.gitignore` - Updated ignore patterns + +--- + +## 🔑 Key Features Implemented + +Based on commit messages and file changes: + +1. **Accounts Feature Integration** - Complete user account system with Auth0 +2. **AI Search Access Points** - AI-powered search entry points and branding +3. **AI Brand Update** - Unified AI feature visual identity +4. **Global Counter Adjustments** - Seed/impact counter refinements +5. **Braze Push Notifications** - Push notification support +6. **User Feedback System** - Dedicated feedback section +7. **Snowplow Analytics** - Enhanced analytics integration +8. **App Store Rating Prompts** - Improved rating prompt flow +9. **SwiftUI Previews** - Developer experience improvements +10. **Critical Health Checks** - CI monitoring improvements + +--- + +## Version History (This Period) + +| Version | Date | +|---------|------| +| 11.1.1 | May 2025 | +| 11.2.0 | June 2025 | +| 11.2.1 | June 2025 | +| 11.3.0 | July 2025 | +| 11.4.0 | August 2025 | +| 11.4.1 | August 2025 | +| 11.5.0 | September 2025 | +| 11.5.1 | October 2025 | +| 11.6.0 | February 2026 | + +--- + +*This summary was generated on February 5, 2026* diff --git a/docs/decisions/0001-swiftlint-configuration-for-upstream-fork.md b/docs/decisions/0001-swiftlint-configuration-for-upstream-fork.md new file mode 100644 index 0000000000000..448c31316ece9 --- /dev/null +++ b/docs/decisions/0001-swiftlint-configuration-for-upstream-fork.md @@ -0,0 +1,74 @@ +# SwiftLint Configuration for Upstream Firefox Fork + +* Status: accepted +* Deciders: Ecosia iOS Team +* Date: 2026-02-03 + +## Context and Problem Statement + +The Ecosia iOS browser is a fork of Mozilla's Firefox iOS browser. As a fork, we regularly merge upstream changes from Firefox to stay up-to-date with security patches, features, and improvements. This creates a challenge with linting: how do we maintain code quality in our codebase while avoiding conflicts when merging upstream changes? + +## Decision Drivers + +* Need to maintain code quality in Ecosia-specific code +* Need to minimize merge conflicts when incorporating Firefox upstream changes +* Want to use SwiftLint's `--fix` option for automatic code formatting +* Firefox core codebase has existing lint violations that are outside our control + +## Considered Options + +* **Option 1**: Fix all Firefox upstream lint issues in our fork +* **Option 2**: Exclude Firefox core files from linting entirely +* **Option 3**: Lint all files but do not auto-fix Firefox core files (current approach) + +## Decision Outcome + +Chosen option: **Option 3 - Lint all files but do not auto-fix Firefox core files**, because it allows us to maintain visibility of code quality across the entire codebase while avoiding merge conflicts that would arise from auto-fixing upstream code. + +### Positive Consequences + +* Merge conflicts with upstream Firefox are minimized +* Ecosia-specific code can still benefit from `swiftlint --fix` automatic corrections +* Lint warnings in Firefox core files remain visible for awareness +* Easier to stay synchronized with Mozilla's upstream changes + +### Negative Consequences + +* Firefox core files retain existing lint violations +* Currently no automated way to selectively apply `--fix` only to Ecosia team files +* Manual discipline required to not accidentally auto-fix Firefox files + +## Pros and Cons of the Options + +### Option 1: Fix all Firefox upstream lint issues + +* Good, because it would result in a completely clean lint output +* Good, because it enforces consistent code style across the entire codebase +* Bad, because it would cause significant merge conflicts on every upstream merge +* Bad, because it requires ongoing effort to re-fix issues after each upstream sync + +### Option 2: Exclude Firefox core files from linting entirely + +* Good, because it eliminates noise from Firefox lint violations +* Good, because it focuses lint output only on Ecosia code +* Bad, because we lose visibility into code quality issues in files we may need to modify +* Bad, because it could mask potential issues when Ecosia code interacts with Firefox code + +### Option 3: Lint all files but do not auto-fix Firefox core files + +* Good, because it maintains visibility of all lint issues +* Good, because it minimizes merge conflicts with upstream +* Good, because Ecosia code can still be auto-fixed +* Bad, because there is currently no built-in SwiftLint mechanism to selectively apply `--fix` + +## Open Issues + +* **Selective auto-fix**: Currently, there is no straightforward way to tell SwiftLint to `--fix` only files changed by the Ecosia team while leaving Firefox core files untouched. A potential solution would involve: + - Creating a script that identifies Ecosia-owned files (possibly based on file paths or git history) + - Running `swiftlint --fix` only on those specific files + - Integrating this into the CI/CD pipeline or pre-commit hooks + +## Links + +* [SwiftLint Configuration](.swiftlint.yml) - Current SwiftLint rules and exclusions +* [Firefox iOS Repository](https://github.com/mozilla-mobile/firefox-ios) - Upstream repository diff --git a/docs/decisions/README.md b/docs/decisions/README.md new file mode 100644 index 0000000000000..7d980c3a82be2 --- /dev/null +++ b/docs/decisions/README.md @@ -0,0 +1,27 @@ +# Mobile Decision Records (MDR) + +This directory contains Mobile Decision Records (MDR) for the Ecosia iOS Browser project. + +## What is an MDR? + +A Mobile Decision Record is a document that captures an important architectural or technical decision made for the mobile application, along with its context and consequences. We use the [MADR (Markdown Any Decision Records)](https://adr.github.io/madr/) template format. + +## Creating a new MDR + +1. Copy the template from an existing MDR or use the MADR template +2. Name the file using the format: `NNNN-short-title.md` where `NNNN` is the next sequential number +3. Fill in all relevant sections +4. Submit a PR with the new MDR for review + +## MDR Statuses + +* **proposed** - The decision is being discussed +* **accepted** - The decision has been accepted and is in effect +* **deprecated** - The decision is no longer relevant +* **superseded** - The decision has been replaced by a newer decision + +## Index + +| ID | Title | Status | Date | +|----|-------|--------|------| +| [0001](0001-swiftlint-configuration-for-upstream-fork.md) | SwiftLint Configuration for Upstream Firefox Fork | Accepted | 2026-02-03 | diff --git a/ecosify-strings.py b/ecosify-strings.py new file mode 100644 index 0000000000000..99f315b578e1a --- /dev/null +++ b/ecosify-strings.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +import os +import glob +import re + + +def _valid_directory(arg, parser): + if os.path.isdir(arg): + return arg + + parser.error("Directory does not exist: {}".format(arg)) + + +def ecosify_dir(dir_path): + for filename in glob.glob(dir_path + '/**/*.strings', recursive=True): + print(filename) + isStringsFile = filename.endswith(".strings") + isEcosiaFile = filename.__contains__("Ecosia") + if isStringsFile and not isEcosiaFile: + ecosify_translations(filename) + continue + else: + print("File extension not valid {}".format(filename)) + continue + + +def ecosify_translations(file_path): + print("Replacing strings in strings file {}".format(file_path)) + + with open(file_path, 'r') as f: + try: + lines = f.readlines() + except Exception: + print('cannot open:' + file_path) + return + + newlines = [] + brandnames = [ + 'firefoksa', + 'firefoxen', + 'firefoxu', + 'firefoxe', + 'firefoxban', + 'firefoksie', + 'firefox', + 'mozilla' + ] + + for line in lines: + parts = line.split('=') + if len(parts) > 1: + value = parts[1] + + for name in brandnames: + value = re.sub(name, 'Ecosia', value, flags=re.IGNORECASE) + + newlines.append(parts[0] + '=' + value) + elif line.strip().endswith(';'): + # some translation values break lines and are ophaned + # we need to replace values there too + for name in brandnames: + line = re.sub(name, 'Ecosia', line, flags=re.IGNORECASE) + newlines.append(line) + else: + newlines.append(line) + + # writing to file + with open(file_path, 'w') as f: + f.writelines(newlines) + f.close() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description="Replace Firefox and Mozilla strings with Ecosia", + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument('ios_source', + type=lambda arg: _valid_directory(arg, parser), + help="The ios project's src folder") + args = parser.parse_args() + + ecosify_dir(args.ios_source) diff --git a/fastlane/.env b/fastlane/.env new file mode 100644 index 0000000000000..f143ec79f9839 --- /dev/null +++ b/fastlane/.env @@ -0,0 +1 @@ +FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT=9 \ No newline at end of file diff --git a/fastlane/Deliverfile b/fastlane/Deliverfile new file mode 100644 index 0000000000000..3f7caf225926e --- /dev/null +++ b/fastlane/Deliverfile @@ -0,0 +1,17 @@ +# The Deliverfile allows you to store various App Store Connect metadata +# For more information, check out the docs +# https://docs.fastlane.tools/actions/deliver/ + +itc_provider("33YMRSYD2L") +app_identifier("com.ecosia.ecosiaapp") + +skip_app_version_update(true) +skip_binary_upload(true) +skip_screenshots(true) +phased_release(true) + +skip_metadata(false) +submit_for_review(false) +edit_live(false) +use_live_version(false) +run_precheck_before_submit(false) \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 0000000000000..f30aadf97b6b8 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,226 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +require "base64" + +opt_out_usage + +fastlane_version "2.210.0" + +default_platform :ios + +target_name = "Client" +target_folder_path = "firefox-ios/#{target_name}" +project_path = "#{target_folder_path}.xcodeproj" +pbx_project_file = "#{project_path}/project.pbxproj" +testflight_archive_args = "GCC_TREAT_WARNINGS_AS_ERRORS='NO' SWIFT_TREAT_WARNINGS_AS_ERRORS='NO' VERSIONING_SYSTEM='apple-generic' CURRENT_PROJECT_VERSION='0' FUSE_BUILD_SCRIPT_PHASES='NO'" + +if ENV['IS_CIRCLECI'] + import_build_tools( + url: 'git@github.com:mozilla/firefox-ios-build-tools.git', + clone_folder: "../build-tools", + ) + ENV["IPA_PATH"] = "/Users/distiller/project/#{target_name}.ipa" + ENV["DSYM_PATH"] = "/Users/distiller/project/#{target_name}.app.dSYM.zip" +end + +platform :ios do + before_all do + setup_circle_ci + end + + desc "Build Ecosia App" + lane :build_ecosia_app do + match( + app_identifier: ['com.ecosia.ecosiaapp.firefox', 'com.ecosia.ecosiaapp.firefox.WidgetKit', 'com.ecosia.ecosiaapp.firefox.ShareTo'], + type: "adhoc", + readonly: true + ) + increment_build_number( + build_number: ENV["CIRCLE_BUILD_NUM"], + xcodeproj: project_path + ) + gym( + scheme: "Ecosia", + project: project_path, + export_method: "ad-hoc", + configuration: "Development_Firebase", + export_options: {iCloudContainerEnvironment: 'Development'} + ) + end + + desc "Upload to Browserstack AppAutomate" + lane :upload_to_browserstack do + ensure_env_vars( + env_vars: [ + 'IPA_PATH', + 'BROWSERSTACK_USERNAME', + 'BROWSERSTACK_ACCESS_KEY'] + ) + upload_to_browserstack_app_automate( + browserstack_username: ENV["BROWSERSTACK_USERNAME"], + browserstack_access_key: ENV["BROWSERSTACK_ACCESS_KEY"], + file_path: ENV["IPA_PATH"] + ) + + persist_browserstack_id_env_var( + app_id: ENV["BROWSERSTACK_APP_ID"] + ) + end + + desc "Persist Browserstack App ID environment variable in CircleCI" + lane :persist_browserstack_id_env_var do |options| + env_var_name = options[:app_id] + UI.message("Persisting Browserstack App ID: #{env_var_name}") + sh "echo \"export BROWSERSTACK_URL=#{env_var_name}\" >> $BASH_ENV" + end + + desc "Upload to Firebase" + lane :upload_to_firebase do + ensure_env_vars( + env_vars: [ + 'IPA_PATH', + 'DSYM_PATH', + 'FIREBASE_TOKEN', + 'FIREBASE_APP_ID'] + ) + firebase_app_distribution( + app: ENV["FIREBASE_APP_ID"], + firebase_cli_token: ENV["FIREBASE_TOKEN"], + ipa_path: ENV["IPA_PATH"], + release_notes: ENV["CIRCLE_BRANCH"], + groups: "ecosia-internal-testers" + ) + end + + desc "Testflight build for Beta Channel" + lane :testflight_beta do + match( + app_identifier: ['com.ecosia.ecosiaapp.firefox', 'com.ecosia.ecosiaapp.firefox.WidgetKit', 'com.ecosia.ecosiaapp.firefox.ShareTo'], + type: 'appstore', + readonly: true + ) + gym( + scheme: "Ecosia", + export_method: "app-store", + configuration: "Development_TestFlight" + ) + pilot( + skip_waiting_for_build_processing: true, + apple_id: "1541358670", + itc_provider: "33YMRSYD2L" + ) + end + + desc "Creates an API KEY to add as step in other lanes" + private_lane :inject_appstore_connect_api do + + app_store_connect_api_key( + key_id: ENV["FASTLANE_APPSTORE_CONNECT_KEY_ID"], + issuer_id: ENV["FASTLANE_APPSTORE_CONNECT_KEY_ISSUER_ID"], + key_content: Base64.decode64(ENV["FASTLANE_APPSTORE_CONNECT_KEY_CONTENT"]) + ) + + end + + desc "Testflight build for Live Channel" + lane :testflight_live do + + version_number = get_xcconfig_value( + path: "#{target_folder_path}/Configuration/Common.xcconfig", + name: 'MARKETING_VERSION' + ) + + ci_run_build_number = ENV["CIRCLE_BUILD_NUM"] + + udpated_build_number = increment_build_number( + build_number: ci_run_build_number, + xcodeproj: project_path + ) + + match( + app_identifier: ['com.ecosia.ecosiaapp', 'com.ecosia.ecosiaapp.WidgetKit', 'com.ecosia.ecosiaapp.ShareTo'], + type: 'appstore', + readonly: true + ) + + build_app( + scheme: "Ecosia", + project: project_path, + configuration: "Release", + export_method: "app-store", + xcargs: testflight_archive_args, + skip_archive: true + ) + + gym( + scheme: "Ecosia", + project: project_path, + export_method: "app-store", + configuration: "Release", + xcargs: testflight_archive_args + ) + + inject_appstore_connect_api + + pilot( + apple_id: "670881887", + itc_provider: "33YMRSYD2L", + distribute_external: true, + notify_external_testers: true, + changelog: "Our brand new iOS release is now ready to test. It includes many bug fixes and stability improvements. Happy testing! 🌳💚", + groups: "Ecosia Team" + ) + + tag_and_push( + version_number: version_number, + build_number: udpated_build_number + ) + + end + + desc 'Upload release notes to the AppStore' + lane :upload_release_notes do + + inject_appstore_connect_api + + version_number = get_xcconfig_value( + path: "#{target_folder_path}/Configuration/Common.xcconfig", + name: 'MARKETING_VERSION' + ) + + upload_to_app_store( + force: true, # Set to true to skip the verification via HTML preview file + app_version: version_number + ) + + end + + desc 'This lane performs the expected git steps when releasing' + private_lane :tag_and_push do |options| + + version_number = options[:version_number] + build_number = options[:build_number] + + add_git_tag( + tag: "v_#{version_number}_#{build_number}", + force: true + ) + + push_to_git_remote + + end + + desc 'Store the derived data path into a file' + private_lane :save_derived_data_path do |options| + + derived_data_path_to_save = options[:derived_data_path] + derived_data_path_file = "/var/tmp/derived_data_path.txt" + + UI.message("Saving #{derived_data_path_to_save} into #{derived_data_path_file}") + + File.open(derived_data_path_file, 'w') { |file| file.write(options[:derived_data_path]) } + + end + +end \ No newline at end of file diff --git a/fastlane/Matchfile b/fastlane/Matchfile new file mode 100644 index 0000000000000..68909b8bc9631 --- /dev/null +++ b/fastlane/Matchfile @@ -0,0 +1,10 @@ +git_url("git@github.com:ecosia/IosSearchSigning.git") + +type("development") # The default type, can be: appstore, adhoc, enterprise or development + +app_identifier(["com.ecosia.ecosiaapp.firefox", "com.ecosia.ecosiaapp.firefox.WidgetKit", "com.ecosia.ecosiaapp.firefox.ShareTo", "com.ecosia.ecosiaapp", "com.ecosia.ecosiaapp.WidgetKit", "com.ecosia.ecosiaapp.ShareTo"]) +username("apple-dev@ecosia.org") # Your Apple Developer Portal username +git_branch("main") + +# For all available options run `fastlane match --help` +# Remove the # in the beginning of the line to enable the other options diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 0000000000000..b5ffab01f7792 --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,7 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-xcconfig' +gem 'fastlane-plugin-browserstack' +gem 'fastlane-plugin-firebase_app_distribution' diff --git a/fastlane/Scanfile b/fastlane/Scanfile new file mode 100644 index 0000000000000..d7838de555cf9 --- /dev/null +++ b/fastlane/Scanfile @@ -0,0 +1,13 @@ +# This Scanfile is kept for reference but not used directly. +# We're using direct xcodebuild command in perform_unit_tests/action.yml instead. +scheme("Ecosia") +configuration("Testing") +project("firefox-ios/Client.xcodeproj") +device("iPhone 16") +prelaunch_simulator(true) +output_types("junit") +output_directory("test_output/xml") +testplan("UnitTest") +code_coverage(false) +result_bundle(true) +fail_build(false) \ No newline at end of file diff --git a/fastlane/actions/import_build_tools.rb b/fastlane/actions/import_build_tools.rb new file mode 100644 index 0000000000000..348923b690f94 --- /dev/null +++ b/fastlane/actions/import_build_tools.rb @@ -0,0 +1,73 @@ +module Fastlane + module Actions + + class ImportBuildToolsAction < Action + def self.run(params) + # fastlane will take care of reading in the parameter and fetching the environment variable: + directory = params[:clone_folder] + + git_command = "" + if File.directory?(directory) + branch_option = "" + branch_option = "git checkout #{params[:branch]}\n" if params[:branch] != 'HEAD' + git_command = "cd #{directory}\n \ + git checkout master\n \ + git fetch\n \ + #{branch_option} \ + git pull" + else + #import from git into subdir + branch_option = "" + branch_option = "--branch #{params[:branch]}" if params[:branch] != 'HEAD' + + git_command = "git clone '#{params[:url]}' '#{directory}' #{branch_option}" + end + + Actions.sh(git_command) + end + + ##################################################### + # @!group Documentation + ##################################################### + + def self.description + "Downloads an Git repo to a given location" + end + + def self.details + "Downloads a Git repo <:url> to a given location <:clone_folder> and checks out a specific branch if <:branch> is provided" + end + + def self.available_options + [ + FastlaneCore::ConfigItem.new(key: :url, + env_name: "FL_IMPORT_BUILD_TOOLS_URL", # The name of the environment variable + description: "URL of github repository that contains build tools", # a short description of this parameter + verify_block: proc do |value| + raise "No URL for ImportBuildToolsAction given, pass using `url: 'value'`".red unless (value and not value.empty?) + # raise "Couldn't find file at path '#{value}'".red unless File.exist?(value) + end), + FastlaneCore::ConfigItem.new(key: :clone_folder, + env_name: "FL_IMPORT_BUILD_TOOLS_CLONE_FOLDER", # The name of the environment variable + description: "path to import build tools to", # a short description of this parameter + verify_block: proc do |value| + raise "No Clone folder for ImportBuildToolsAction given, pass using `clone_folder: 'path'`".red unless (value and not value.empty?) + # raise "Couldn't find file at path '#{value}'".red unless File.exist?(value) + end), + FastlaneCore::ConfigItem.new(key: :branch, + env_name: "FL_IMPORT_BUILD_TOOLS_BRANCH", + description: "Branch of build tools to import", + default_value: "HEAD") # the default value if the user didn't provide one + ] + end + + def self.authors + ["Mozilla"] + end + + def self.is_supported?(platform) + platform == :ios + end + end + end +end diff --git a/fastlane/metadata/ar-SA/release_notes.txt b/fastlane/metadata/ar-SA/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/ar-SA/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/da/release_notes.txt b/fastlane/metadata/da/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/da/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/de-DE/release_notes.txt b/fastlane/metadata/de-DE/release_notes.txt new file mode 100644 index 0000000000000..b960984bc9431 --- /dev/null +++ b/fastlane/metadata/de-DE/release_notes.txt @@ -0,0 +1 @@ +Wir haben einige Bugs behoben und die Stabilität verbessert. \ No newline at end of file diff --git a/fastlane/metadata/en-AU/release_notes.txt b/fastlane/metadata/en-AU/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/en-AU/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/en-CA/release_notes.txt b/fastlane/metadata/en-CA/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/en-CA/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/en-GB/release_notes.txt b/fastlane/metadata/en-GB/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/en-GB/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/en-US/release_notes.txt b/fastlane/metadata/en-US/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/en-US/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/en_AU/release_notes.txt b/fastlane/metadata/en_AU/release_notes.txt new file mode 100644 index 0000000000000..42a26a2242caa --- /dev/null +++ b/fastlane/metadata/en_AU/release_notes.txt @@ -0,0 +1 @@ +We fixed some bugs and added some stability improvements. \ No newline at end of file diff --git a/fastlane/metadata/es-ES/release_notes.txt b/fastlane/metadata/es-ES/release_notes.txt new file mode 100644 index 0000000000000..204323bc612d6 --- /dev/null +++ b/fastlane/metadata/es-ES/release_notes.txt @@ -0,0 +1 @@ +Hemos solucionado algunos bugs y añadido algunas mejoras de estabilidad. \ No newline at end of file diff --git a/fastlane/metadata/es-MX/release_notes.txt b/fastlane/metadata/es-MX/release_notes.txt new file mode 100644 index 0000000000000..972fbd8bb560b --- /dev/null +++ b/fastlane/metadata/es-MX/release_notes.txt @@ -0,0 +1 @@ +Arreglamos algunos bugs y añadimos algunas mejoras de estabilidad. \ No newline at end of file diff --git a/fastlane/metadata/es_MX/release_notes.txt b/fastlane/metadata/es_MX/release_notes.txt new file mode 100644 index 0000000000000..972fbd8bb560b --- /dev/null +++ b/fastlane/metadata/es_MX/release_notes.txt @@ -0,0 +1 @@ +Arreglamos algunos bugs y añadimos algunas mejoras de estabilidad. \ No newline at end of file diff --git a/fastlane/metadata/fr-FR/release_notes.txt b/fastlane/metadata/fr-FR/release_notes.txt new file mode 100644 index 0000000000000..f5e42ffb1e0fb --- /dev/null +++ b/fastlane/metadata/fr-FR/release_notes.txt @@ -0,0 +1 @@ +Nous avons résolu quelques problèmes et ajouté des améliorations en termes de stabilité. \ No newline at end of file diff --git a/fastlane/metadata/it/release_notes.txt b/fastlane/metadata/it/release_notes.txt new file mode 100644 index 0000000000000..120bcd3a6d8b0 --- /dev/null +++ b/fastlane/metadata/it/release_notes.txt @@ -0,0 +1 @@ +Abbiamo risolto alcuni bug e aggiunto alcuni miglioramenti della stabilità. \ No newline at end of file diff --git a/fastlane/metadata/ja/release_notes.txt b/fastlane/metadata/ja/release_notes.txt new file mode 100644 index 0000000000000..5362bf8421709 --- /dev/null +++ b/fastlane/metadata/ja/release_notes.txt @@ -0,0 +1 @@ +いくつかのバグを修正し、安定性の改善を加えました。 \ No newline at end of file diff --git a/fastlane/metadata/nl-NL/release_notes.txt b/fastlane/metadata/nl-NL/release_notes.txt new file mode 100644 index 0000000000000..d7844922e84a8 --- /dev/null +++ b/fastlane/metadata/nl-NL/release_notes.txt @@ -0,0 +1 @@ +We hebben een aantal bugs verholpen en een aantal stabiliteitsverbeteringen doorgevoerd. \ No newline at end of file diff --git a/fastlane/metadata/review_information/demo_password.txt b/fastlane/metadata/review_information/demo_password.txt new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/fastlane/metadata/review_information/demo_password.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/review_information/demo_user.txt b/fastlane/metadata/review_information/demo_user.txt new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/fastlane/metadata/review_information/demo_user.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/review_information/email_address.txt b/fastlane/metadata/review_information/email_address.txt new file mode 100644 index 0000000000000..1e42e0a52425f --- /dev/null +++ b/fastlane/metadata/review_information/email_address.txt @@ -0,0 +1 @@ +joern.ehmann@ecosia.org diff --git a/fastlane/metadata/review_information/first_name.txt b/fastlane/metadata/review_information/first_name.txt new file mode 100644 index 0000000000000..f43e40ffcb2be --- /dev/null +++ b/fastlane/metadata/review_information/first_name.txt @@ -0,0 +1 @@ +Jörn diff --git a/fastlane/metadata/review_information/last_name.txt b/fastlane/metadata/review_information/last_name.txt new file mode 100644 index 0000000000000..464b1ce0e0489 --- /dev/null +++ b/fastlane/metadata/review_information/last_name.txt @@ -0,0 +1 @@ +Ehmann diff --git a/fastlane/metadata/review_information/notes.txt b/fastlane/metadata/review_information/notes.txt new file mode 100644 index 0000000000000..6ffcb540646a4 --- /dev/null +++ b/fastlane/metadata/review_information/notes.txt @@ -0,0 +1,3 @@ +- Our app does make use of Siri Intents. There is one Shortcut that can be configured to open a new tab. +- We provide iOS14 widgets for both iPad and iPhone. There is a Today Widget for lower iOS versions. They can be added via the usual way. +- The main functionality of our app is browsing the web. So access to camera, photos and microphone is needed when a WKWebView requires access to provide these website's functionalities. diff --git a/fastlane/metadata/review_information/phone_number.txt b/fastlane/metadata/review_information/phone_number.txt new file mode 100644 index 0000000000000..8d2d5cb2f904e --- /dev/null +++ b/fastlane/metadata/review_information/phone_number.txt @@ -0,0 +1 @@ ++491719990726 diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 79b9243ef3251..6a63fd5864c62 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -3,12 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ 03CCC9181AF05E7300DBF30D /* RelativeDatesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CCC9171AF05E7300DBF30D /* RelativeDatesTests.swift */; }; - 0430A545203B372D00FDF76D /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0430A544203B372D00FDF76D /* IntegrationTests.swift */; }; 047F9B2924E1FE1C00CD7DF7 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAD46F3524A1606C001B3967 /* SwiftUI.framework */; }; 047F9B2C24E1FE1C00CD7DF7 /* WidgetKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047F9B2B24E1FE1C00CD7DF7 /* WidgetKit.swift */; }; 047F9B2E24E1FE1F00CD7DF7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 047F9B2D24E1FE1F00CD7DF7 /* Assets.xcassets */; }; @@ -30,18 +29,12 @@ 0AFF7F682C78989000265214 /* CertificatesHeaderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F672C78989000265214 /* CertificatesHeaderItem.swift */; }; 0AFF7F6C2C7C7BBA00265214 /* CertificatesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F692C7C7BB800265214 /* CertificatesCell.swift */; }; 0AFF7F6D2C7C7BBA00265214 /* CertificatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */; }; - 0B11AF022CB412D100AD51D5 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11AF002CB412D100AD51D5 /* Metrics.swift */; }; 0B11AF042CB4130F00AD51D5 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11AF032CB4130F00AD51D5 /* Metrics.swift */; }; - 0B305E1B1E3A98A900BE0767 /* BookmarksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */; }; - 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */; }; + 0B1C58D12CE5019A00F498F0 /* UserAgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B1C58D02CE5019A00F498F0 /* UserAgentTests.swift */; }; 0B3F8C5E2CA4471C00DB5367 /* EditBookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3F8C5D2CA4471C00DB5367 /* EditBookmarkViewModel.swift */; }; 0B54BD191B698B7C004C822C /* SuggestedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B54BD181B698B7C004C822C /* SuggestedSites.swift */; }; - 0B5A93221B1EB4C8004F47A2 /* ReadingListTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5A93211B1EB4C8004F47A2 /* ReadingListTest.swift */; }; - 0B5A93421B1EB572004F47A2 /* readablePage.html in Resources */ = {isa = PBXBuildFile; fileRef = 0B5A93411B1EB572004F47A2 /* readablePage.html */; }; 0B62EFD21AD63CD100ACB9CD /* Clearables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B62EFD11AD63CD100ACB9CD /* Clearables.swift */; }; - 0B6FBAB21AC1F830007EC669 /* numberedPage.html in Resources */ = {isa = PBXBuildFile; fileRef = 0B6FBAB11AC1F830007EC669 /* numberedPage.html */; }; 0B75AEA91AC20FB20015E5DC /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B8E0FF31A932BD500161DC3 /* ImageIO.framework */; }; - 0B7C1E951F6097AD006A8869 /* TrackingProtectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7C1E941F6097AD006A8869 /* TrackingProtectionTests.swift */; }; 0B7CF8872CAC1C4E00DC02F8 /* DefaultFolderHeirarchyFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7CF8862CAC1C4E00DC02F8 /* DefaultFolderHeirarchyFetcherTests.swift */; }; 0B7FC3D32CAE811F005C5CCE /* DefaultBookmarksSaverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B7FC3D12CAE811B005C5CCE /* DefaultBookmarksSaverTests.swift */; }; 0B8BF3702CA2D60B00E9812D /* BookmarksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8BF36F2CA2D60B00E9812D /* BookmarksViewController.swift */; }; @@ -49,14 +42,13 @@ 0B8BF3752CA2EFC000E9812D /* EditBookmarkCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B8BF3742CA2EFC000E9812D /* EditBookmarkCell.swift */; }; 0B8E0FF41A932BD500161DC3 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B8E0FF31A932BD500161DC3 /* ImageIO.framework */; }; 0BA02DB22942605600C92603 /* FormAutofillHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BA02DB12942605600C92603 /* FormAutofillHelper.swift */; }; + 0BA152502CE78DAD0090B869 /* UserAgentBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BA1524F2CE78DAD0090B869 /* UserAgentBuilderTests.swift */; }; 0BA1E00E1B03FB0B007675AF /* NetError.html in Resources */ = {isa = PBXBuildFile; fileRef = 0BA1E00D1B03FB0B007675AF /* NetError.html */; }; 0BA1E0301B051A07007675AF /* NetError.css in Resources */ = {isa = PBXBuildFile; fileRef = 0BA1E02F1B051A07007675AF /* NetError.css */; }; 0BA8964B1A250E6500C1010C /* ProfileTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BA896491A250E6500C1010C /* ProfileTest.swift */; }; 0BA8964C1A250E6500C1010C /* TestBookmarks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BA8964A1A250E6500C1010C /* TestBookmarks.swift */; }; - 0BB5B2881AC0A2B90052877D /* SnackBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB5B2861AC0A2B90052877D /* SnackBar.swift */; }; 0BB5B30B1AC0AD1F0052877D /* LoginsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB5B30A1AC0AD1F0052877D /* LoginsHelper.swift */; }; 0BBC50952CA1F7F900CB7248 /* BookmarksRefactorFeatureFlagProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BBC50942CA1F7F900CB7248 /* BookmarksRefactorFeatureFlagProvider.swift */; }; - 0BC9C9C41F26F54D000E8AB5 /* SiteLoadTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC9C9C31F26F54D000E8AB5 /* SiteLoadTest.swift */; }; 0BDDB33D2CA6B1DA00D501DF /* EditFolderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDDB33C2CA6B1DA00D501DF /* EditFolderViewController.swift */; }; 0BDDB33F2CA6B1F000D501DF /* EditFolderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDDB33E2CA6B1F000D501DF /* EditFolderViewModel.swift */; }; 0BDDB3412CA6B23300D501DF /* EditFolderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDDB3402CA6B23300D501DF /* EditFolderCell.swift */; }; @@ -64,10 +56,8 @@ 0BDDB3462CA6E43A00D501DF /* BookmarksSaver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BDDB3452CA6E43A00D501DF /* BookmarksSaver.swift */; }; 0BE181E72CBED065002DE979 /* LegacyBookmarkDetailPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BE181E62CBED065002DE979 /* LegacyBookmarkDetailPanel.swift */; }; 0BE4158B2CA16B5300F3B7A1 /* libStorage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FCAE21A1ABB51F800877008 /* libStorage.a */; }; - 0BF0DB4A1E57B05E009172B0 /* LaunchArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65075861E37F7AB006961AC /* LaunchArguments.swift */; }; 0BF0DB941A8545800039F300 /* URLBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BF0DB931A8545800039F300 /* URLBarView.swift */; }; 0BF1B7E31AC60DEA00A7B407 /* InsetButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BF1B7E21AC60DEA00A7B407 /* InsetButton.swift */; }; - 0BF8F8DA1AEFF1C900E90BC2 /* noTitle.html in Resources */ = {isa = PBXBuildFile; fileRef = 0BF8F8D91AEFF1C900E90BC2 /* noTitle.html */; }; 0E456F152C541CB300EE93BF /* PasswordGeneratorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E456F142C541CB300EE93BF /* PasswordGeneratorViewController.swift */; }; 0E6C1E212C909AD7001A43BB /* PasswordGeneratorAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6C1E202C909AD7001A43BB /* PasswordGeneratorAction.swift */; }; 0E6C1E232C909E04001A43BB /* PasswordGeneratorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6C1E222C909E04001A43BB /* PasswordGeneratorState.swift */; }; @@ -77,6 +67,70 @@ 0EC57CE52CA31E59002E3F04 /* PasswordGeneratorStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC57CE42CA31E59002E3F04 /* PasswordGeneratorStateTests.swift */; }; 0EC57D082CA6FCA5002E3F04 /* PasswordGeneratorHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC57D072CA6FCA5002E3F04 /* PasswordGeneratorHeaderView.swift */; }; 0EC57D0A2CA6FCC5002E3F04 /* PasswordGeneratorPasswordFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC57D092CA6FCC5002E3F04 /* PasswordGeneratorPasswordFieldView.swift */; }; + 0ECB6B6D2CCFF718006A7C82 /* DateGroupedTableDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECB6B672CCFE663006A7C82 /* DateGroupedTableDataTests.swift */; }; + 120059182F3C8BD800D65907 /* BrowserViewController+EcosiaNavigationHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120059172F3C8BD800D65907 /* BrowserViewController+EcosiaNavigationHandling.swift */; }; + 120EBBA22F3F30FA0056F398 /* SpotlightToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120EBBA12F3F30FA0056F398 /* SpotlightToast.swift */; }; + 120EBBA42F3F31140056F398 /* ProductTourSpotlightCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120EBBA32F3F31140056F398 /* ProductTourSpotlightCoordinator.swift */; }; + 120EBBB62F3F35F60056F398 /* BrowserViewController+ProductTourSpotlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120EBBB52F3F35F60056F398 /* BrowserViewController+ProductTourSpotlight.swift */; }; + 120ED1362F446A260056F398 /* NTPFirstSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 120ED1352F446A260056F398 /* NTPFirstSearchView.swift */; }; + 121416752D6373BA0097788B /* AllFramesAtDocumentEnd.js in Resources */ = {isa = PBXBuildFile; fileRef = 121416722D6373BA0097788B /* AllFramesAtDocumentEnd.js */; }; + 121416762D6373BA0097788B /* AllFramesAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = 121416732D6373BA0097788B /* AllFramesAtDocumentStart.js */; }; + 121416772D6373BA0097788B /* AutofillAllFramesAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = 121416742D6373BA0097788B /* AutofillAllFramesAtDocumentStart.js */; }; + 121416782D6373BA0097788B /* AddressFormManager.js in Resources */ = {isa = PBXBuildFile; fileRef = 121416712D6373BA0097788B /* AddressFormManager.js */; }; + 1214167B2D6375960097788B /* MainFrameAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = 1214167A2D6375960097788B /* MainFrameAtDocumentStart.js */; }; + 1214167C2D6375960097788B /* MainFrameAtDocumentEnd.js in Resources */ = {isa = PBXBuildFile; fileRef = 121416792D6375960097788B /* MainFrameAtDocumentEnd.js */; }; + 12141E6C2D6877EB0097788B /* EcosiaNetError.html in Resources */ = {isa = PBXBuildFile; fileRef = 12141E682D686C030097788B /* EcosiaNetError.html */; }; + 12141E6D2D6877F00097788B /* EcosiaNetError.css in Resources */ = {isa = PBXBuildFile; fileRef = 12141E6A2D686C1C0097788B /* EcosiaNetError.css */; }; + 121C6E182EC1EF870060319A /* BrowserViewController+WelcomeTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 121C6E172EC1EF870060319A /* BrowserViewController+WelcomeTransition.swift */; }; + 1230CFF22EBB990900D7AC00 /* BrazeKit in Frameworks */ = {isa = PBXBuildFile; productRef = 126509842CD925B40011BA36 /* BrazeKit */; }; + 1230CFF32EBB990900D7AC00 /* BrazeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 126509862CD925B40011BA36 /* BrazeUI */; }; + 123283ED2F210C10009EA5BC /* NTPFirstSearchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123283EC2F210C10009EA5BC /* NTPFirstSearchCell.swift */; }; + 123283EF2F210C23009EA5BC /* NTPFirstSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123283EE2F210C23009EA5BC /* NTPFirstSearchViewModel.swift */; }; + 123475C32DA6730A0017B0C2 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A6AB4528CA6A4C00EBEBDD /* String+Extension.swift */; }; + 123475CC2DA7D6620017B0C2 /* BeforeOrAfterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123475CB2DA7D6580017B0C2 /* BeforeOrAfterView.swift */; }; + 12348FFA2DB8D2D40017B0C2 /* PrefsKeys+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12348FF92DB8D2CC0017B0C2 /* PrefsKeys+Ecosia.swift */; }; + 123CEC4C2E7814A2009EB379 /* FoundersGroteskCondensed-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 123CEC4B2E7814A2009EB379 /* FoundersGroteskCondensed-Semibold.ttf */; }; + 124DAE822D350EFD0050104C /* String+Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6B2AC11D41F02D00429414 /* String+Punycode.swift */; }; + 124DAE8A2D3512FA0050104C /* DispatchQueueHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83821FF1FC7961D00303C12 /* DispatchQueueHelper.swift */; }; + 1251623C2D59FB30005CB958 /* Ecosia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CFE99662D45329200B25CE0 /* Ecosia.framework */; }; + 1251623D2D59FB31005CB958 /* Ecosia.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 2CFE99662D45329200B25CE0 /* Ecosia.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 128D68602F2A3AEE00F4B3EE /* LocalizedSearchSuggestions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 128D685F2F2A3AE700F4B3EE /* LocalizedSearchSuggestions.swift */; }; + 128D6E822F2A94CF00F4B3EE /* SearchSuggestionFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 128D6E812F2A94CF00F4B3EE /* SearchSuggestionFlowLayout.swift */; }; + 129F18FE2EC5F04200E870C0 /* Task+Sleep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129F18FD2EC5F04200E870C0 /* Task+Sleep.swift */; }; + 12A536342D3E637800924CB0 /* DownloadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA8D1C61BA037F500C8AE9E /* DownloadHelper.swift */; }; + 12A536352D3E63A200924CB0 /* GlobalTabEventHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F819C51FD70F5D009E31E4 /* GlobalTabEventHandlers.swift */; }; + 12A536372D3E89BF00924CB0 /* GCDWebServers in Frameworks */ = {isa = PBXBuildFile; productRef = 12A536362D3E89BF00924CB0 /* GCDWebServers */; }; + 12AEB1182D36A8C40035D7D8 /* QRCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA9293D31D6580E100AC8D33 /* QRCodeViewController.swift */; }; + 12AEB1222D37BE030035D7D8 /* SnackBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB5B2861AC0A2B90052877D /* SnackBar.swift */; }; + 12AEB1232D37CC4A0035D7D8 /* LegacyTabTrayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA52E1D925F5961F0092204C /* LegacyTabTrayViewController.swift */; }; + 12AEB1242D37CC6F0035D7D8 /* TabTrayButtonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E3984B1D21F2FD004E89BA /* TabTrayButtonExtensions.swift */; }; + 12AEB1262D3A51670035D7D8 /* LegacySessionData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12AEB1252D3A51670035D7D8 /* LegacySessionData.swift */; }; + 12AEB1282D3A568D0035D7D8 /* SessionRestoreHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12AEB1272D3A568D0035D7D8 /* SessionRestoreHandler.swift */; }; + 12AEB1292D3A9AE70035D7D8 /* CreditCardPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FF02B194A5800F0FEC1 /* CreditCardPayload.swift */; }; + 12AEB12A2D3A9AEB0035D7D8 /* FillCreditCardForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FF22B194A8300F0FEC1 /* FillCreditCardForm.swift */; }; + 12C11E272D281BCC00E4DDBF /* EcosiaHomepageSectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C11E212D281B7200E4DDBF /* EcosiaHomepageSectionType.swift */; }; + 12C11E282D281BD100E4DDBF /* EcosiaTopSiteItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C11E222D281B7A00E4DDBF /* EcosiaTopSiteItemCell.swift */; }; + 12C11E8D2D2826A600E4DDBF /* EcosiaDebugSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289252B07C8F000A8FCB3 /* EcosiaDebugSettings.swift */; }; + 12C11E8E2D2826A600E4DDBF /* NTPCustomizationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289272B07C8F000A8FCB3 /* NTPCustomizationSettingsViewController.swift */; }; + 12C11E8F2D2826A600E4DDBF /* EcosiaSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289262B07C8F000A8FCB3 /* EcosiaSettings.swift */; }; + 12C11E9A2D28281100E4DDBF /* WebsiteConnectionStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289422B07C8F000A8FCB3 /* WebsiteConnectionStatus.swift */; }; + 12C11EA12D28291300E4DDBF /* (null) in Sources */ = {isa = PBXBuildFile; }; + 12C11EA42D28291300E4DDBF /* URL+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB2894D2B07C8F000A8FCB3 /* URL+Ecosia.swift */; }; + 12C11EA52D28291300E4DDBF /* BrowserViewController+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289442B07C8F000A8FCB3 /* BrowserViewController+Ecosia.swift */; }; + 12C11EA62D28291300E4DDBF /* AppSettingsTableViewController+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF2206E2B72B0530038157D /* AppSettingsTableViewController+Ecosia.swift */; }; + 12C11EA72D28291300E4DDBF /* ErrorPageHandler+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289462B07C8F000A8FCB3 /* ErrorPageHandler+Ecosia.swift */; }; + 12C11EA82D28291300E4DDBF /* HomepageViewController+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289482B07C8F000A8FCB3 /* HomepageViewController+Ecosia.swift */; }; + 12C11EAA2D28291300E4DDBF /* UIView+maskedCorners.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB2894A2B07C8F000A8FCB3 /* UIView+maskedCorners.swift */; }; + 12C11EAB2D28291300E4DDBF /* NumberFormatter+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB2894C2B07C8F000A8FCB3 /* NumberFormatter+Ecosia.swift */; }; + 12C11EAC2D28291300E4DDBF /* UIFont+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289452B07C8F000A8FCB3 /* UIFont+Ecosia.swift */; }; + 12C11EAD2D28291300E4DDBF /* SnapKit+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289472B07C8F000A8FCB3 /* SnapKit+Ecosia.swift */; }; + 12C11EAE2D28291300E4DDBF /* SimpleToast+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C11E9D2D2828AA00E4DDBF /* SimpleToast+Ecosia.swift */; }; + 12C11EAF2D28291300E4DDBF /* UIButton+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C11E9C2D28289900E4DDBF /* UIButton+Ecosia.swift */; }; + 12C11EB02D28291900E4DDBF /* DispatchQueueHelper+BuildChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C11EA02D2828EA00E4DDBF /* DispatchQueueHelper+BuildChannel.swift */; }; + 12C11EB92D2829B200E4DDBF /* BookmarksExchange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB289542B07C8F000A8FCB3 /* BookmarksExchange.swift */; }; + 12C11F632D2831E500E4DDBF /* ScreenGraphTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39012F271F8ED262002E3D31 /* ScreenGraphTest.swift */; }; + 12C25BD62D27D9510048BADA /* MozillaAppServices in Frameworks */ = {isa = PBXBuildFile; productRef = 2CABD7152C11C9CC00A0750F /* MozillaAppServices */; }; + 12C25BED2D27F0A40048BADA /* SnowplowTracker in Frameworks */ = {isa = PBXBuildFile; productRef = 12C25BEC2D27F0A40048BADA /* SnowplowTracker */; }; 158241282820698B00956B39 /* RustRemoteTabsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 158241272820698B00956B39 /* RustRemoteTabsTests.swift */; }; 15DE98FD27FCED4F00F1ECDB /* RustRemoteTabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15DE98FC27FCED4F00F1ECDB /* RustRemoteTabs.swift */; }; 1BE7A4902C636AC800460798 /* URLSession+sharedMPTCP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BE7A48F2C636AC800460798 /* URLSession+sharedMPTCP.swift */; }; @@ -84,12 +138,8 @@ 1D06AE6624FEE4D5000B092B /* TopSitesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D06AE6524FEE4D5000B092B /* TopSitesProvider.swift */; }; 1D06AE6A24FEE8D6000B092B /* TabProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D06AE6924FEE8D6000B092B /* TabProvider.swift */; }; 1D0BA05C24F46A0400D731B5 /* TopSitesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D0BA05B24F46A0400D731B5 /* TopSitesProvider.swift */; }; - 1D1933742AF2C8C8005089C9 /* EventQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78962ADF32590011E9F2 /* EventQueue.swift */; }; 1D1933752AF2C8C9005089C9 /* EventQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78962ADF32590011E9F2 /* EventQueue.swift */; }; - 1D1933762AF2C8C9005089C9 /* EventQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78962ADF32590011E9F2 /* EventQueue.swift */; }; - 1D1933772AF2C8CE005089C9 /* AppEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78982ADF328E0011E9F2 /* AppEvent.swift */; }; 1D1933782AF2C8CE005089C9 /* AppEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78982ADF328E0011E9F2 /* AppEvent.swift */; }; - 1D1933792AF2C8CF005089C9 /* AppEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78982ADF328E0011E9F2 /* AppEvent.swift */; }; 1D2F68AB2ACB262900524B92 /* RemoteTabsPanelAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2F68AA2ACB262900524B92 /* RemoteTabsPanelAction.swift */; }; 1D2F68AD2ACB266300524B92 /* RemoteTabsPanelState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2F68AC2ACB266300524B92 /* RemoteTabsPanelState.swift */; }; 1D2F68AF2ACB272500524B92 /* RemoteTabsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2F68AE2ACB272500524B92 /* RemoteTabsTableViewController.swift */; }; @@ -102,7 +152,6 @@ 1D558A5A2BEE7D07001EF527 /* WindowSimpleTabsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D558A592BEE7D07001EF527 /* WindowSimpleTabsCoordinator.swift */; }; 1D558A5B2BEE7D07001EF527 /* WindowSimpleTabsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D558A592BEE7D07001EF527 /* WindowSimpleTabsCoordinator.swift */; }; 1D5CBF492B17E3CB0001D033 /* NotificationPayloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5CBF482B17E3CB0001D033 /* NotificationPayloads.swift */; }; - 1D5CBF4A2B17E3CB0001D033 /* NotificationPayloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5CBF482B17E3CB0001D033 /* NotificationPayloads.swift */; }; 1D69FF8D27B17286001F660E /* HomeLogoHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D69FF8C27B17285001F660E /* HomeLogoHeaderCell.swift */; }; 1D74FF502B2797EA00FF01D0 /* WindowManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D74FF4D2B27962200FF01D0 /* WindowManagerTests.swift */; }; 1D7B78972ADF32590011E9F2 /* EventQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D7B78962ADF32590011E9F2 /* EventQueue.swift */; }; @@ -127,7 +176,6 @@ 1DF426CF251BDF6A0086386A /* photon-colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C49854D206173C800893DAE /* photon-colors.swift */; }; 1DFE57FB27B2CB870025DE58 /* HighlightItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE57FA27B2CB870025DE58 /* HighlightItem.swift */; }; 1DFE57FD27BADD7D0025DE58 /* HomepageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE57FC27BADD7C0025DE58 /* HomepageViewModel.swift */; }; - 1DFE57FF27BAE3150025DE58 /* HomepageSectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE57FE27BAE3150025DE58 /* HomepageSectionType.swift */; }; 210887CC293E8800000AB4EE /* LegacyRemoteTabsErrorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210887CB293E8800000AB4EE /* LegacyRemoteTabsErrorCell.swift */; }; 2109478928AFD24C00B73D44 /* OnboardingViewControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2109478828AFD24C00B73D44 /* OnboardingViewControllerProtocol.swift */; }; 210E0EB8298D9D4500BB4F33 /* DefaultSearchEngineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96A5F72F298D8BEE00234E5F /* DefaultSearchEngineProvider.swift */; }; @@ -136,7 +184,6 @@ 210E0EBB298D9D6600BB4F33 /* OpenSearchEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1E3BE528CBBF44003388C4 /* OpenSearchEngine.swift */; }; 211046C92A7ADE9000A7309F /* BlockPopupSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211046C82A7ADE9000A7309F /* BlockPopupSetting.swift */; }; 211046CD2A7D842A00A7309F /* TPAccessoryInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211046CC2A7D842A00A7309F /* TPAccessoryInfo.swift */; }; - 21112968289480630082C08B /* HomepageMessageCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21112967289480630082C08B /* HomepageMessageCardViewModel.swift */; }; 211F00AC27F4D918001D9189 /* HistoryPanel+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211F00AB27F4D918001D9189 /* HistoryPanel+Search.swift */; }; 2128E27B292E624400FB91BE /* SendToDeviceActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2128E27A292E624400FB91BE /* SendToDeviceActivity.swift */; }; 2128E27C2930216F00FB91BE /* SendToDeviceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AFBAF8292EA0330065E35E /* SendToDeviceHelper.swift */; }; @@ -146,7 +193,6 @@ 212985E72A72B39D00546684 /* ThemeSettingsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212985E52A72B22800546684 /* ThemeSettingsControllerTests.swift */; }; 21357F2D293FDB60004BF9FD /* LegacyRemoteTabsErrorDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21357F2C293FDB60004BF9FD /* LegacyRemoteTabsErrorDataSource.swift */; }; 21357F2F294237D8004BF9FD /* RemoteTabsClientAndTabsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21357F2E294237D8004BF9FD /* RemoteTabsClientAndTabsDataSource.swift */; }; - 21371FA228A6C4A200BC3F37 /* OnboardingTelemetryUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21371FA128A6C4A200BC3F37 /* OnboardingTelemetryUtilityTests.swift */; }; 21371FA428AA7A8D00BC3F37 /* OnboardingViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21371FA328AA7A8D00BC3F37 /* OnboardingViewModelProtocol.swift */; }; 2137785D297F1F2800D01309 /* DownloadedFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2137785C297F1F2800D01309 /* DownloadedFile.swift */; }; 2137785F297F3B1B00D01309 /* DownloadsPanelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2137785E297F3B1B00D01309 /* DownloadsPanelViewModel.swift */; }; @@ -159,8 +205,6 @@ 21420EF72ABA338D00B28550 /* TabTrayCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21420EF62ABA338D00B28550 /* TabTrayCoordinator.swift */; }; 21420EF92ABC75A400B28550 /* TabTrayCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21420EF82ABC75A400B28550 /* TabTrayCoordinatorTests.swift */; }; 214EF4152AC5D5D0005BCCDA /* TabDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 214EF4142AC5D5D0005BCCDA /* TabDisplayView.swift */; }; - 21534904288201E300FADB4D /* GleanPlumbMessageManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215349022881FA3C00FADB4D /* GleanPlumbMessageManagerTests.swift */; }; - 215349062886007900FADB4D /* GleanPlumbMessageStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215349052886007900FADB4D /* GleanPlumbMessageStoreTests.swift */; }; 21583E422B1A3703009D084D /* LegacyInactiveTabModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21583E412B1A3703009D084D /* LegacyInactiveTabModel.swift */; }; 215B457F27D7FD4B00E5E800 /* LegacyTabGroupData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215B457E27D7FD4B00E5E800 /* LegacyTabGroupData.swift */; }; 215B458027D7FD7D00E5E800 /* LegacyTabGroupData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 215B457E27D7FD4B00E5E800 /* LegacyTabGroupData.swift */; }; @@ -171,7 +215,6 @@ 21618A8A2A4389F700A5189E /* ActiveScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21618A892A4389F700A5189E /* ActiveScreenState.swift */; }; 21618A8C2A438A0900A5189E /* ActiveScreenAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21618A8B2A438A0900A5189E /* ActiveScreenAction.swift */; }; 21618A932A4499FC00A5189E /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21618A912A4499FC00A5189E /* AppState.swift */; }; - 2165B2C02860BB41004C0786 /* AdjustTelemetryHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2165B2BF2860BB41004C0786 /* AdjustTelemetryHelperTests.swift */; }; 2165B2C22860C2F4004C0786 /* AdjustTelemetryHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2165B2C12860C2F4004C0786 /* AdjustTelemetryHelper.swift */; }; 2165B2C42860CB34004C0786 /* MockAdjustTelemetryData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2165B2C32860CB34004C0786 /* MockAdjustTelemetryData.swift */; }; 2165B2CC28748CD7004C0786 /* LibraryPanelDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2165B2CB28748CD7004C0786 /* LibraryPanelDescriptor.swift */; }; @@ -223,13 +266,12 @@ 21E78A7028F9A8C500F8D687 /* MockUIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21E78A6F28F9A8C500F8D687 /* MockUIDevice.swift */; }; 21E78A7228F9A93100F8D687 /* UIDeviceInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21E78A7128F9A93100F8D687 /* UIDeviceInterface.swift */; }; 21EA466A2B04130500AAAB2D /* TabsPanelState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21EA46692B04130500AAAB2D /* TabsPanelState.swift */; }; - 21ED80B32AF2E43A0065D4C7 /* TabDisplayViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21ED80B22AF2E43A0065D4C7 /* TabDisplayViewTests.swift */; }; + 21ED80B32AF2E43A0065D4C7 /* TabDisplayDiffableDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21ED80B22AF2E43A0065D4C7 /* TabDisplayDiffableDataSourceTests.swift */; }; 21F2A2D22B0BC85200626AEC /* InactiveTabsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21F2A2D12B0BC85200626AEC /* InactiveTabsModel.swift */; }; 21F2A2D42B0D194A00626AEC /* TabsPanelStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21F2A2D32B0D194A00626AEC /* TabsPanelStateTests.swift */; }; 21FA8FAE2AE856460013B815 /* TabsCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FA8FAD2AE856460013B815 /* TabsCoordinatorTests.swift */; }; 21FA8FB02AE856590013B815 /* RemoteTabsCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FA8FAF2AE856590013B815 /* RemoteTabsCoordinatorTests.swift */; }; 21FA8FB22AE856EB0013B815 /* MockTabTrayCoordinatorDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FA8FB12AE856EB0013B815 /* MockTabTrayCoordinatorDelegate.swift */; }; - 2386E4E624F8358E0072EF17 /* HomepageMessageCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2386E4E524F8358E0072EF17 /* HomepageMessageCard.swift */; }; 23BEA767251A99ED00A014BF /* NewYorkMedium-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 23BEA766251A99E300A014BF /* NewYorkMedium-Bold.otf */; }; 23BEA768251A99ED00A014BF /* NewYorkMedium-BoldItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = 23BEA765251A99E200A014BF /* NewYorkMedium-BoldItalic.otf */; }; 23BEA769251A99ED00A014BF /* NewYorkMedium-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 23BEA763251A99E200A014BF /* NewYorkMedium-Regular.otf */; }; @@ -262,28 +304,131 @@ 28E08C9A1AF44F00009BA2FA /* BrowserSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 282915E51AF1A7920006EEB5 /* BrowserSchema.swift */; }; 28E91E751B443AD5009DF274 /* SyncConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28E91E741B443AD5009DF274 /* SyncConstants.swift */; }; 28ECD9BF1BA1F19900D829DA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C001B90A44F005ABB0D /* libz.tbd */; }; - 2C2A5EF41E68469500F02659 /* PrivateBrowsingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A5EF31E68469500F02659 /* PrivateBrowsingTest.swift */; }; - 2C2A91291FA2410D002E36BD /* HistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C2A91281FA2410D002E36BD /* HistoryTests.swift */; }; - 2C31A7A91E8BFB2200DAC646 /* ReadingListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C31A7A81E8BFB2200DAC646 /* ReadingListTests.swift */; }; - 2C31A8471E8D447F00DAC646 /* HomePageSettingsUITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C31A8461E8D447F00DAC646 /* HomePageSettingsUITest.swift */; }; - 2C3406C81E719F00000FD889 /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C3406C71E719F00000FD889 /* SettingsTests.swift */; }; - 2C473BD0209778900008C853 /* DownloadsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C473BCF209778900008C853 /* DownloadsTests.swift */; }; + 2C00E15F2E299AE800811A76 /* InvisibleTabSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C00E15D2E299AE800811A76 /* InvisibleTabSession.swift */; }; + 2C00E1642E29A70500811A76 /* InvisibleTabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C00E1622E29A70500811A76 /* InvisibleTabManager.swift */; }; + 2C00E3942E29B0E700811A76 /* EcosiaAuthFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C00E38F2E29B0E700811A76 /* EcosiaAuthFlow.swift */; }; + 2C1298AF2BF602D3005AE4E4 /* DefaultSuggestedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 394CF6CE1BAA493C00906917 /* DefaultSuggestedSites.swift */; }; + 2C2EA77F2DA3F2990085F5BC /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 2C2EA77E2DA3F2990085F5BC /* Lottie */; }; + 2C2F31F52D46612F00977F55 /* TopSitesHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5BD9582878871B000FE773 /* TopSitesHelperTests.swift */; }; + 2C2F31F62D4663D800977F55 /* DependencyHelperMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70EF18295E2E1600790249 /* DependencyHelperMock.swift */; }; + 2C2F31F72D4664CB00977F55 /* MockThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA75A622A46272000533F8D /* MockThemeManager.swift */; }; + 2C2F31F82D4664E300977F55 /* XCTestCaseExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3BA41671BD82F2200DA5457 /* XCTestCaseExtensions.swift */; }; + 2C2F31F92D46672300977F55 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84266742728462900382274 /* AccessibilityIdentifiers.swift */; }; + 2C2F31FA2D46941400977F55 /* MockWindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D558A562BED7ECB001EF527 /* MockWindowManager.swift */; }; + 2C2F31FB2D46941400977F55 /* MockWindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D558A562BED7ECB001EF527 /* MockWindowManager.swift */; }; + 2C2F31FC2D46942A00977F55 /* MockMicrosurveySurfaceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A827E312C20C8AE008D5E3C /* MockMicrosurveySurfaceManager.swift */; }; + 2C2F31FD2D46942A00977F55 /* MockMicrosurveySurfaceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A827E312C20C8AE008D5E3C /* MockMicrosurveySurfaceManager.swift */; }; + 2C2F31FE2D46961000977F55 /* MockablePinnedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D5EDBF292D619000311934 /* MockablePinnedSites.swift */; }; 2C49854E206173C800893DAE /* photon-colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C49854D206173C800893DAE /* photon-colors.swift */; }; - 2C4A07DC20246EAD0083E320 /* DragAndDropTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4A07DB20246EAD0083E320 /* DragAndDropTests.swift */; }; - 2C4B6BF320349EB800A009C2 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4B6BF220349EB800A009C2 /* OnboardingTests.swift */; }; - 2C8C07771E7800EA00DC1237 /* FindInPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8C07761E7800EA00DC1237 /* FindInPageTests.swift */; }; - 2C97EC711E72C80E0092EC18 /* TopTabsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C97EC701E72C80E0092EC18 /* TopTabsTest.swift */; }; - 2CA16FDE1E5F089100332277 /* SearchTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA16FDD1E5F089100332277 /* SearchTest.swift */; }; - 2CB1A65A1FDEA8B60084E96D /* NewTabSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB1A6591FDEA8B60084E96D /* NewTabSettings.swift */; }; - 2CB56E3F1E926BFB00AF7586 /* ToolbarTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB56E3E1E926BFB00AF7586 /* ToolbarTest.swift */; }; - 2CC1B3F01E9B861400814EEC /* DomainAutocompleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC1B3EF1E9B861400814EEC /* DomainAutocompleteTests.swift */; }; - 2CCB296720A99C9500121DD8 /* LoginsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CCB296620A99C9500121DD8 /* LoginsTests.swift */; }; - 2CCF17532105E4FD00705AE5 /* DisplaySettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CCF17522105E4FD00705AE5 /* DisplaySettingsTests.swift */; }; - 2CEA6F791E93E3A600D4100E /* SearchSettingsUITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEA6F781E93E3A600D4100E /* SearchSettingsUITest.swift */; }; - 2CEDADA220207EC400223A89 /* SyncFAUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEDADA120207EC400223A89 /* SyncFAUITests.swift */; }; - 2CF21D0920A4A163000D08B7 /* PocketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF21D0820A4A163000D08B7 /* PocketTests.swift */; }; - 2CF449A51E7BFE2C00FD7595 /* NavigationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF449A41E7BFE2C00FD7595 /* NavigationTest.swift */; }; - 2CF9D9AA20067FA10083DF2A /* BrowsingPDFTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF9D9A920067FA10083DF2A /* BrowsingPDFTests.swift */; }; + 2C4BFFCA2D470E4100707D2F /* disconnect-block-cookies-analytics.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC42D470E4100707D2F /* disconnect-block-cookies-analytics.json */; }; + 2C4BFFCB2D470E4100707D2F /* disconnect-block-cookies-advertising.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC32D470E4100707D2F /* disconnect-block-cookies-advertising.json */; }; + 2C4BFFCC2D470E4100707D2F /* disconnect-block-cookies-social.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC62D470E4100707D2F /* disconnect-block-cookies-social.json */; }; + 2C4BFFCD2D470E4100707D2F /* disconnect-block-social.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC92D470E4100707D2F /* disconnect-block-social.json */; }; + 2C4BFFCE2D470E4100707D2F /* disconnect-block-advertising.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC02D470E4100707D2F /* disconnect-block-advertising.json */; }; + 2C4BFFCF2D470E4100707D2F /* disconnect-block-cookies-content.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC52D470E4100707D2F /* disconnect-block-cookies-content.json */; }; + 2C4BFFD02D470E4100707D2F /* disconnect-block-analytics.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC12D470E4100707D2F /* disconnect-block-analytics.json */; }; + 2C4BFFD12D470E4100707D2F /* disconnect-block-cryptomining.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC72D470E4100707D2F /* disconnect-block-cryptomining.json */; }; + 2C4BFFD22D470E4100707D2F /* disconnect-block-fingerprinting.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC82D470E4100707D2F /* disconnect-block-fingerprinting.json */; }; + 2C4BFFD32D470E4100707D2F /* disconnect-block-content.json in Resources */ = {isa = PBXBuildFile; fileRef = 2C4BFFC22D470E4100707D2F /* disconnect-block-content.json */; }; + 2C69DA782C62259D00D7F69F /* MockProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 281B2BE91ADF4D90002917DC /* MockProfile.swift */; }; + 2C69DA792C6225AE00D7F69F /* DependencyHelperMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70EF18295E2E1600790249 /* DependencyHelperMock.swift */; }; + 2C69DA7B2C6225C400D7F69F /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 2C69DA7A2C6225C400D7F69F /* Common */; }; + 2C69DA7C2C6225DB00D7F69F /* MockOverlayModeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21B41A1B298B1876008BC0A2 /* MockOverlayModeManager.swift */; }; + 2C69DA7D2C6244BE00D7F69F /* MockTabDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A475E8C29DB888E009C13FD /* MockTabDataStore.swift */; }; + 2C69DA7E2C6244C400D7F69F /* MockThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA75A622A46272000533F8D /* MockThemeManager.swift */; }; + 2C6C908F2C614A6C007D9B43 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 2C6C908E2C614A6C007D9B43 /* SnapshotTesting */; }; + 2C7DC2402B1648BA00C049C8 /* LegacyTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9A179A20E69A7E00B12184 /* LegacyTheme.swift */; }; + 2C7DC2462B16493C00C049C8 /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2816EFFF1B33E05400522243 /* UIConstants.swift */; }; + 2C8723942EA930C50085902C /* Auth0 in Frameworks */ = {isa = PBXBuildFile; productRef = 2C8723932EA930C50085902C /* Auth0 */; }; + 2C8723962EA9313D0085902C /* TabAutoCloseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8723952EA9313D0085902C /* TabAutoCloseManager.swift */; }; + 2C87239A2EA931490085902C /* BrowserViewController+EcosiaErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8723972EA931490085902C /* BrowserViewController+EcosiaErrorHandling.swift */; }; + 2C87239B2EA931490085902C /* TabManager+InvisibleTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8723992EA931490085902C /* TabManager+InvisibleTab.swift */; }; + 2C87239C2EA931490085902C /* Tab+InvisibleTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8723982EA931490085902C /* Tab+InvisibleTab.swift */; }; + 2C87239E2EA931DB0085902C /* AccountsProviderWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C87239D2EA931DB0085902C /* AccountsProviderWrapper.swift */; }; + 2CB5C33F2E45F02D00B1048F /* SearchViewController+Ecosia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CB5C33E2E45F02D00B1048F /* SearchViewController+Ecosia.swift */; }; + 2CC0C2AD2DA804EA006FE9B7 /* ViewInspector in Frameworks */ = {isa = PBXBuildFile; productRef = 2CC0C2AC2DA804EA006FE9B7 /* ViewInspector */; }; + 2CC246602D520EF90098467A /* EcosiaLightTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2465E2D520EF90098467A /* EcosiaLightTheme.swift */; }; + 2CC246632D520EF90098467A /* EcosiaDarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2465D2D520EF90098467A /* EcosiaDarkTheme.swift */; }; + 2CC246642D520EF90098467A /* EcosiaLightTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2465E2D520EF90098467A /* EcosiaLightTheme.swift */; }; + 2CC246672D520EF90098467A /* EcosiaDarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2465D2D520EF90098467A /* EcosiaDarkTheme.swift */; }; + 2CC246682D520EF90098467A /* EcosiaLightTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2465E2D520EF90098467A /* EcosiaLightTheme.swift */; }; + 2CC2466B2D520EF90098467A /* EcosiaDarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2465D2D520EF90098467A /* EcosiaDarkTheme.swift */; }; + 2CC24C752D52478E0098467A /* MockTabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A36AC2B2886F27F00CDC0AD /* MockTabManager.swift */; }; + 2CE48EDA2E29B5C400CE84CE /* EcosiaAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE48ED92E29B5C400CE84CE /* EcosiaAuth.swift */; }; + 2CE9E8072E43927000141C6D /* NTPHeaderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE9E8052E43927000141C6D /* NTPHeaderViewModel.swift */; }; + 2CE9E8082E43927000141C6D /* NTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE9E8042E43927000141C6D /* NTPHeader.swift */; }; + 2CFE9FBF2D45348200B25CE0 /* SnowplowTracker in Frameworks */ = {isa = PBXBuildFile; productRef = 2CFE9FBE2D45348200B25CE0 /* SnowplowTracker */; }; + 2CFE9FC02D45348700B25CE0 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; + 2CFE9FC22D4535FD00B25CE0 /* Ecosia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CFE99662D45329200B25CE0 /* Ecosia.framework */; }; + 2CFE9FCC2D45363500B25CE0 /* BrazeKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2CFE9FCB2D45363500B25CE0 /* BrazeKit */; }; + 2CFE9FCE2D45363500B25CE0 /* BrazeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 2CFE9FCD2D45363500B25CE0 /* BrazeUI */; }; + 2CFE9FD02D45364100B25CE0 /* SnowplowTracker in Frameworks */ = {isa = PBXBuildFile; productRef = 2CFE9FCF2D45364100B25CE0 /* SnowplowTracker */; }; + 2CFE9FD22D45364600B25CE0 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 2CFE9FD12D45364600B25CE0 /* Common */; }; + 2CFE9FE32D453F0600B25CE0 /* Ecosia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CFE99662D45329200B25CE0 /* Ecosia.framework */; }; + 2CFE9FEA2D45404800B25CE0 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 2CFE9FE92D45404800B25CE0 /* SwiftSoup */; }; + 2CFE9FF02D4557EF00B25CE0 /* FxNimbusMessaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FEE2D4557EF00B25CE0 /* FxNimbusMessaging.swift */; }; + 2CFE9FF12D4557EF00B25CE0 /* FxNimbus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FED2D4557EF00B25CE0 /* FxNimbus.swift */; }; + 2CFE9FF22D4557EF00B25CE0 /* Metrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FEB2D4557EF00B25CE0 /* Metrics.swift */; }; + 2CFEA0542D455BD500B25CE0 /* MarketsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0062D455BD500B25CE0 /* MarketsController.swift */; }; + 2CFEA0562D455BD500B25CE0 /* NewsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA02B2D455BD500B25CE0 /* NewsController.swift */; }; + 2CFEA0572D455BD500B25CE0 /* NTPLogoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0292D455BD500B25CE0 /* NTPLogoCell.swift */; }; + 2CFEA0592D455BD500B25CE0 /* NTPImpactCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA01F2D455BD500B25CE0 /* NTPImpactCell.swift */; }; + 2CFEA05A2D455BD500B25CE0 /* EcosiaPrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FFC2D455BD500B25CE0 /* EcosiaPrimaryButton.swift */; }; + 2CFEA05C2D455BD500B25CE0 /* WhatsNewLocalDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA04D2D455BD500B25CE0 /* WhatsNewLocalDataProvider.swift */; }; + 2CFEA05D2D455BD500B25CE0 /* NTPTooltipDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0372D455BD500B25CE0 /* NTPTooltipDelegate.swift */; }; + 2CFEA05E2D455BD500B25CE0 /* EmptyReadingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0002D455BD500B25CE0 /* EmptyReadingListView.swift */; }; + 2CFEA05F2D455BD500B25CE0 /* NTPImpactRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0222D455BD500B25CE0 /* NTPImpactRowView.swift */; }; + 2CFEA0612D455BD500B25CE0 /* EmptyHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FFF2D455BD500B25CE0 /* EmptyHeader.swift */; }; + 2CFEA0622D455BD500B25CE0 /* NTPTooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0352D455BD500B25CE0 /* NTPTooltip.swift */; }; + 2CFEA0632D455BD500B25CE0 /* NTPLibraryShortcutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0272D455BD500B25CE0 /* NTPLibraryShortcutView.swift */; }; + 2CFEA0642D455BD500B25CE0 /* MultiplyImpact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0072D455BD500B25CE0 /* MultiplyImpact.swift */; }; + 2CFEA0672D455BD500B25CE0 /* PageActionsShortcutsHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0452D455BD500B25CE0 /* PageActionsShortcutsHeader.swift */; }; + 2CFEA0682D455BD500B25CE0 /* NTPImpactDividerFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0212D455BD500B25CE0 /* NTPImpactDividerFooter.swift */; }; + 2CFEA0692D455BD500B25CE0 /* EmptyBookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FFD2D455BD500B25CE0 /* EmptyBookmarksView.swift */; }; + 2CFEA06E2D455BD500B25CE0 /* CircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0322D455BD500B25CE0 /* CircleButton.swift */; }; + 2CFEA06F2D455BD500B25CE0 /* WhatsNewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0512D455BD500B25CE0 /* WhatsNewViewController.swift */; }; + 2CFEA0732D455BD500B25CE0 /* WhatsNewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0522D455BD500B25CE0 /* WhatsNewViewModel.swift */; }; + 2CFEA0752D455BD500B25CE0 /* NTPTooltip.Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0362D455BD500B25CE0 /* NTPTooltip.Highlight.swift */; }; + 2CFEA0762D455BD500B25CE0 /* NTPNewsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA02C2D455BD500B25CE0 /* NTPNewsCell.swift */; }; + 2CFEA0772D455BD500B25CE0 /* NTPNewsCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA02D2D455BD500B25CE0 /* NTPNewsCellViewModel.swift */; }; + 2CFEA0782D455BD500B25CE0 /* FilterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0012D455BD500B25CE0 /* FilterController.swift */; }; + 2CFEA0792D455BD500B25CE0 /* PageActionMenuCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0442D455BD500B25CE0 /* PageActionMenuCell.swift */; }; + 2CFEA07A2D455BD500B25CE0 /* EcosiaLaunchScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0032D455BD500B25CE0 /* EcosiaLaunchScreenView.swift */; }; + 2CFEA07B2D455BD500B25CE0 /* WhatsNewDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA04C2D455BD500B25CE0 /* WhatsNewDataProvider.swift */; }; + 2CFEA07C2D455BD500B25CE0 /* NTPImpactCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0202D455BD500B25CE0 /* NTPImpactCellViewModel.swift */; }; + 2CFEA07D2D455BD500B25CE0 /* EmptyBookmarksViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FFE2D455BD500B25CE0 /* EmptyBookmarksViewDelegate.swift */; }; + 2CFEA07F2D455BD500B25CE0 /* DefaultBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0332D455BD500B25CE0 /* DefaultBrowserViewController.swift */; }; + 2CFEA0812D455BD500B25CE0 /* WhatsNewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0502D455BD500B25CE0 /* WhatsNewItem.swift */; }; + 2CFEA0822D455BD500B25CE0 /* NTPLibaryCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0252D455BD500B25CE0 /* NTPLibaryCellViewModel.swift */; }; + 2CFEA0832D455BD500B25CE0 /* NTPConfigurableNudgeCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA02F2D455BD500B25CE0 /* NTPConfigurableNudgeCardCell.swift */; }; + 2CFEA0842D455BD500B25CE0 /* LoadingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0052D455BD500B25CE0 /* LoadingScreen.swift */; }; + 2CFEA0852D455BD500B25CE0 /* NTPCustomizationCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA01C2D455BD500B25CE0 /* NTPCustomizationCellViewModel.swift */; }; + 2CFEA0872D455BD500B25CE0 /* NTPCustomizationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA01B2D455BD500B25CE0 /* NTPCustomizationCell.swift */; }; + 2CFEA08A2D455BD500B25CE0 /* MultiplyImpactStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0082D455BD500B25CE0 /* MultiplyImpactStep.swift */; }; + 2CFEA08B2D455BD500B25CE0 /* EcosiaFindInPageBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FFA2D455BD500B25CE0 /* EcosiaFindInPageBar.swift */; }; + 2CFEA08C2D455BD500B25CE0 /* CustomizableNTPSettingConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA01A2D455BD500B25CE0 /* CustomizableNTPSettingConfig.swift */; }; + 2CFEA08D2D455BD500B25CE0 /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0232D455BD500B25CE0 /* ProgressView.swift */; }; + 2CFEA08F2D455BD500B25CE0 /* NTPConfigurableNudgeCardCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0302D455BD500B25CE0 /* NTPConfigurableNudgeCardCellViewModel.swift */; }; + 2CFEA0912D455BD500B25CE0 /* ClimateImpactInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA01E2D455BD500B25CE0 /* ClimateImpactInfo.swift */; }; + 2CFEA0942D455BD500B25CE0 /* NTPLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0342D455BD500B25CE0 /* NTPLayout.swift */; }; + 2CFEA0952D455BD500B25CE0 /* PageActionMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0432D455BD500B25CE0 /* PageActionMenu.swift */; }; + 2CFEA0972D455BD500B25CE0 /* NTPLibraryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA0262D455BD500B25CE0 /* NTPLibraryCell.swift */; }; + 2CFEA0982D455BD500B25CE0 /* WhatsNewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA04F2D455BD500B25CE0 /* WhatsNewCell.swift */; }; + 2CFEA0992D455BD500B25CE0 /* EcosiaThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA04A2D455BD500B25CE0 /* EcosiaThemeManager.swift */; }; + 2CFEA09B2D455BD500B25CE0 /* EcosiaNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFE9FFB2D455BD500B25CE0 /* EcosiaNavigation.swift */; }; + 2CFEA09D2D455BD500B25CE0 /* EcosiaLaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2CFEA0022D455BD500B25CE0 /* EcosiaLaunchScreen.xib */; }; + 2CFEA09E2D455BD500B25CE0 /* Ecosia.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2CFE9FF92D455BD500B25CE0 /* Ecosia.xcassets */; }; + 2CFEA0A02D455DE500B25CE0 /* EcosiaThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA04A2D455BD500B25CE0 /* EcosiaThemeManager.swift */; }; + 2CFEA0A42D455EB200B25CE0 /* EcosiaThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CFEA04A2D455BD500B25CE0 /* EcosiaThemeManager.swift */; }; + 2CFEA0A72D455F8D00B25CE0 /* (null) in Sources */ = {isa = PBXBuildFile; }; + 2CFEA0B12D45655200B25CE0 /* DispatchQueueHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83821FF1FC7961D00303C12 /* DispatchQueueHelper.swift */; }; + 2CFEA0B22D4641F600B25CE0 /* MockProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 281B2BE91ADF4D90002917DC /* MockProfile.swift */; }; + 2CFEA0B32D46420E00B25CE0 /* MockTabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A36AC2B2886F27F00CDC0AD /* MockTabManager.swift */; }; + 2CFEA0B42D46423600B25CE0 /* MockURLBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9A09D528B01FD500B6F51E /* MockURLBarView.swift */; }; + 2CFEA0B52D46424B00B25CE0 /* MockOverlayModeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21B41A1B298B1876008BC0A2 /* MockOverlayModeManager.swift */; }; + 2CFEA0B62D46427600B25CE0 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; + 2CFEA0B72D46427600B25CE0 /* RustMozillaAppServices.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 2CFEA0BB2D46429900B25CE0 /* ProfileTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BA896491A250E6500C1010C /* ProfileTest.swift */; }; 2F13E79B1AC0C02700D75081 /* StringExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F13E79A1AC0C02700D75081 /* StringExtensionsTests.swift */; }; 2F44FA1B1A9D426A00FD20CC /* TestHashExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F44FA1A1A9D426A00FD20CC /* TestHashExtensions.swift */; }; 2F44FC721A9E840300FD20CC /* SettingsNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F44FC711A9E840300FD20CC /* SettingsNavigationController.swift */; }; @@ -303,55 +448,35 @@ 2FDE87FE1ABB3817005317B1 /* LegacyRemoteTabsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FDE87FD1ABB3817005317B1 /* LegacyRemoteTabsPanel.swift */; }; 318FB6EB1DB5600D0004E40F /* SQLiteHistoryFactories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 318FB6EA1DB5600D0004E40F /* SQLiteHistoryFactories.swift */; }; 31ADB5DA1E58CEC300E87909 /* ClipboardBarDisplayHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ADB5D91E58CEC300E87909 /* ClipboardBarDisplayHandler.swift */; }; - 39012F281F8ED262002E3D31 /* ScreenGraphTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39012F271F8ED262002E3D31 /* ScreenGraphTest.swift */; }; 391B4FFF1F9767F50094F841 /* FxScreenGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EB46981E26DDB4006346E8 /* FxScreenGraph.swift */; }; 39236E721FCC600200A38F1B /* TabEventHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39236E711FCC600200A38F1B /* TabEventHandlerTests.swift */; }; 392ED7E41D0AEF56009D9B62 /* NewTabAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392ED7E31D0AEF56009D9B62 /* NewTabAccessors.swift */; }; 392ED7E61D0AEFEF009D9B62 /* HomePageAccessors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 392ED7E51D0AEFEF009D9B62 /* HomePageAccessors.swift */; }; 3943A81D1E9807C700D4F6DC /* FxAPushMessageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3943A81C1E9807C700D4F6DC /* FxAPushMessageTest.swift */; }; 39455F771FC83F430088A22C /* TabEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39455F761FC83F430088A22C /* TabEventHandler.swift */; }; - 394CF6CF1BAA493C00906917 /* DefaultSuggestedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 394CF6CE1BAA493C00906917 /* DefaultSuggestedSites.swift */; }; 3964B09A1EA8F06F00F2EEF4 /* FeatureSwitch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3964B0991EA8F06F00F2EEF4 /* FeatureSwitch.swift */; }; 3964B09C1EA8F32C00F2EEF4 /* FeatureSwitchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3964B09B1EA8F32C00F2EEF4 /* FeatureSwitchTests.swift */; }; - 39673BC12B6D82F400653F4A /* FxNimbusMessaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39673BC02B6D82F400653F4A /* FxNimbusMessaging.swift */; }; 396CDB55203C5B870034A3A3 /* TabTrayController+KeyCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 396CDB54203C5B870034A3A3 /* TabTrayController+KeyCommands.swift */; }; - 396E38CC1EE0816C00CC180F /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34DC84D1A16C40C00D49B7B /* Profile.swift */; }; - 396E38DD1EE081DA00CC180F /* SyncDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E60D03171D511398002FE3F6 /* SyncDisplayState.swift */; }; 396E38F11EE0C8EC00CC180F /* FxAPushMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3905B4D41E8E7A6B0027D953 /* FxAPushMessageHandler.swift */; }; - 396E38F21EE0C8ED00CC180F /* FxAPushMessageHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3905B4D41E8E7A6B0027D953 /* FxAPushMessageHandler.swift */; }; - 397848DE1ED86605004C0C0B /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397848DD1ED86605004C0C0B /* NotificationService.swift */; }; - 397848E21ED86605004C0C0B /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 397848DB1ED86605004C0C0B /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 39A359E41BFCCE94006B9E87 /* UserActivityHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39A359E31BFCCE94006B9E87 /* UserActivityHandler.swift */; }; 39A35AED1C0662A3006B9E87 /* SpotlightHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = 39A35AEC1C0662A3006B9E87 /* SpotlightHelper.js */; }; - 39AF317429DAE37E00F8E6F7 /* NimbusMessagingMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39AF317329DAE37E00F8E6F7 /* NimbusMessagingMessageTests.swift */; }; 39C137972655798A003DC662 /* NimbusIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C137962655798A003DC662 /* NimbusIntegrationTests.swift */; }; - 39C261CC2018DE21009D97BD /* FxScreenGraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C261CB2018DE20009D97BD /* FxScreenGraphTests.swift */; }; 39D056382665235700FBEE59 /* initial_experiments.json in Resources */ = {isa = PBXBuildFile; fileRef = 3964F5FB2656D2B500065278 /* initial_experiments.json */; }; - 39D0DA7629D767DE000760B8 /* NimbusMessagingTriggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39D0DA7429D767D1000760B8 /* NimbusMessagingTriggerTests.swift */; }; 39E65D271CA5B92000C63CE3 /* AsyncReducerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39E65D261CA5B92000C63CE3 /* AsyncReducerTests.swift */; }; - 39EB469A1E26DDB4006346E8 /* FxScreenGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EB46981E26DDB4006346E8 /* FxScreenGraph.swift */; }; 39EF434E260A73950011E22E /* Experiments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39EF434D260A73950011E22E /* Experiments.swift */; }; 39F4C0FA2045D87400746155 /* FocusHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = 39F4C0F92045D87400746155 /* FocusHelper.js */; }; 39F4C10A2045DB2E00746155 /* FocusHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F4C1092045DB2E00746155 /* FocusHelper.swift */; }; - 39F819C61FD70F5D009E31E4 /* GlobalTabEventHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39F819C51FD70F5D009E31E4 /* GlobalTabEventHandlers.swift */; }; 3B39EDBA1E16E18900EF029F /* CustomSearchEnginesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B39EDB91E16E18900EF029F /* CustomSearchEnginesTest.swift */; }; 3B39EDCB1E16E1AA00EF029F /* CustomSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B39EDCA1E16E1AA00EF029F /* CustomSearchViewController.swift */; }; 3B43E3D31D95C48D00BBA9DB /* StoragePerfTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B43E3D21D95C48D00BBA9DB /* StoragePerfTests.swift */; }; 3B4AA24B1D8B8C4C00A2E008 /* ArrayExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B4AA24A1D8B8C4C00A2E008 /* ArrayExtensionTests.swift */; }; - 3B546EC01D95ECAE00BDBE36 /* ActivityStreamTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B546EBF1D95ECAE00BDBE36 /* ActivityStreamTest.swift */; }; - 3B61CD591F2A750800D38DE1 /* PocketFeedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B61CD581F2A750800D38DE1 /* PocketFeedTests.swift */; }; 3B61CD631F2A769D00D38DE1 /* pocketglobalfeed.json in Resources */ = {isa = PBXBuildFile; fileRef = 3B61CD621F2A769D00D38DE1 /* pocketglobalfeed.json */; }; 3B6F40181DC7849C00656CC6 /* TopSitesViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B6F40171DC7849C00656CC6 /* TopSitesViewModelTests.swift */; }; - 3BB50E111D6274CD004B33DF /* TopSiteItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB50E101D6274CD004B33DF /* TopSiteItemCell.swift */; }; 3BB50E201D627539004B33DF /* LegacyHomepageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BB50E1F1D627539004B33DF /* LegacyHomepageViewController.swift */; }; 3BCE6D3C1CEB9E4D0080928C /* ThirdPartySearchAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCE6D3B1CEB9E4D0080928C /* ThirdPartySearchAlerts.swift */; }; - 3BF4B8E91D38497A00493393 /* BaseTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF4B8E81D38497A00493393 /* BaseTestCase.swift */; }; 3BF56D271CDBBE1F00AC4D75 /* SimpleToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF56D261CDBBE1F00AC4D75 /* SimpleToast.swift */; }; - 3BFE4B501D34673D00DDF53F /* ThirdPartySearchTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BFE4B4F1D34673D00DDF53F /* ThirdPartySearchTest.swift */; }; - 3D9CA9841EF456A8002434DD /* NightModeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9CA9831EF456A8002434DD /* NightModeTests.swift */; }; - 3D9CAA1C1EFCD655002434DD /* ClipBoardTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9CAA1B1EFCD655002434DD /* ClipBoardTests.swift */; }; - 3DEFED081F55EBE300F8620C /* TrackingProtectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DEFED071F55EBE300F8620C /* TrackingProtectionTests.swift */; }; 43017ECB278E0C6700CED011 /* RustMozillaAppServices.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 430407CD2CDE52F40091115A /* LibraryPanel.strings in Resources */ = {isa = PBXBuildFile; fileRef = 430407CB2CDE52F40091115A /* LibraryPanel.strings */; }; 43079D1F2C85D3BB0047C28D /* Bookmarks.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43079D1D2C85D3BB0047C28D /* Bookmarks.strings */; }; 43079D232C85D3BB0047C28D /* MainMenu.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43079D212C85D3BB0047C28D /* MainMenu.strings */; }; 4308A4E62A52E38C001D652E /* SelectCreditCard.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4308A4E42A52E38C001D652E /* SelectCreditCard.strings */; }; @@ -374,7 +499,6 @@ 433BADA429C8769800E34991 /* ErrorState.strings in Resources */ = {isa = PBXBuildFile; fileRef = 433BADA229C8769800E34991 /* ErrorState.strings */; }; 433F87CE2788EAB600693368 /* GCDWebServers in Frameworks */ = {isa = PBXBuildFile; productRef = 433F87CD2788EAB600693368 /* GCDWebServers */; }; 433F87D02788ECDD00693368 /* GCDWebServers in Frameworks */ = {isa = PBXBuildFile; productRef = 433F87CF2788ECDD00693368 /* GCDWebServers */; }; - 433F87D52788F01500693368 /* KIF in Frameworks */ = {isa = PBXBuildFile; productRef = 433F87D42788F01500693368 /* KIF */; }; 43446CF02412DDBE00F5C643 /* UpdateViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43446CEF2412DDBE00F5C643 /* UpdateViewModelTests.swift */; }; 4344BD7D2AEFC6490057F642 /* Shopping.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4344BD7B2AEFC6490057F642 /* Shopping.strings */; }; 4345441D26D2E52600D5EEAA /* SearchTermGroupsUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4345441C26D2E52600D5EEAA /* SearchTermGroupsUtility.swift */; }; @@ -421,7 +545,6 @@ 43A715402A2DF94F00DD5747 /* RememberCard.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43A7153E2A2DF94F00DD5747 /* RememberCard.strings */; }; 43A715432A2DF94F00DD5747 /* UpdateCard.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43A715412A2DF94F00DD5747 /* UpdateCard.strings */; }; 43A878172B838FF90039D6B7 /* PasswordAutofill.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43A878152B838FF90039D6B7 /* PasswordAutofill.strings */; }; - 43A878FC27AB498E0071C372 /* MozillaAppServices in Frameworks */ = {isa = PBXBuildFile; productRef = 43A878FB27AB498E0071C372 /* MozillaAppServices */; }; 43A878FD27AB4A110071C372 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43A878FE27AC39D30071C372 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43AB6FA225DC53D30016B015 /* GoogleTopSiteManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AB6F9C25DC53D20016B015 /* GoogleTopSiteManager.swift */; }; @@ -439,14 +562,11 @@ 43BDBBFE2752FA8600254DE4 /* LegacyTabCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43BDBBFD2752FA8600254DE4 /* LegacyTabCell.swift */; }; 43BE5809278BA9D700491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43BE580E278BABCF00491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; - 43BE5816278BE53E00491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43BE581B278BE5AE00491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43BE5820278BE68100491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; - 43BE5825278BE7EF00491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43BE5827278BE81500491291 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43C50B502A0BCAA800C6A134 /* FirefoxSync.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43C50B4E2A0BCAA800C6A134 /* FirefoxSync.strings */; }; 43C50B532A0BCAA800C6A134 /* Upgrade.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43C50B512A0BCAA800C6A134 /* Upgrade.strings */; }; - 43C6A47F27A0679300C79856 /* MappaMundi in Frameworks */ = {isa = PBXBuildFile; productRef = 43C6A47E27A0679300C79856 /* MappaMundi */; }; 43D00493296FC48F00CB0F31 /* CreditCardSettingsEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D00492296FC48F00CB0F31 /* CreditCardSettingsEmptyView.swift */; }; 43D16B7A29831C7F009F8279 /* CreditCardAutofillToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D16B7929831C7F009F8279 /* CreditCardAutofillToggle.swift */; }; 43D16B7C29831CD0009F8279 /* CreditCardItemRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D16B7B29831CD0009F8279 /* CreditCardItemRow.swift */; }; @@ -467,16 +587,13 @@ 43E6BE162C57AF7600C2152C /* Toolbar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43E6BE142C57AF7600C2152C /* Toolbar.strings */; }; 43F119002A52E42400C44C6C /* CustomizeFirefoxHome.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43F118FE2A52E42400C44C6C /* CustomizeFirefoxHome.strings */; }; 43F7952525795F69005AEE40 /* SearchTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F7952425795F69005AEE40 /* SearchTelemetry.swift */; }; - 43F92B3829E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = 43F92B3729E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js */; }; 43F93C2427A8681C009833D9 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43F93C2827A8683E009833D9 /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; 43FA499A29C875C0005062DB /* Alert.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43FA499829C875C0005062DB /* Alert.strings */; }; 43FA499D29C875C0005062DB /* EditCard.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43FA499B29C875C0005062DB /* EditCard.strings */; }; 43FA49A029C875C0005062DB /* Settings.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43FA499E29C875C0005062DB /* Settings.strings */; }; 43FA49A329C875C0005062DB /* SnackBar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43FA49A129C875C0005062DB /* SnackBar.strings */; }; - 45355B232A269E7100B1EA8E /* Autopush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45355B222A269E7100B1EA8E /* Autopush.swift */; }; 45355B242A269E7100B1EA8E /* Autopush.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45355B222A269E7100B1EA8E /* Autopush.swift */; }; - 45355B262A269EAC00B1EA8E /* PushConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45355B252A269EAC00B1EA8E /* PushConfiguration.swift */; }; 45355B272A269EAC00B1EA8E /* PushConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45355B252A269EAC00B1EA8E /* PushConfiguration.swift */; }; 4590912E2A2E4F7700061F0C /* AutopushTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4590912D2A2E4F7700061F0C /* AutopushTests.swift */; }; 4590912F2A2E558000061F0C /* RustMozillaAppServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; }; @@ -488,11 +605,6 @@ 45D5EDD1292D857000311934 /* TestSQLitePinnedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45D5EDCF292D854000311934 /* TestSQLitePinnedSites.swift */; }; 45D5EDD2292D89A200311934 /* PinnedSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A5BD95B2878AA74000FE773 /* PinnedSite.swift */; }; 4A59B58AD11B5EE1F80BBDEB /* TestHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A59BF410BBD9B3BE71F4C7C /* TestHistory.swift */; }; - 4F2A06BE26F8E46E0017DA05 /* TabCounterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F2A06BD26F8E46E0017DA05 /* TabCounterTests.swift */; }; - 4F514FD41ACD8F2C0022D7EA /* HistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F514FD31ACD8F2C0022D7EA /* HistoryTests.swift */; }; - 4F97573B1AFA6F37006ECC24 /* readerContent.html in Resources */ = {isa = PBXBuildFile; fileRef = 4F9757391AFA6F37006ECC24 /* readerContent.html */; }; - 4FB4AF7526E7E789005FDF91 /* HomeButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FB4AF7426E7E789005FDF91 /* HomeButtonTests.swift */; }; - 580B0C4221748CFE00448DF8 /* DataManagementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580B0C4121748CFE00448DF8 /* DataManagementTests.swift */; }; 59A68B280D62462B85CF57A4 /* HistoryPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59A6825233896FC846499289 /* HistoryPanel.swift */; }; 59A68D66379CFA85C4EAF00B /* TwoLineImageOverlayCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59A68B1F857A8638598A63A0 /* TwoLineImageOverlayCell.swift */; }; 59A68E0B4ABBF55E14819668 /* LegacyBookmarksPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59A6839879D615FC1C0D71CE /* LegacyBookmarksPanel.swift */; }; @@ -507,8 +619,6 @@ 5A31275828906422001F30FA /* BookmarksDelegateMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A31275728906422001F30FA /* BookmarksDelegateMock.swift */; }; 5A32C2B62AD8517200A9B5A4 /* MetricKitWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A32C2B52AD8517200A9B5A4 /* MetricKitWrapper.swift */; }; 5A37861429A2BFB9006B3A34 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A37861329A2BFB9006B3A34 /* Common */; }; - 5A37861929A2C337006B3A34 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 5A37861829A2C337006B3A34 /* Sentry */; }; - 5A37861D29A2C37C006B3A34 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A37861C29A2C37C006B3A34 /* Common */; }; 5A393F8D2C8A410900D2628D /* SiteImageView in Frameworks */ = {isa = PBXBuildFile; productRef = 5A393F8C2C8A410900D2628D /* SiteImageView */; }; 5A3A2A0D287F742C00B79EAC /* BackgroundSyncUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A3A2A0C287F742C00B79EAC /* BackgroundSyncUtility.swift */; }; 5A3A7DCE2886F7880065F81A /* BookmarksDataAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A3A7DCD2886F7880065F81A /* BookmarksDataAdaptor.swift */; }; @@ -527,7 +637,6 @@ 5A70EF10295DFD4900790249 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A70EF0F295DFD4900790249 /* Common */; }; 5A70EF12295DFD6400790249 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A70EF11295DFD6400790249 /* Common */; }; 5A70EF14295DFD7C00790249 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A70EF13295DFD7C00790249 /* Common */; }; - 5A70EF16295DFD9000790249 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A70EF15295DFD9000790249 /* Common */; }; 5A70EF19295E2E1600790249 /* DependencyHelperMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70EF18295E2E1600790249 /* DependencyHelperMock.swift */; }; 5A70EF1D295E3C3500790249 /* TestSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70EF1C295E3C3500790249 /* TestSetup.swift */; }; 5A70EF1F295E3DFC00790249 /* UnitTestAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A70EF1E295E3DFC00790249 /* UnitTestAppDelegate.swift */; }; @@ -541,7 +650,6 @@ 5A871490292EA3910039A5BD /* SiteImageView in Frameworks */ = {isa = PBXBuildFile; productRef = 5A87148F292EA3910039A5BD /* SiteImageView */; }; 5A8FD0EC293A7D5E00333AA7 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5A8FD0EB293A7D5E00333AA7 /* SnapKit */; }; 5A8FD0EE293A7D6D00333AA7 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5A8FD0ED293A7D6D00333AA7 /* SnapKit */; }; - 5A8FD0F2293A7D9000333AA7 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5A8FD0F1293A7D9000333AA7 /* SnapKit */; }; 5A984D312C89FD88007938C9 /* SiteImageView in Frameworks */ = {isa = PBXBuildFile; productRef = 5A984D302C89FD88007938C9 /* SiteImageView */; }; 5A984D342C8A31C6007938C9 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 5A984D332C8A31C6007938C9 /* Kingfisher */; }; 5A984D362C8A3407007938C9 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 5A984D352C8A3407007938C9 /* Kingfisher */; }; @@ -554,12 +662,16 @@ 5A9F83442B2B8CE900272819 /* TabPeekModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9F83432B2B8CE900272819 /* TabPeekModel.swift */; }; 5A9FF8492942454600DF9FBB /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 5A9FF8482942454600DF9FBB /* Common */; }; 5AA0CC662A4B8F6100014E2A /* PasswordManagerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA0CC652A4B8F6100014E2A /* PasswordManagerCoordinator.swift */; }; - 5AA1D8272BC09ECB00957516 /* TabTrayAnimationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA1D8262BC09ECB00957516 /* TabTrayAnimationQueue.swift */; }; 5AA75A652A46274A00533F8D /* MockThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AA75A622A46272000533F8D /* MockThemeManager.swift */; }; 5AB4237C28A1947A003BC40C /* MockNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AB4237B28A1947A003BC40C /* MockNotificationCenter.swift */; }; 5AB4237E28A2BA9C003BC40C /* HistoryHighlightsDataAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AB4237D28A2BA9C003BC40C /* HistoryHighlightsDataAdaptor.swift */; }; - 5AC40329291AFBDB002BF91C /* DispatchQueueHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83821FF1FC7961D00303C12 /* DispatchQueueHelper.swift */; }; 5AC7110729F822E60011ED11 /* MockTabSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC7110629F822E60011ED11 /* MockTabSessionStore.swift */; }; + 5AD3B6742CF625B000AFA1FE /* DefaultBrowserUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD3B6732CF625A300AFA1FE /* DefaultBrowserUtil.swift */; }; + 5AD3B6782CF650DF00AFA1FE /* DefaultBrowserUitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD3B6772CF650D000AFA1FE /* DefaultBrowserUitTests.swift */; }; + 5AD3B67A2CF653A600AFA1FE /* MockLocale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD3B6792CF653A200AFA1FE /* MockLocale.swift */; }; + 5AD3B67C2CF65DE600AFA1FE /* LocaleInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD3B67B2CF65DE300AFA1FE /* LocaleInterface.swift */; }; + 5AD3B67E2CF665B400AFA1FE /* UIApplicationInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD3B67D2CF665AE00AFA1FE /* UIApplicationInterface.swift */; }; + 5AD3B6802CF6674F00AFA1FE /* MockUIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AD3B67F2CF6674B00AFA1FE /* MockUIApplication.swift */; }; 5AE371842A4DD6F50092A760 /* PasswordManagerListViewControllerSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AE371832A4DD6F50092A760 /* PasswordManagerListViewControllerSpy.swift */; }; 5AE371852A4DD6FE0092A760 /* PasswordManagerCoordinatorDelegateMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AE371812A4DD0D70092A760 /* PasswordManagerCoordinatorDelegateMock.swift */; }; 5AE371872A4E11750092A760 /* AboutSettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AE371862A4E11750092A760 /* AboutSettingsDelegate.swift */; }; @@ -571,18 +683,9 @@ 5F130D2E2483508E00B0F7D0 /* FxAWebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F130D2D2483508E00B0F7D0 /* FxAWebViewModel.swift */; }; 5FA2233C27F74071005B3D87 /* Glean in Frameworks */ = {isa = PBXBuildFile; productRef = 5FA2233B27F74071005B3D87 /* Glean */; }; 5FC276552894AEFF00AF2721 /* LibraryPanelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D30B101D1AA7F9C600C01CA3 /* LibraryPanelHelper.swift */; }; - 5FF4AF5C2CC69CC900BABC62 /* TodayWidgetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FF4AF5B2CC69CC900BABC62 /* TodayWidgetTests.swift */; }; - 6025B109267B6BB300F59F6B /* NoSearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B107267B6BB300F59F6B /* NoSearchResultCell.swift */; }; - 6025B10A267B6BB300F59F6B /* SelectPasswordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B108267B6BB300F59F6B /* SelectPasswordCell.swift */; }; - 6025B10C267B6BEA00F59F6B /* LoginRecordExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B10B267B6BEA00F59F6B /* LoginRecordExtension.swift */; }; 6025B10D267B6C5400F59F6B /* LoginRecordExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B10B267B6BEA00F59F6B /* LoginRecordExtension.swift */; }; - 6025B10E267B6C7F00F59F6B /* LoginRecordExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B10B267B6BEA00F59F6B /* LoginRecordExtension.swift */; }; 6025B10F267B6C7F00F59F6B /* LoginRecordExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B10B267B6BEA00F59F6B /* LoginRecordExtension.swift */; }; - 6025B111267B6EE100F59F6B /* CredentialWelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6025B110267B6EE100F59F6B /* CredentialWelcomeViewController.swift */; }; - 602A2B9827F6256200C3CB78 /* FxNimbus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 455880A027B42F720078DEBB /* FxNimbus.swift */; }; 602B3D6729B0E1DB0066DEF8 /* ConversionValueUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 602B3D6629B0E1DB0066DEF8 /* ConversionValueUtil.swift */; }; - 60CE80C12667780D004026C7 /* CredentialListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60CE80C02667780C004026C7 /* CredentialListPresenter.swift */; }; - 60D71AEC26AAF45E00355588 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60D71AEB26AAF45E00355588 /* UIColorExtension.swift */; }; 630FE1342C7FB42500D9D6B2 /* NativeErrorPageMockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630FE1302C7FB42500D9D6B2 /* NativeErrorPageMockModel.swift */; }; 630FE1352C7FB42500D9D6B2 /* NativeErrorPageViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 630FE1322C7FB42500D9D6B2 /* NativeErrorPageViewControllerTests.swift */; }; 63306D3921103EAE00F25400 /* LegacySavedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63306D3821103EAE00F25400 /* LegacySavedTab.swift */; }; @@ -597,7 +700,6 @@ 6F994AFD2AF56234008B8112 /* NetworkUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F994AFC2AF56234008B8112 /* NetworkUtilsTests.swift */; }; 742A56391D80B54A00BDB803 /* PhotonActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 742A56381D80B54A00BDB803 /* PhotonActionSheet.swift */; }; 742BD99E2A13AC9000BA6B15 /* OnboardingInstructionPopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 742BD99D2A13AC9000BA6B15 /* OnboardingInstructionPopupViewController.swift */; }; - 744B0FFE1B4F172E00100422 /* ToolbarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 744B0FFD1B4F172E00100422 /* ToolbarTests.swift */; }; 744ED5611DBFEB8D00A2B5BE /* MailtoLinkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 744ED5601DBFEB8D00A2B5BE /* MailtoLinkHandler.swift */; }; 745DAB301CDAAFAA00D44181 /* RecentlyClosedTabsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 745DAB2F1CDAAFAA00D44181 /* RecentlyClosedTabsPanel.swift */; }; 7479B4EF1C5306A200DF000B /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7479B4ED1C5306A200DF000B /* Reachability.swift */; }; @@ -610,22 +712,15 @@ 74E36D781B71323500D69DA1 /* SettingsContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74E36D771B71323500D69DA1 /* SettingsContentViewController.swift */; }; 74F80D342A0A52D700013C3D /* PrivacyPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F80D332A0A52D700013C3D /* PrivacyPolicyViewController.swift */; }; 781C19CF2A780BEC0000DF46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 781C19CE2A780BEC0000DF46 /* Common */; }; - 782B0A362AB41DFC0049EE1A /* FakespotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B0A352AB41DFC0049EE1A /* FakespotTests.swift */; }; - 787EDD852943EE75002B93AE /* JumpBackInTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787EDD832943EE75002B93AE /* JumpBackInTests.swift */; }; - 78F28FC02CB81FDF00DA862E /* InactiveTabsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78F28FBF2CB81FDF00DA862E /* InactiveTabsTest.swift */; }; - 78FE1E892B040E7000338465 /* FirefoxSuggestTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78FE1E872B040E7000338465 /* FirefoxSuggestTest.swift */; }; 7AC7E0502C160FF800051D4D /* ReaderPanelEmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC7E04F2C160FF800051D4D /* ReaderPanelEmptyStateView.swift */; }; 7ADC1D192C27D35B003ED924 /* ActionProviderBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADC1D182C27D35B003ED924 /* ActionProviderBuilder.swift */; }; 7B10AA9F1E3A15020002DD08 /* DataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B10AA9E1E3A15020002DD08 /* DataExtensions.swift */; }; 7B10AABB1E3A1F650002DD08 /* URLRequestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B10AABA1E3A1F650002DD08 /* URLRequestExtensions.swift */; }; 7B2142FE1E5E055000CDD3FC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7B2142FC1E5E055000CDD3FC /* InfoPlist.strings */; }; - 7B24DC9C1B67B3590005766B /* ClearPrivateDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B24DC9B1B67B3590005766B /* ClearPrivateDataTests.swift */; }; 7B844E3D1BBDDB9D00E733A2 /* ChevronView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B844E3C1BBDDB9D00E733A2 /* ChevronView.swift */; }; 7B8A47F61D01D3B400C07734 /* PassKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B8A47F51D01D3B400C07734 /* PassKit.framework */; }; 7BA0601B1C0F4DE200DFADB6 /* LegacyTabPeekViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA0601A1C0F4DE200DFADB6 /* LegacyTabPeekViewController.swift */; }; - 7BA8D1C71BA037F500C8AE9E /* DownloadHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA8D1C61BA037F500C8AE9E /* DownloadHelper.swift */; }; 7BEB64441C7345600092C02E /* L10nSuite2SnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3632D31C2983F000D12AF9 /* L10nSuite2SnapshotTests.swift */; }; - 7BEB64451C7345600092C02E /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B60B0071BDE3AE10090C984 /* SnapshotHelper.swift */; }; 7BEFC6801BFF68C30059C952 /* QuickActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEFC67F1BFF68C30059C952 /* QuickActions.swift */; }; 81020C922BB5AFA2007B8481 /* OnboardingMultipleChoiceButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81020C912BB5AFA2007B8481 /* OnboardingMultipleChoiceButtonView.swift */; }; 81020C942BB5B026007B8481 /* OnboardingMultipleChoiceButtonViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81020C932BB5B026007B8481 /* OnboardingMultipleChoiceButtonViewModel.swift */; }; @@ -664,11 +759,7 @@ 8A03309528C2653600286539 /* LegacyTabFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A03309428C2653600286539 /* LegacyTabFileManager.swift */; }; 8A03309828C2691800286539 /* LegacyTabFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A03309428C2653600286539 /* LegacyTabFileManager.swift */; }; 8A04136928258DF600D20B10 /* SponsoredTileTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A04136828258DF600D20B10 /* SponsoredTileTelemetry.swift */; }; - 8A04136B2825ABEA00D20B10 /* SponsoredTileTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A04136A2825ABEA00D20B10 /* SponsoredTileTelemetryTests.swift */; }; - 8A05B0052A69A0C40011B622 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 8A05B0042A69A0C40011B622 /* Common */; }; - 8A05B0072A69A25C0011B622 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 8A05B0062A69A25C0011B622 /* Common */; }; 8A0727462B4890B50071BB9F /* WebviewTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0727452B4890B50071BB9F /* WebviewTelemetry.swift */; }; - 8A0727492B4898D20071BB9F /* WebviewTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A0727472B4898750071BB9F /* WebviewTelemetryTests.swift */; }; 8A07910F278F62F2005529CB /* AdjustHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A07910E278F62F2005529CB /* AdjustHelper.swift */; }; 8A08EC6227EBDCA400E119C7 /* iAd.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4B334871BBF23F3004E2BFF /* iAd.framework */; }; 8A08EC6427EBDCAD00E119C7 /* AdServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A08EC6327EBDCAC00E119C7 /* AdServices.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; @@ -705,34 +796,17 @@ 8A1E3BE328CBACDD003388C4 /* SponsoredContentFilterUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1E3BE128CBACD7003388C4 /* SponsoredContentFilterUtilityTests.swift */; }; 8A1E93EA2A3CDC6100DD540A /* BaseCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1E93E92A3CDC6100DD540A /* BaseCoordinator.swift */; }; 8A1F6C2F2BC5A62400DA6F86 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8ACD3B972BA8EA8300E73E9A /* PrivacyInfo.xcprivacy */; }; - 8A1F6C302BC5A6B600DA6F86 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8ACD3B972BA8EA8300E73E9A /* PrivacyInfo.xcprivacy */; }; - 8A1F6C312BC5A6BE00DA6F86 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8ACD3B972BA8EA8300E73E9A /* PrivacyInfo.xcprivacy */; }; 8A1F6C322BC5A6C400DA6F86 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8ACD3B972BA8EA8300E73E9A /* PrivacyInfo.xcprivacy */; }; 8A1F6C332BC5A6D100DA6F86 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8ACD3B972BA8EA8300E73E9A /* PrivacyInfo.xcprivacy */; }; 8A2783F1275FFDC50080D29D /* KeyboardPressesHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2783F0275FFDC50080D29D /* KeyboardPressesHandler.swift */; }; 8A2825352760399B00395E66 /* KeyboardPressesHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2825342760399B00395E66 /* KeyboardPressesHandlerTests.swift */; }; 8A285B08294A5D4C00149B0F /* HomepageHeroImageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A285B07294A5D4C00149B0F /* HomepageHeroImageViewModel.swift */; }; 8A28C628291028870078A81A /* CanRemoveQuickActionBookmarkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A28C627291028870078A81A /* CanRemoveQuickActionBookmarkTests.swift */; }; - 8A2B1A5D28216C4D0061216B /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 8A2B1A5A28216C4C0061216B /* Debug.xcconfig */; }; - 8A2B1A5E28216C4D0061216B /* Common.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 8A2B1A5B28216C4C0061216B /* Common.xcconfig */; }; - 8A2B1A5F28216C4D0061216B /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 8A2B1A5C28216C4D0061216B /* Release.xcconfig */; }; 8A2D593E27DC0AA100713EC9 /* TopSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2D593D27DC0AA100713EC9 /* TopSite.swift */; }; 8A2DAD4B2CC02AA00067ECD0 /* LabelButtonHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A2DAD4A2CC02AA00067ECD0 /* LabelButtonHeaderView.swift */; }; 8A3233FC286270CF003E1C33 /* FxBookmarkNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3233FB286270CF003E1C33 /* FxBookmarkNode.swift */; }; 8A3233FE28627446003E1C33 /* LocalDesktopFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3233FD28627446003E1C33 /* LocalDesktopFolder.swift */; }; - 8A32DD5028B419B300D57C60 /* HomepageMessageCardViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A32DD4F28B419B300D57C60 /* HomepageMessageCardViewModelTests.swift */; }; - 8A33221F27DFE318008F809E /* TopSitesDataAdaptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A33221E27DFE318008F809E /* TopSitesDataAdaptorTests.swift */; }; 8A33222227DFE658008F809E /* NimbusMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A33222127DFE658008F809E /* NimbusMock.swift */; }; - 8A3345612BA499B7008C52AB /* disconnect-block-fingerprinting.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A3345572BA499B6008C52AB /* disconnect-block-fingerprinting.json */; }; - 8A3345622BA499B7008C52AB /* disconnect-block-advertising.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A3345582BA499B6008C52AB /* disconnect-block-advertising.json */; }; - 8A3345632BA499B7008C52AB /* disconnect-block-cookies-content.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A3345592BA499B6008C52AB /* disconnect-block-cookies-content.json */; }; - 8A3345642BA499B7008C52AB /* disconnect-block-analytics.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A33455A2BA499B6008C52AB /* disconnect-block-analytics.json */; }; - 8A3345652BA499B7008C52AB /* disconnect-block-cookies-advertising.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A33455B2BA499B7008C52AB /* disconnect-block-cookies-advertising.json */; }; - 8A3345662BA499B7008C52AB /* disconnect-block-content.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A33455C2BA499B7008C52AB /* disconnect-block-content.json */; }; - 8A3345672BA499B7008C52AB /* disconnect-block-cookies-analytics.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A33455D2BA499B7008C52AB /* disconnect-block-cookies-analytics.json */; }; - 8A3345682BA499B7008C52AB /* disconnect-block-social.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A33455E2BA499B7008C52AB /* disconnect-block-social.json */; }; - 8A3345692BA499B7008C52AB /* disconnect-block-cookies-social.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A33455F2BA499B7008C52AB /* disconnect-block-cookies-social.json */; }; - 8A33456A2BA499B7008C52AB /* disconnect-block-cryptomining.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A3345602BA499B7008C52AB /* disconnect-block-cryptomining.json */; }; 8A359EF32A1FD449004A5BB7 /* AdjustWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A359EF22A1FD449004A5BB7 /* AdjustWrapper.swift */; }; 8A359EF62A1FE840004A5BB7 /* MockAdjustWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A359EF52A1FE840004A5BB7 /* MockAdjustWrapper.swift */; }; 8A36AC2C2886F27F00CDC0AD /* MockTabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A36AC2B2886F27F00CDC0AD /* MockTabManager.swift */; }; @@ -765,7 +839,6 @@ 8A454D302CB81697009436D9 /* PocketMiddleware.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A454D2F2CB81697009436D9 /* PocketMiddleware.swift */; }; 8A454D322CB8170D009436D9 /* PocketManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A454D312CB8170D009436D9 /* PocketManager.swift */; }; 8A454D362CB86993009436D9 /* PocketStoryState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A454D352CB86993009436D9 /* PocketStoryState.swift */; }; - 8A454D372CB86B86009436D9 /* PocketStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A454D332CB85C7D009436D9 /* PocketStateTests.swift */; }; 8A4593C72BF7BECA002758DE /* MicrosurveyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4593C32BF7BEC9002758DE /* MicrosurveyTableViewCell.swift */; }; 8A4593C82BF7BECA002758DE /* MicrosurveyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4593C42BF7BECA002758DE /* MicrosurveyViewController.swift */; }; 8A4593C92BF7BECA002758DE /* MicrosurveyTableHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4593C52BF7BECA002758DE /* MicrosurveyTableHeaderView.swift */; }; @@ -779,7 +852,6 @@ 8A4AC0EB28C929D700439F83 /* URLSessionDataTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4AC0E928C929D700439F83 /* URLSessionDataTaskProtocol.swift */; }; 8A4AC0EC28C929D700439F83 /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4AC0EA28C929D700439F83 /* URLSessionProtocol.swift */; }; 8A4EA0D12C010BE700E4E4F1 /* MicrosurveySurfaceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4EA0D02C010BE700E4E4F1 /* MicrosurveySurfaceManager.swift */; }; - 8A4EA0D42C01100200E4E4F1 /* MicrosurveySurfaceManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4EA0D22C010BF800E4E4F1 /* MicrosurveySurfaceManagerTests.swift */; }; 8A4EA0D92C01127C00E4E4F1 /* MicrosurveyMockModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4EA0D72C01125100E4E4F1 /* MicrosurveyMockModel.swift */; }; 8A4EA0DB2C0117CD00E4E4F1 /* MobileMessageSurfaceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4EA0DA2C0117CD00E4E4F1 /* MobileMessageSurfaceProtocol.swift */; }; 8A4EA0DD2C0117F200E4E4F1 /* MicrosurveyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4EA0DC2C0117F200E4E4F1 /* MicrosurveyModel.swift */; }; @@ -825,11 +897,8 @@ 8A720C602A4C8B700003018A /* SharedSettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A720C5F2A4C8B700003018A /* SharedSettingsDelegate.swift */; }; 8A720C622A4CBB370003018A /* SupportSettingsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A720C612A4CBB370003018A /* SupportSettingsDelegate.swift */; }; 8A7368AD27924AAF005D7704 /* CanRemoveQuickActionBookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7368AC27924AAF005D7704 /* CanRemoveQuickActionBookmark.swift */; }; - 8A75F1B828B558E20054E34D /* MessageCardDataAdaptorImplementationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A75F1B728B558E20054E34D /* MessageCardDataAdaptorImplementationTests.swift */; }; 8A7653BD28A2C61D00924ABF /* PocketDataAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7653BC28A2C61D00924ABF /* PocketDataAdaptor.swift */; }; 8A7653BF28A2C92600924ABF /* PocketStandardCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7653BE28A2C92600924ABF /* PocketStandardCellViewModel.swift */; }; - 8A7653C228A2E57D00924ABF /* PocketDataAdaptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7653C128A2E57D00924ABF /* PocketDataAdaptorTests.swift */; }; - 8A7653C528A2E69100924ABF /* MockPocketAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7653C328A2E68B00924ABF /* MockPocketAPI.swift */; }; 8A76B01629F6EB3900A82607 /* ScreenshotService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A76B01529F6EB3900A82607 /* ScreenshotService.swift */; }; 8A7A26E129D4785900EA76F1 /* MockRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7A26E029D4785900EA76F1 /* MockRouter.swift */; }; 8A7A26E329D4ACF300EA76F1 /* SceneCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7A26E229D4ACF300EA76F1 /* SceneCoordinatorTests.swift */; }; @@ -843,7 +912,6 @@ 8A7D08E32CAAF7C30035999C /* HomepageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A7D08E22CAAF7C30035999C /* HomepageViewController.swift */; }; 8A7D1AC52BA3542600162F4B /* splashScreen.json in Resources */ = {isa = PBXBuildFile; fileRef = 8A7D1AC42BA3542600162F4B /* splashScreen.json */; }; 8A8158CB2C2C77B000281F72 /* MicrosurveyTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A8158CA2C2C77B000281F72 /* MicrosurveyTelemetry.swift */; }; - 8A827E302C20C7F5008D5E3C /* MicrosurveyMiddlewareTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A827E2E2C20C55E008D5E3C /* MicrosurveyMiddlewareTests.swift */; }; 8A827E322C20C8AE008D5E3C /* MockMicrosurveySurfaceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A827E312C20C8AE008D5E3C /* MockMicrosurveySurfaceManager.swift */; }; 8A827E362C20CB5B008D5E3C /* MicrosurveyPromptMiddlewareTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A827E342C20CB44008D5E3C /* MicrosurveyPromptMiddlewareTests.swift */; }; 8A832A9029DC96C50025D5DD /* LaunchScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A832A8F29DC96C50025D5DD /* LaunchScreenView.swift */; }; @@ -879,25 +947,20 @@ 8A93F87229D3A5AD004159D9 /* BrowserCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A93F87129D3A5AD004159D9 /* BrowserCoordinator.swift */; }; 8A93F87429D3A5C1004159D9 /* LaunchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A93F87329D3A5C1004159D9 /* LaunchCoordinator.swift */; }; 8A95FF642B1E969E00AC303D /* TelemetryContextualIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */; }; - 8A95FF672B1E97A800AC303D /* TelemetryContextualIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */; }; 8A96C4BB28F9E7B300B75884 /* XCTestCaseRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */; }; 8A97E6EA2C58487900F94793 /* AddressLocaleFeatureValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */; }; 8A97E6EF2C584C4900F94793 /* AddressLocaleFeatureValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */; }; 8A9AC465276CEC4E0047F5B0 /* JumpBackInCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9AC464276CEC4E0047F5B0 /* JumpBackInCell.swift */; }; - 8A9AC46B276D11280047F5B0 /* PocketViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9AC46A276D11280047F5B0 /* PocketViewModel.swift */; }; 8A9B87AD2C1B39100042B894 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9B87AC2C1B39100042B894 /* SearchViewModel.swift */; }; 8A9B87AF2C1B39EA0042B894 /* SearchListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9B87AE2C1B39EA0042B894 /* SearchListSection.swift */; }; 8A9E46BD2A6599E5003327D4 /* MockStatusBarScrollDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9E46BC2A6599E5003327D4 /* MockStatusBarScrollDelegate.swift */; }; 8A9F0B5627C595F300FE09AE /* ImageIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F0B5527C595F300FE09AE /* ImageIdentifiers.swift */; }; 8A9F0B5727C59E1700FE09AE /* ImageIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F0B5527C595F300FE09AE /* ImageIdentifiers.swift */; }; - 8A9F0B5827C59E1800FE09AE /* ImageIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F0B5527C595F300FE09AE /* ImageIdentifiers.swift */; }; 8A9F0B5927C5A2AB00FE09AE /* ImageIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F0B5527C595F300FE09AE /* ImageIdentifiers.swift */; }; 8AA020EF2B9A37E500771DE0 /* NimbusSplashScreenFeatureLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA020EE2B9A37E500771DE0 /* NimbusSplashScreenFeatureLayer.swift */; }; 8AA0A6632CAC40AA00AC7EB3 /* HomepageDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA0A6622CAC40AA00AC7EB3 /* HomepageDiffableDataSource.swift */; }; 8AA0A6652CAC6B7100AC7EB3 /* HomepageSectionLayoutProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA0A6642CAC6B7100AC7EB3 /* HomepageSectionLayoutProvider.swift */; }; 8AA0A6682CAC747500AC7EB3 /* HomepageDiffableDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA0A6662CAC745300AC7EB3 /* HomepageDiffableDataSourceTests.swift */; }; - 8AA6ADB52742B567004EEE23 /* TelemetryWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA6ADB42742B567004EEE23 /* TelemetryWrapperTests.swift */; }; - 8AA7347B28AEDB3100443D24 /* PocketViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA7347A28AEDB3100443D24 /* PocketViewModelTests.swift */; }; 8AA8389D2CA2FEFC003FA256 /* StoreTestUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AA8389C2CA2FEFC003FA256 /* StoreTestUtility.swift */; }; 8AAAB0592C1B7240008830B3 /* MockRustFirefoxSuggest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB0582C1B723F008830B3 /* MockRustFirefoxSuggest.swift */; }; 8AAAB05B2C1B7268008830B3 /* ClientTabsSearchWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB05A2C1B7268008830B3 /* ClientTabsSearchWrapper.swift */; }; @@ -916,7 +979,6 @@ 8AAEBA0B2BF53AF6000C02B5 /* MicrosurveyStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAEBA092BF53AB5000C02B5 /* MicrosurveyStateTests.swift */; }; 8AB30EC82B6C038600BD9A9B /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 8AB30EC72B6C038600BD9A9B /* Lottie */; }; 8AB30ECA2B6C03C700BD9A9B /* DataClearanceAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB30EC92B6C03C700BD9A9B /* DataClearanceAnimation.swift */; }; - 8AB5958828413F6C0090F4AE /* BookmarksCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB5958728413F6C0090F4AE /* BookmarksCell.swift */; }; 8AB5958A284145B30090F4AE /* HomepageSectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB59589284145B30090F4AE /* HomepageSectionHandler.swift */; }; 8AB8571D27D929350075C173 /* TopSitesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8571C27D929350075C173 /* TopSitesViewModel.swift */; }; 8AB8571F27D931B40075C173 /* EmptyTopSiteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8571E27D931B40075C173 /* EmptyTopSiteCell.swift */; }; @@ -925,8 +987,6 @@ 8AB8572E27D94A1A0075C173 /* UXSizeClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8572D27D94A1A0075C173 /* UXSizeClass.swift */; }; 8AB8573027D94CAD0075C173 /* HomepageViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8572F27D94CAD0075C173 /* HomepageViewModelProtocol.swift */; }; 8AB8573727D951640075C173 /* HomeLogoHeaderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8573627D951640075C173 /* HomeLogoHeaderViewModel.swift */; }; - 8AB8574127D9630E0075C173 /* LegacyTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9A179A20E69A7E00B12184 /* LegacyTheme.swift */; }; - 8AB8574227D963290075C173 /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2816EFFF1B33E05400522243 /* UIConstants.swift */; }; 8AB8574627D97CB00075C173 /* HomepageContextMenuProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574527D97CB00075C173 /* HomepageContextMenuProtocol.swift */; }; 8AB8574827D97CD40075C173 /* HomePanelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574727D97CD40075C173 /* HomePanelType.swift */; }; 8AB8574A27D97CE90075C173 /* HomePanelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574927D97CE90075C173 /* HomePanelDelegate.swift */; }; @@ -938,14 +998,12 @@ 8ABA9C8E28931288002C0077 /* JumpBackInDataAdaptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABA9C8A28931207002C0077 /* JumpBackInDataAdaptorTests.swift */; }; 8ABC5AEE284532C900FEA552 /* PocketDiscoverCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABC5AED284532C900FEA552 /* PocketDiscoverCell.swift */; }; 8ABCFEA32B45C36100C2988A /* PrivateBrowsingTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABCFEA22B45C36100C2988A /* PrivateBrowsingTelemetry.swift */; }; - 8ABCFEA62B45CB4C00C2988A /* PrivateBrowsingTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABCFEA42B45CAC300C2988A /* PrivateBrowsingTelemetryTests.swift */; }; 8ABDBAA62CB6BF6600B51F63 /* HomepageHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABDBAA52CB6BF6600B51F63 /* HomepageHeaderCell.swift */; }; 8ABE9F1A2CB45B4B0080E1DF /* RemotePasswordRules.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AF4E7702C45B98F00BAD91C /* RemotePasswordRules.json */; }; 8ABE9F1B2CB4620A0080E1DF /* RemoteSettingsFetchConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AF4E76D2C41D86100BAD91C /* RemoteSettingsFetchConfig.json */; }; 8ABE9F1D2CB462730080E1DF /* RemoteSettingsFetchConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABE9F1C2CB462730080E1DF /* RemoteSettingsFetchConfigTests.swift */; }; 8ABE9F1E2CB462CA0080E1DF /* RemoteSettingsFetchConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = 8AF4E76D2C41D86100BAD91C /* RemoteSettingsFetchConfig.json */; }; 8AC1065F28D0CD700013263A /* OpenQLPreviewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC1065E28D0CD700013263A /* OpenQLPreviewHelper.swift */; }; - 8AC225662B6D403200CDA7FD /* HomepageTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC225642B6D3FA400CDA7FD /* HomepageTelemetryTests.swift */; }; 8AC5D55F28BFE6C8001F6F7F /* Presenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC5D55E28BFE6C8001F6F7F /* Presenter.swift */; }; 8ACA8F74291987AE00D3075D /* AccountSyncHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACA8F73291987AE00D3075D /* AccountSyncHandlerTests.swift */; }; 8ACA8F7629198D6400D3075D /* ThrottlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ACA8F7529198D6400D3075D /* ThrottlerTests.swift */; }; @@ -953,7 +1011,6 @@ 8AD0110D2CB42FE700368BF3 /* HeaderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD0110C2CB42FE700368BF3 /* HeaderState.swift */; }; 8AD0110F2CB4314B00368BF3 /* HeaderAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD0110E2CB4314B00368BF3 /* HeaderAction.swift */; }; 8AD08D1527E9198E00B8E907 /* TabsTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD08D1427E9198E00B8E907 /* TabsTelemetry.swift */; }; - 8AD08D1727E91AC800B8E907 /* TabsTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD08D1627E91AC800B8E907 /* TabsTelemetryTests.swift */; }; 8AD1980F27BEB3F100D64B0E /* PhotonActionSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD1980E27BEB3F100D64B0E /* PhotonActionSheetViewModel.swift */; }; 8AD248F12C0F735200500AB7 /* BehavioralTargetingEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD248F02C0F735200500AB7 /* BehavioralTargetingEvent.swift */; }; 8AD3271527E3B45D00EAF033 /* SponsoredTile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AD3271427E3B45D00EAF033 /* SponsoredTile.swift */; }; @@ -1000,7 +1057,6 @@ 8AE80BBE2891C21A00BC12EA /* JumpBackInSyncedTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE80BBD2891C21A00BC12EA /* JumpBackInSyncedTab.swift */; }; 8AEAD9F32C3D7B3E001A2C5A /* FeatureFlagsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F22C3D7B3E001A2C5A /* FeatureFlagsSettings.swift */; }; 8AEAD9F52C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F42C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift */; }; - 8AEAD9F92C3DB0CD001A2C5A /* MicrosurveyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAD9F62C3DB0BF001A2C5A /* MicrosurveyTests.swift */; }; 8AED23C527AC1F9500DE7E97 /* BaseContentStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AED23C427AC1F9500DE7E97 /* BaseContentStackView.swift */; }; 8AED868328CA3B3400351A50 /* BookmarkPanelViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AED868228CA3B3400351A50 /* BookmarkPanelViewModelTests.swift */; }; 8AEDB11529F9F00400F2A53B /* SceneContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AEDB11429F9F00400F2A53B /* SceneContainer.swift */; }; @@ -1079,7 +1135,6 @@ 9636D92A27F767EC00771F5E /* NimbusMessagingEvaluationUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636D92927F767EC00771F5E /* NimbusMessagingEvaluationUtility.swift */; }; 9636D92C27F9E50100771F5E /* GleanPlumbMessageStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636D92B27F9E50100771F5E /* GleanPlumbMessageStore.swift */; }; 9636D92E27F9E5D900771F5E /* GleanPlumbMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9636D92D27F9E5D900771F5E /* GleanPlumbMessage.swift */; }; - 9638332327E14ACC0011EEFC /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84266742728462900382274 /* AccessibilityIdentifiers.swift */; }; 964FA97528A1A8F20024BB3B /* ContextualHintEligibilityUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964FA97428A1A8F20024BB3B /* ContextualHintEligibilityUtility.swift */; }; 964FA97728A2A55C0024BB3B /* ContextualHintPrefsKeysProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964FA97628A2A55C0024BB3B /* ContextualHintPrefsKeysProvider.swift */; }; 9658143C29FAB610007339BD /* CreditCardInputFieldHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9658143B29FAB610007339BD /* CreditCardInputFieldHelper.swift */; }; @@ -1087,7 +1142,6 @@ 965C3C942933A860006499ED /* LaunchSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C3C932933A860006499ED /* LaunchSessionProvider.swift */; }; 965C3C96293431FC006499ED /* MockLaunchSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C3C95293431FC006499ED /* MockLaunchSessionProvider.swift */; }; 965C3C9829343445006499ED /* MockAppSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C3C9729343445006499ED /* MockAppSessionManager.swift */; }; - 966206CD2698DE1E005C0A55 /* BookmarksViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966206CC2698DE1E005C0A55 /* BookmarksViewModel.swift */; }; 96666D0129969AF700A4029F /* URLCaching.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96666D0029969AF700A4029F /* URLCaching.swift */; }; 96666D0229969F7D00A4029F /* GeneralizedImageFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96A5F73729928B3700234E5F /* GeneralizedImageFetcher.swift */; }; 966B0DC82926F60500A85A7E /* UIResponder+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 966B0DC72926F60500A85A7E /* UIResponder+Extensions.swift */; }; @@ -1149,13 +1203,6 @@ ABEF80D12A24D2BE003F52C4 /* CreditCardBottomSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D02A24D2BE003F52C4 /* CreditCardBottomSheetViewModel.swift */; }; ABEF80D52A254185003F52C4 /* CreditCardBottomSheetFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D42A254185003F52C4 /* CreditCardBottomSheetFooterView.swift */; }; ABEF80D92A2F283E003F52C4 /* CreditCardBottomSheetViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEF80D82A2F283D003F52C4 /* CreditCardBottomSheetViewModelTests.swift */; }; - B10997432A97251D00CC8860 /* UrlBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10997422A97251D00CC8860 /* UrlBarTests.swift */; }; - B1158F2A2B5029F200AC9D70 /* URLValidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1158F292B5029F200AC9D70 /* URLValidationTests.swift */; }; - B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */; }; - B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15058802AA0A878008B7382 /* OpeningScreenTests.swift */; }; - B1664E9E2B163B7A005D4C71 /* CreditCardsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1664E9D2B163B7A005D4C71 /* CreditCardsTests.swift */; }; - B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */; }; - B1F90EC12BB3F6B600A4D431 /* ZoomingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F90EC02BB3F6B600A4D431 /* ZoomingTests.swift */; }; B236204B2B851FE1000B1DE7 /* AddressAutoFillBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204A2B851FE0000B1DE7 /* AddressAutoFillBottomSheetView.swift */; }; B236204D2B8673DE000B1DE7 /* AddressScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204C2B8673DE000B1DE7 /* AddressScrollView.swift */; }; B236204F2B86C56F000B1DE7 /* AddressAutofillCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204E2B86C56F000B1DE7 /* AddressAutofillCoordinator.swift */; }; @@ -1166,8 +1213,6 @@ B2981F8A2B71AD7A00132C1B /* AutofillAccessoryViewButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2981F892B71AD7A00132C1B /* AutofillAccessoryViewButtonItem.swift */; }; B2999FED2B044A5900F0FEC1 /* UnencryptedCreditCardFields.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FEC2B044A5900F0FEC1 /* UnencryptedCreditCardFields.swift */; }; B2999FEF2B044B4E00F0FEC1 /* RustAutofillEncryptionKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FEE2B044B4E00F0FEC1 /* RustAutofillEncryptionKeys.swift */; }; - B2999FF12B194A5800F0FEC1 /* CreditCardPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FF02B194A5800F0FEC1 /* CreditCardPayload.swift */; }; - B2999FF32B194A8300F0FEC1 /* FillCreditCardForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FF22B194A8300F0FEC1 /* FillCreditCardForm.swift */; }; B2999FF52B194AB200F0FEC1 /* FormAutofillHelperError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FF42B194AB200F0FEC1 /* FormAutofillHelperError.swift */; }; B2999FF72B194ADE00F0FEC1 /* FormAutofillPayloadType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2999FF62B194ADE00F0FEC1 /* FormAutofillPayloadType.swift */; }; B2DFB7DF2B619DB80004CEA5 /* AddressListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2DFB7DE2B619DB80004CEA5 /* AddressListViewModel.swift */; }; @@ -1181,8 +1226,6 @@ BA1C68BA2B7E9EA0000D9397 /* WKFrameInfoExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA1C68B92B7E9EA0000D9397 /* WKFrameInfoExtensionsTest.swift */; }; BA1C68BC2B7ED153000D9397 /* MockWebKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA1C68BB2B7ED153000D9397 /* MockWebKit.swift */; }; BA7A14842C2CCEB3008DF1D9 /* EditAddressWebViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7A14832C2CCEB3008DF1D9 /* EditAddressWebViewManager.swift */; }; - BA8E197F2BF2FB1900590B5F /* AddressFormManager.js in Resources */ = {isa = PBXBuildFile; fileRef = BA8E197E2BF2FB1900590B5F /* AddressFormManager.js */; }; - BC003F5E2B59F44600929ECB /* BrowserViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC003F5D2B59F44500929ECB /* BrowserViewControllerTests.swift */; }; BCFF93EE2AAA9F6E005B5B71 /* RustFirefoxSuggest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF93ED2AAA9C47005B5B71 /* RustFirefoxSuggest.swift */; }; BCFF93F02AABA55A005B5B71 /* BackgroundFirefoxSuggestIngestUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF93EF2AAB97A8005B5B71 /* BackgroundFirefoxSuggestIngestUtility.swift */; }; BCFF93F22AAF9688005B5B71 /* FirefoxSuggestSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCFF93F12AAF9688005B5B71 /* FirefoxSuggestSettings.swift */; }; @@ -1196,6 +1239,7 @@ BD57D9A729D4C42B00039394 /* ZoomLevelStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD57D9A629D4C42B00039394 /* ZoomLevelStoreTests.swift */; }; BD6B361E2B3C2511005E5345 /* CircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6B361D2B3C2511005E5345 /* CircularProgressView.swift */; }; BD6CC84229CDDA3400546A5D /* ZoomLevelStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6CC84129CDDA3400546A5D /* ZoomLevelStore.swift */; }; + BDD262562CE3AC8200DF2C62 /* PrivacyWindowHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD262552CE3AC8200DF2C62 /* PrivacyWindowHelper.swift */; }; C2200A6A2B7D148C00DC062A /* ContentBlockerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2200A692B7D148C00DC062A /* ContentBlockerTests.swift */; }; C22753402A3C9E1300B9C0D1 /* WebsiteDataManagementViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C227533F2A3C9E1300B9C0D1 /* WebsiteDataManagementViewModel.swift */; }; C2296FCC2A601C190046ECA6 /* IntensityVisualEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2296FCB2A601C190046ECA6 /* IntensityVisualEffectView.swift */; }; @@ -1227,7 +1271,6 @@ C40046FA1CF8E0B200B08303 /* BackForwardListAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40046F91CF8E0B200B08303 /* BackForwardListAnimator.swift */; }; C45F44691D087DB600CB7EF0 /* TopTabsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45F44681D087DB600CB7EF0 /* TopTabsViewController.swift */; }; C4E3983D1D21F1E7004E89BA /* TopTabCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E3983C1D21F1E7004E89BA /* TopTabCell.swift */; }; - C4E3984C1D21F2FD004E89BA /* TabTrayButtonExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E3984B1D21F2FD004E89BA /* TabTrayButtonExtensions.swift */; }; C4E398601D22C409004E89BA /* TopTabsLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E3985F1D22C409004E89BA /* TopTabsLayout.swift */; }; C4EFEECF1CEBB6F2009762A4 /* BackForwardTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EFEECE1CEBB6F2009762A4 /* BackForwardTableViewCell.swift */; }; C4F3B29A1CFCF93A00966259 /* ButtonToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F3B2991CFCF93A00966259 /* ButtonToast.swift */; }; @@ -1241,14 +1284,12 @@ C80E1A102A0943640025B9E1 /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80E1A0F2A0943640025B9E1 /* UIFont+Extension.swift */; }; C8124BB129D6F55400540B79 /* Route.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8124BB029D6F55400540B79 /* Route.swift */; }; C8163851268A0899004C7160 /* AddCredentialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8163850268A0899004C7160 /* AddCredentialViewController.swift */; }; - C818AD452A2100BA007F30BC /* OnboardingNotificationCardHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C818AD442A2100BA007F30BC /* OnboardingNotificationCardHelperTests.swift */; }; C81A8F2526D3ED1900EBA539 /* UIWindow+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81A8F2426D3ED1900EBA539 /* UIWindow+Extension.swift */; }; C81AC6B626160091007800C5 /* LegacyTabTrayViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81AC6B526160091007800C5 /* LegacyTabTrayViewModel.swift */; }; C81B78A4280752A20000C15F /* NimbusFeatureFlagLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81B78A3280752A20000C15F /* NimbusFeatureFlagLayer.swift */; }; C81C66C429F00D1000F6422F /* UserActivityRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81C66C329F00D1000F6422F /* UserActivityRouteTests.swift */; }; C82043852523DBF600740B71 /* Sync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2827315E1ABC9BE600AA1954 /* Sync.framework */; }; C820439A2523DC4500740B71 /* libStorage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FCAE21A1ABB51F800877008 /* libStorage.a */; }; - C82043AE2523DC8B00740B71 /* Sync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2827315E1ABC9BE600AA1954 /* Sync.framework */; }; C82043AF2523DC9600740B71 /* Sync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2827315E1ABC9BE600AA1954 /* Sync.framework */; }; C82043C32523DD6A00740B71 /* Sync.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 2827315E1ABC9BE600AA1954 /* Sync.framework */; }; C825E9832832A425006CB811 /* NimbusSearchBarLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C825E9822832A425006CB811 /* NimbusSearchBarLayer.swift */; }; @@ -1261,10 +1302,8 @@ C834ACD128D3ACA900203AD1 /* Blurrable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C834ACD028D3ACA900203AD1 /* Blurrable.swift */; }; C838FD5E289981240068F60B /* WallpaperURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C838FD5D289981240068F60B /* WallpaperURLProvider.swift */; }; C838FD612899A9BB0068F60B /* WallpaperURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C838FD5F2899A9390068F60B /* WallpaperURLProviderTests.swift */; }; - C83B7DD629BBB49D005565C2 /* SurveySurfaceManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C83B7DD429BBAF7E005565C2 /* SurveySurfaceManagerTests.swift */; }; C8417D222657F0600010B877 /* LibraryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8417D212657F0600010B877 /* LibraryViewModel.swift */; }; C84266752728462900382274 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84266742728462900382274 /* AccessibilityIdentifiers.swift */; }; - C84266772728462900382274 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84266742728462900382274 /* AccessibilityIdentifiers.swift */; }; C8445A14264428DC00B83F53 /* LibraryPanelViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8445A13264428DC00B83F53 /* LibraryPanelViewState.swift */; }; C8445AD126443C7F00B83F53 /* LibraryPanelViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8445AD026443C7F00B83F53 /* LibraryPanelViewStateTests.swift */; }; C84655E22887388F00861B4A /* Wallpaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84655E12887388F00861B4A /* Wallpaper.swift */; }; @@ -1311,30 +1350,21 @@ C869916328918C36007ACC5C /* WallpaperNetworkingModuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C869916228918C36007ACC5C /* WallpaperNetworkingModuleTests.swift */; }; C869916528918C8E007ACC5C /* WallpaperURLSessionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C869916428918C8E007ACC5C /* WallpaperURLSessionMock.swift */; }; C8741FE928C4D30F00030029 /* FileManagerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8741FE828C4D30F00030029 /* FileManagerInterface.swift */; }; - C874FB3A2660E8B900EBE86E /* CredentialProviderPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C874FB392660E8B900EBE86E /* CredentialProviderPresenter.swift */; }; - C874FC2826612BFF00EBE86E /* AlertControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C874FC2726612BFF00EBE86E /* AlertControllerView.swift */; }; - C874FC652661367900EBE86E /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C874FC642661367900EBE86E /* MainInterface.storyboard */; }; C877037A25222F30006E38EB /* Shared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288A2D861AB8B3260023ABC3 /* Shared.framework */; }; C877039625222FDC006E38EB /* Shared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288A2D861AB8B3260023ABC3 /* Shared.framework */; }; - C877039725222FE6006E38EB /* Shared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288A2D861AB8B3260023ABC3 /* Shared.framework */; }; C87703D225223EA5006E38EB /* Shared.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 288A2D861AB8B3260023ABC3 /* Shared.framework */; }; C87A121A28C2451A0097ED51 /* WallpaperMigrationUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87A121928C2451A0097ED51 /* WallpaperMigrationUtility.swift */; }; C87BE0A428A2ED3F00BAADF5 /* wallpaperNoLearnMoreURL.json in Resources */ = {isa = PBXBuildFile; fileRef = C87BE0A328A2ED3F00BAADF5 /* wallpaperNoLearnMoreURL.json */; }; C87D8B802818333F00A6307D /* NimbusManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87D8B7F2818333F00A6307D /* NimbusManager.swift */; }; - C87DF962267246730097E707 /* photon-colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C49854D206173C800893DAE /* photon-colors.swift */; }; C87DF9DB267247190097E707 /* UIConstants+BottomInset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87DF9DA267247190097E707 /* UIConstants+BottomInset.swift */; }; C88012232A40E38D00F4D1D6 /* IntroViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88012222A40E38D00F4D1D6 /* IntroViewControllerTests.swift */; }; C889569A27E8D1AC00E3779E /* LegacyInactiveTabHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C889569927E8D1AC00E3779E /* LegacyInactiveTabHeader.swift */; }; - C889D7CE2858C4B500121E1D /* ContextMenuHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C889D7CD2858C4B500121E1D /* ContextMenuHelperTests.swift */; }; C889D7D52858CD8800121E1D /* HistoryHighlightsTestEntryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C889D7D42858CD8800121E1D /* HistoryHighlightsTestEntryProvider.swift */; }; C88E7A552A0553180072E638 /* OnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E7A542A0553180072E638 /* OnboardingViewModel.swift */; }; C88E7A572A0553360072E638 /* OnboardingButtonInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E7A562A0553360072E638 /* OnboardingButtonInfoModel.swift */; }; C88E7A592A0553440072E638 /* OnboardingCardInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E7A582A0553440072E638 /* OnboardingCardInfoModel.swift */; }; C88E7A5B2A0553510072E638 /* OnboardingLinkInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E7A5A2A0553510072E638 /* OnboardingLinkInfoModel.swift */; }; C88E7A602A05551B0072E638 /* NimbusOnboardingFeatureLayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C88E7A5F2A05551B0072E638 /* NimbusOnboardingFeatureLayerProtocol.swift */; }; - C893075D265501EE00A1DB2F /* CredentialAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C893075C265501EE00A1DB2F /* CredentialAssets.xcassets */; }; - C89C91AD2A1FE9E900BE57B1 /* OnboardingTelemetryDelegationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89C91AC2A1FE9E900BE57B1 /* OnboardingTelemetryDelegationTests.swift */; }; - C8A012F126AB07D70096A7A7 /* JumpBackInViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A012F026AB07D70096A7A7 /* JumpBackInViewModel.swift */; }; C8A4137428BE58C900D8EFEA /* WallpaperMetadataCodableProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A4137328BE58C900D8EFEA /* WallpaperMetadataCodableProtocol.swift */; }; C8B07A4128199500000AFCE7 /* NimbusFlaggableFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B07A4028199500000AFCE7 /* NimbusFlaggableFeature.swift */; }; C8B0F5EB283B7BF9007AE65D /* pocketsponsoredfeed.json in Resources */ = {isa = PBXBuildFile; fileRef = C8B0F5EA283B7BF9007AE65D /* pocketsponsoredfeed.json */; }; @@ -1361,11 +1391,6 @@ C8CD80D82A1E31C20097C3AE /* NimbusOnboardingTestingConfigUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92F32A1407BA00AA7B05 /* NimbusOnboardingTestingConfigUtility.swift */; }; C8CD80DA2A1E8C1D0097C3AE /* OnboardingTelemetryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CD80D92A1E8C1D0097C3AE /* OnboardingTelemetryProtocol.swift */; }; C8CD80DC2A1E8C970097C3AE /* OnboardingTelemetryUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CD80DB2A1E8C970097C3AE /* OnboardingTelemetryUtility.swift */; }; - C8CE389C265E71E00009B09E /* CredentialListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CE389A265E71E00009B09E /* CredentialListViewController.swift */; }; - C8CE38B9265E71FE0009B09E /* EmptyPlaceholderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CE38B1265E71FE0009B09E /* EmptyPlaceholderCell.swift */; }; - C8CE38BB265E71FE0009B09E /* ItemListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CE38B3265E71FE0009B09E /* ItemListCell.swift */; }; - C8CE38BD265E71FE0009B09E /* UIFontExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CE38B6265E71FE0009B09E /* UIFontExtension.swift */; }; - C8CE38BF265E71FE0009B09E /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CE38B8265E71FE0009B09E /* UIImageExtension.swift */; }; C8DC90BD2A06699E0008832B /* MarkupNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DC90BC2A06699E0008832B /* MarkupNode.swift */; }; C8DC90C32A066B4A0008832B /* MarkupToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DC90C22A066B4A0008832B /* MarkupToken.swift */; }; C8DC90C52A066B6A0008832B /* MarkupTokenizingUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DC90C42A066B6A0008832B /* MarkupTokenizingUtility.swift */; }; @@ -1385,8 +1410,6 @@ C8E531CA29E5F7D300E03FEF /* URLScannerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E531C929E5F7D300E03FEF /* URLScannerTests.swift */; }; C8E531CC29E72A2F00E03FEF /* ShortcutRouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E531CB29E72A2F00E03FEF /* ShortcutRouteTests.swift */; }; C8E78BDD27F4A1E700C48BAA /* HistoryDeletionUtilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E78BDC27F4A1E700C48BAA /* HistoryDeletionUtilityTests.swift */; }; - C8EB60C41F1FB12500F9B5B3 /* navigationDelegate.html in Resources */ = {isa = PBXBuildFile; fileRef = C8EB60C31F1FB12500F9B5B3 /* navigationDelegate.html */; }; - C8EB60DC1F1FB9AD00F9B5B3 /* NavigationDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EB60DB1F1FB9AD00F9B5B3 /* NavigationDelegateTests.swift */; }; C8EDDBF029DD83FC003A4C07 /* RouteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EDDBEF29DD83FC003A4C07 /* RouteTests.swift */; }; C8EDDBF229DF1159003A4C07 /* URLScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EDDBF129DF1159003A4C07 /* URLScanner.swift */; }; C8EDDBF429DF119F003A4C07 /* DeeplinkInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8EDDBF329DF119F003A4C07 /* DeeplinkInput.swift */; }; @@ -1415,7 +1438,6 @@ D018F93E1F44A71A0098F8CA /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018F93D1F44A7190098F8CA /* Schema.swift */; }; D029A04920A62DB0001DB72F /* TemporaryDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = D029A04820A62DB0001DB72F /* TemporaryDocument.swift */; }; D03DCD95211B63D000151ACA /* DateGroupedTableData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03DCD94211B63D000151ACA /* DateGroupedTableData.swift */; }; - D03F8F23200EAC1F003C2224 /* AllFramesAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = D03F8F22200EAC1E003C2224 /* AllFramesAtDocumentStart.js */; }; D04CD718215EBD85004FF5B0 /* SettingsLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CD717215EBD85004FF5B0 /* SettingsLoadingView.swift */; }; D04CD74A216CF86B004FF5B0 /* SiriShortcuts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CD747216CF86A004FF5B0 /* SiriShortcuts.swift */; }; D04CD74B216CF86B004FF5B0 /* DevicePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04CD748216CF86B004FF5B0 /* DevicePickerViewController.swift */; }; @@ -1441,26 +1463,17 @@ D0E17FB6201F847600F1FCB5 /* FxASignIn.js in Resources */ = {isa = PBXBuildFile; fileRef = D0E17FA7201F847600F1FCB5 /* FxASignIn.js */; }; D0E89A2920910917001CE5C7 /* DownloadsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E89A2820910917001CE5C7 /* DownloadsPanel.swift */; }; D0FCF7F51FE45842004A7995 /* UserScriptManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FCF7F41FE45842004A7995 /* UserScriptManager.swift */; }; - D0FCF8061FE4772D004A7995 /* AllFramesAtDocumentEnd.js in Resources */ = {isa = PBXBuildFile; fileRef = D0FCF8031FE4772C004A7995 /* AllFramesAtDocumentEnd.js */; }; - D0FCF8071FE4772D004A7995 /* MainFrameAtDocumentEnd.js in Resources */ = {isa = PBXBuildFile; fileRef = D0FCF8041FE4772D004A7995 /* MainFrameAtDocumentEnd.js */; }; - D0FCF8081FE4772D004A7995 /* MainFrameAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = D0FCF8051FE4772D004A7995 /* MainFrameAtDocumentStart.js */; }; D301AAEE1A3A55B70078DD1D /* LegacyGridTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D301AAED1A3A55B70078DD1D /* LegacyGridTabViewController.swift */; }; D30684F11C84F12A002D8D82 /* SearchPlugins in Resources */ = {isa = PBXBuildFile; fileRef = D30684F01C84F12A002D8D82 /* SearchPlugins */; }; D308EE561CBF0BF5006843F2 /* CertError.css in Resources */ = {isa = PBXBuildFile; fileRef = D308EE551CBF0BF5006843F2 /* CertError.css */; }; - D313BE981B2F5096009EF241 /* DomainAutocompleteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D313BE971B2F5096009EF241 /* DomainAutocompleteTests.swift */; }; D314E7F71A37B98700426A76 /* TabToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D314E7F51A37B98700426A76 /* TabToolbar.swift */; }; D31A0FC71A65D6D000DC8C7E /* SearchSuggestClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31A0FC61A65D6D000DC8C7E /* SearchSuggestClient.swift */; }; D31CF65C1CC1959A001D0BD0 /* PrivilegedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31CF65B1CC1959A001D0BD0 /* PrivilegedRequest.swift */; }; - D31EC05F1CC57ED80096F4AB /* localhostLoad.html in Resources */ = {isa = PBXBuildFile; fileRef = D31EC05E1CC57ED80096F4AB /* localhostLoad.html */; }; D31F95E91AC226CB005C9F3B /* ScreenshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D31F95E81AC226CB005C9F3B /* ScreenshotHelper.swift */; }; D32CACED1AE04DA1000658EB /* TestSwiftData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D32CACEC1AE04DA1000658EB /* TestSwiftData.swift */; }; - D343DCFE1C446BDB00D7EEE8 /* findPage.html in Resources */ = {isa = PBXBuildFile; fileRef = D343DCFD1C446BDB00D7EEE8 /* findPage.html */; }; D34510881ACF415700EC27F0 /* SearchLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34510871ACF415700EC27F0 /* SearchLoader.swift */; }; D34DC8531A16C40C00D49B7B /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34DC84D1A16C40C00D49B7B /* Profile.swift */; }; - D34E33031BA793C2006135F0 /* loginForm.html in Resources */ = {isa = PBXBuildFile; fileRef = D34E33021BA793C2006135F0 /* loginForm.html */; }; - D36998891AD70A0A00650C6C /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D36998881AD70A0A00650C6C /* IOKit.framework */; }; D37524871C6E8B5A00A5F6C2 /* topdomains.txt in Resources */ = {isa = PBXBuildFile; fileRef = D37524861C6E8B5A00A5F6C2 /* topdomains.txt */; }; - D375A9201AE71675001B30D5 /* ViewMemoryLeakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D375A91F1AE71675001B30D5 /* ViewMemoryLeakTests.swift */; }; D37DE2831CA2047500A5EC69 /* CertStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D37DE2821CA2047500A5EC69 /* CertStore.swift */; }; D37DE2C71CA356D800A5EC69 /* CertTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D37DE2C61CA356D800A5EC69 /* CertTests.swift */; }; D37DE2CA1CA356F900A5EC69 /* testcert1.pem in Resources */ = {isa = PBXBuildFile; fileRef = D37DE2C81CA356F900A5EC69 /* testcert1.pem */; }; @@ -1469,12 +1482,9 @@ D38A1EE01CB458EC0080C842 /* CertError.html in Resources */ = {isa = PBXBuildFile; fileRef = D38A1EDF1CB458EC0080C842 /* CertError.html */; }; D38B2D8C1A8D98D90040E6B5 /* OpenSearchParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3FA77831A43B2CE0010CD32 /* OpenSearchParser.swift */; }; D38F02D11C05127100175932 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D38F02D01C05127100175932 /* Authenticator.swift */; }; - D38F03701C06387900175932 /* AuthenticationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D38F036F1C06387900175932 /* AuthenticationTests.swift */; }; D3968F251A38FE8500CEFD3B /* TabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3968F241A38FE8500CEFD3B /* TabManager.swift */; }; D3972BF31C22412B00035B87 /* ShareExtensionHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3972BF11C22412B00035B87 /* ShareExtensionHelper.swift */; }; D3972BF41C22412B00035B87 /* TitleActivityItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3972BF21C22412B00035B87 /* TitleActivityItemProvider.swift */; }; - D39FA16C1A83E17800EE869C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D39FA16B1A83E17800EE869C /* CoreGraphics.framework */; }; - D39FA1811A83E84900EE869C /* Global.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39FA1801A83E84900EE869C /* Global.swift */; }; D3A14C221CB3145E00253BC6 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D35210E01CB2F16600FC5DCB /* Strings.swift */; }; D3A9949C1A3686BD008AD1AC /* BrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A994951A3686BD008AD1AC /* BrowserViewController.swift */; }; D3A9949D1A3686BD008AD1AC /* Tab.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A994961A3686BD008AD1AC /* Tab.swift */; }; @@ -1486,22 +1496,16 @@ D3BE7B461B054F8600641031 /* UITestAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3BE7B451B054F8600641031 /* UITestAppDelegate.swift */; }; D3BF8CBB1B7425570007AFE6 /* DiskImageStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3BF8CBA1B7425570007AFE6 /* DiskImageStore.swift */; }; D3C3696E1CC6B78800348A61 /* LocalRequestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C3696D1CC6B78800348A61 /* LocalRequestHelper.swift */; }; - D3C3EB651B6FF44000388E9A /* SessionRestoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C3EB641B6FF44000388E9A /* SessionRestoreTests.swift */; }; D3C744CD1A687D6C004CE85D /* URIFixup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C744CC1A687D6C004CE85D /* URIFixup.swift */; }; D3C744CF1A687D6C004CE85D /* URIFixup.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C744CC1A687D6C004CE85D /* URIFixup.swift */; }; - D3CFD3641CC5605B0064AB4A /* SecurityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3CFD3631CC5605B0064AB4A /* SecurityTests.swift */; }; D3D488591ABB54CD00A93597 /* FileAccessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D488581ABB54CD00A93597 /* FileAccessorTests.swift */; }; - D3E171C21A841EAD00AB44CD /* KIFHelper.js in Resources */ = {isa = PBXBuildFile; fileRef = D3E171C11A841EAD00AB44CD /* KIFHelper.js */; }; D3E8EF101B97BE69001900FB /* ClearPrivateDataTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E8EEE71B97A87A001900FB /* ClearPrivateDataTableViewController.swift */; }; D3FA777B1A43B2990010CD32 /* SearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3FA777A1A43B2990010CD32 /* SearchTests.swift */; }; D3FEC38D1AC4B42F00494F45 /* AutocompleteTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3FEC38C1AC4B42F00494F45 /* AutocompleteTextField.swift */; }; D433852C27ABC8150069DD33 /* MappaMundi in Frameworks */ = {isa = PBXBuildFile; productRef = D433852B27ABC8150069DD33 /* MappaMundi */; }; D437C4FD25FF5A3E00316F2C /* L10nMktSuiteSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D437C4FC25FF5A3E00316F2C /* L10nMktSuiteSnapshotTests.swift */; }; D4859A7727328569009A2390 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84266742728462900382274 /* AccessibilityIdentifiers.swift */; }; - D4AFA84E2AFA5482000BFEAA /* ExperimentIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AFA84D2AFA5482000BFEAA /* ExperimentIntegrationTests.swift */; }; D4AFAB0E2AFA8F9A000BFEAA /* SyncIntegrationTests in Resources */ = {isa = PBXBuildFile; fileRef = D4AFAB0D2AFA8F99000BFEAA /* SyncIntegrationTests */; }; - D4C35391283500A600F7DC7D /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C35390283500A600F7DC7D /* PerformanceTests.swift */; }; - D4C4BDCE2253725E00986F04 /* LibraryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4C4BDCD2253725E00986F04 /* LibraryTests.swift */; }; D51EA5BA26406A0000334331 /* ExperimentsBranchesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D51EA5B926406A0000334331 /* ExperimentsBranchesViewController.swift */; }; D51EA5CF26406D8300334331 /* ExperimentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D51EA5CE26406D8300334331 /* ExperimentsViewController.swift */; }; D525DFB325FBE5E000B18763 /* TabTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D525DFB225FBE5E000B18763 /* TabTests.swift */; }; @@ -1531,15 +1535,12 @@ D5D052D92645ABF400759F85 /* ExperimentsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D052D82645ABF400759F85 /* ExperimentsSettingsView.swift */; }; D5D0532E2645B3A700759F85 /* ExperimentsTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D0532D2645B3A700759F85 /* ExperimentsTableView.swift */; }; D5D237782640BBA600326204 /* ExperimentsSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D237772640BBA600326204 /* ExperimentsSettingsViewController.swift */; }; - D81127D81F84023B0050841D /* PhotonActionSheetTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81127D71F84023B0050841D /* PhotonActionSheetTests.swift */; }; - D815A3A824A53F3200AAB221 /* TabToolbarHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D815A3A724A53F3200AAB221 /* TabToolbarHelperTests.swift */; }; D81E45131F82C56D004EFFBA /* NewTabContentSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D81E45121F82C56C004EFFBA /* NewTabContentSettingsViewController.swift */; }; D821E90E2141B71C00452C55 /* SiriSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D821E9052141B71C00452C55 /* SiriSettingsViewController.swift */; }; D82ED2641FEB3C420059570B /* DefaultSearchPrefsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82ED2631FEB3C420059570B /* DefaultSearchPrefsTests.swift */; }; D83822001FC7961D00303C12 /* DispatchQueueHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D83821FF1FC7961D00303C12 /* DispatchQueueHelper.swift */; }; D863C8F21F68BFC20058D95F /* GradientProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D863C8E31F68BFC20058D95F /* GradientProgressBar.swift */; }; D87F84AC20B891160091F2DA /* TabDisplayManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87F84AB20B891160091F2DA /* TabDisplayManager.swift */; }; - D88C01B722B29EC200936951 /* ScreenGraphTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39012F271F8ED262002E3D31 /* ScreenGraphTest.swift */; }; D88FDA9F1F4E2B9200FD9709 /* PhotonActionSheetProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FDA9E1F4E2B9200FD9709 /* PhotonActionSheetProtocol.swift */; }; D88FDAAF1F4E2BA000FD9709 /* PhotonActionSheetAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D88FDAAE1F4E2BA000FD9709 /* PhotonActionSheetAnimator.swift */; }; D8AA923421A602DC002605C0 /* HomePageSettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8AA923321A602DC002605C0 /* HomePageSettingViewController.swift */; }; @@ -1551,7 +1552,6 @@ D980063D26D8308700321BC1 /* RustPlacesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D980063C26D8308700321BC1 /* RustPlacesTests.swift */; }; DA27EEDB28BADF4700DD6F5D /* MenuBuilderHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA27EEDA28BADF4700DD6F5D /* MenuBuilderHelper.swift */; }; DA4F826729AD221600189590 /* ZoomPageBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA4F826629AD221600189590 /* ZoomPageBar.swift */; }; - DA52E1DA25F5961F0092204C /* LegacyTabTrayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA52E1D925F5961F0092204C /* LegacyTabTrayViewController.swift */; }; DA9FD88424E213B500168D1E /* SmallQuickLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9FD88224E213B400168D1E /* SmallQuickLink.swift */; }; DA9FD88624E213CD00168D1E /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9FD88524E213CC00168D1E /* Helpers.swift */; }; DA9FD88824E213DD00168D1E /* QuickLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9FD88724E213DC00168D1E /* QuickLink.swift */; }; @@ -1566,7 +1566,6 @@ DF529EA12AB1B421003C5373 /* FakespotReliabilityScoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF529EA02AB1B421003C5373 /* FakespotReliabilityScoreView.swift */; }; DF5D47522A9381C700D6AE74 /* FakespotSettingsCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF5D47512A9381C700D6AE74 /* FakespotSettingsCardView.swift */; }; DF8C6DD72A52EED1007FAAF2 /* ClientSyncManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF8C6DD62A52EED1007FAAF2 /* ClientSyncManagerTests.swift */; }; - DF940A0C2A96352B00C1497D /* FakespotSettingsCardViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF940A0A2A96316D00C1497D /* FakespotSettingsCardViewModelTests.swift */; }; DFA51481275FFEE500266AA0 /* HistoryHighlightsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFA51480275FFEE500266AA0 /* HistoryHighlightsManager.swift */; }; DFA51484276103A000266AA0 /* HistoryHighlightsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFA514822761012D00266AA0 /* HistoryHighlightsManagerTests.swift */; }; DFACBF7F277B5F7B003D5F41 /* WallpaperBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFACBF7E277B5F7B003D5F41 /* WallpaperBackgroundView.swift */; }; @@ -1585,7 +1584,6 @@ E12BD0AC28AC37F00029AAF0 /* UIColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12BD0AB28AC37F00029AAF0 /* UIColor+Extension.swift */; }; E12BD0AE28AC38480029AAF0 /* UIImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12BD0AD28AC38480029AAF0 /* UIImage+Extension.swift */; }; E12BD0B028AC3A7E0029AAF0 /* UIEdgeInsets+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12BD0AF28AC3A7E0029AAF0 /* UIEdgeInsets+Extension.swift */; }; - E1312FD129D237EE008DDA85 /* NotificationSurfaceManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1312FD029D237EE008DDA85 /* NotificationSurfaceManagerTests.swift */; }; E134D5802B31FF3100C6B17B /* FakespotAdLinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E134D57F2B31FF3000C6B17B /* FakespotAdLinkButton.swift */; }; E136D41A2B19D35D003D0302 /* EmbeddedNavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E136D4192B19D35D003D0302 /* EmbeddedNavController.swift */; }; E1380B8D2AEA897C00630AFA /* SidebarEnabledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1380B8C2AEA897C00630AFA /* SidebarEnabledView.swift */; }; @@ -1599,7 +1597,6 @@ E13E9AB52AAB0FB5001A0E9D /* FakespotViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13E9AB22AAB0FB4001A0E9D /* FakespotViewModel.swift */; }; E13F8C342928194800BDC8B4 /* PhotonActionSheetSiteHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E13F8C332928194800BDC8B4 /* PhotonActionSheetSiteHeaderView.swift */; }; E1442FBF294782B6003680B0 /* CGRect+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FBE294782B6003680B0 /* CGRect+Extension.swift */; }; - E1442FC0294782B6003680B0 /* CGRect+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FBE294782B6003680B0 /* CGRect+Extension.swift */; }; E1442FCF294782D9003680B0 /* UIView+Screenshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FC4294782D7003680B0 /* UIView+Screenshot.swift */; }; E1442FD0294782D9003680B0 /* UIAlertController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FC5294782D7003680B0 /* UIAlertController+Extension.swift */; }; E1442FD1294782D9003680B0 /* UIModalPresentationStyle+Photon.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FC6294782D7003680B0 /* UIModalPresentationStyle+Photon.swift */; }; @@ -1610,7 +1607,6 @@ E1442FD6294782D9003680B0 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FCB294782D8003680B0 /* UIView+Extension.swift */; }; E1442FD8294782D9003680B0 /* UIPasteboard+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FCD294782D8003680B0 /* UIPasteboard+Extension.swift */; }; E1442FDA294782F7003680B0 /* UIPasteboard+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FCD294782D8003680B0 /* UIPasteboard+Extension.swift */; }; - E1442FDC2947836E003680B0 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1442FCB294782D8003680B0 /* UIView+Extension.swift */; }; E1463D022981DA190074E16E /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1463D012981DA190074E16E /* NotificationManager.swift */; }; E1463D042982D0240074E16E /* NotificationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1463D032982D0240074E16E /* NotificationManagerTests.swift */; }; E1463D0629830E4F0074E16E /* MockUserNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1463D0529830E4F0074E16E /* MockUserNotificationCenter.swift */; }; @@ -1666,12 +1662,8 @@ E1A6AB4828CA833000EBEBDD /* WallpaperBaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A6AB4728CA833000EBEBDD /* WallpaperBaseViewController.swift */; }; E1ADE23C2B0649F200FD17AA /* FakespotState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1ADE23B2B0649F200FD17AA /* FakespotState.swift */; }; E1ADE23E2B06559500FD17AA /* FakespotAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1ADE23D2B06559500FD17AA /* FakespotAction.swift */; }; - E1AEC176286E0CF500062E29 /* JumpBackInViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AEC16F286E0CF500062E29 /* JumpBackInViewModelTests.swift */; }; - E1AEC177286E0CF500062E29 /* HistoryHighlightsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AEC170286E0CF500062E29 /* HistoryHighlightsViewModelTests.swift */; }; E1AEC178286E0CF500062E29 /* LegacyHomepageViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AEC171286E0CF500062E29 /* LegacyHomepageViewControllerTests.swift */; }; - E1AEC179286E0CF500062E29 /* FirefoxHomeViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AEC172286E0CF500062E29 /* FirefoxHomeViewModelTests.swift */; }; E1AEC17A286E0CF500062E29 /* WebViewNavigationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AEC175286E0CF500062E29 /* WebViewNavigationHandlerTests.swift */; }; - E1AF27362A13BDFE00CE5991 /* EngagementNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1AF27352A13BDFE00CE5991 /* EngagementNotificationTests.swift */; }; E1AF27442A17BCF700CE5991 /* engagementNotificationWithoutConditions.json in Resources */ = {isa = PBXBuildFile; fileRef = E1AF27432A17BCF700CE5991 /* engagementNotificationWithoutConditions.json */; }; E1AF3560286DE5F800960045 /* Smoketest3.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = E1AF3558286DE5F600960045 /* Smoketest3.xctestplan */; }; E1AF3561286DE5F800960045 /* Smoketest4.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = E1AF3559286DE5F600960045 /* Smoketest4.xctestplan */; }; @@ -1685,11 +1677,8 @@ E1B04A9D28E20A8300670E54 /* InstructionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B04A9C28E20A8300670E54 /* InstructionsView.swift */; }; E1B18C782B5FFFA9009CD968 /* UIView+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B18C772B5FFFA9009CD968 /* UIView+extension.swift */; }; E1B9A2C22CAD91EF00F6A0E9 /* ToolbarTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B9A2C12CAD91EF00F6A0E9 /* ToolbarTelemetry.swift */; }; - E1B9A2C42CADA78300F6A0E9 /* ToolbarTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1B9A2C32CADA78300F6A0E9 /* ToolbarTelemetryTests.swift */; }; - E1BDAC812B9F5DE40063E6BF /* ReportSiteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1BDAC802B9F5DE40063E6BF /* ReportSiteTests.swift */; }; E1BDAC832B9F65780063E6BF /* reportSiteIssueOff.json in Resources */ = {isa = PBXBuildFile; fileRef = E1BDAC822B9F65780063E6BF /* reportSiteIssueOff.json */; }; E1C437A32A96343A00D188CB /* FakespotFadeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C437A22A96343A00D188CB /* FakespotFadeLabel.swift */; }; - E1CD81BA290C4ED900124B27 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C84266742728462900382274 /* AccessibilityIdentifiers.swift */; }; E1CD81BC290C5C3F00124B27 /* DevicePickerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CD81BB290C5C3F00124B27 /* DevicePickerTableViewCell.swift */; }; E1CD81BE290C5C7500124B27 /* DevicePickerTableViewHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CD81BD290C5C7500124B27 /* DevicePickerTableViewHeaderCell.swift */; }; E1CD81BF290C5C9500124B27 /* DevicePickerTableViewHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1CD81BD290C5C7500124B27 /* DevicePickerTableViewHeaderCell.swift */; }; @@ -1734,20 +1723,16 @@ E4CD9F541A71506400318571 /* Reader.html in Resources */ = {isa = PBXBuildFile; fileRef = E4CD9F531A71506400318571 /* Reader.html */; }; E4CD9F5B1A71506C00318571 /* Reader.css in Resources */ = {isa = PBXBuildFile; fileRef = E4CD9F5A1A71506C00318571 /* Reader.css */; }; E4CD9F6D1A77DD2800318571 /* ReaderModeStyleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4CD9F6C1A77DD2800318571 /* ReaderModeStyleViewController.swift */; }; - E4D6BEB91A0930EC00F538BD /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = E4D6BEB81A0930EC00F538BD /* LaunchScreen.xib */; }; E4E25CCB1CA99E7400D0F088 /* HexExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E25CCA1CA99E7400D0F088 /* HexExtensionsTests.swift */; }; E4E7EB6D1C4AED5E0094275D /* SupportUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E7EB6C1C4AED5E0094275D /* SupportUtilsTests.swift */; }; - E571EE7E28756A130051D9AA /* PocketStoryProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E571EE79287569960051D9AA /* PocketStoryProviderTests.swift */; }; E58368AA287D632F0087A449 /* StoryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58368A9287D632F0087A449 /* StoryProvider.swift */; }; E60D03181D511398002FE3F6 /* SyncDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E60D03171D511398002FE3F6 /* SyncDisplayState.swift */; }; E60D03271D511554002FE3F6 /* SyncDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E60D03171D511398002FE3F6 /* SyncDisplayState.swift */; }; E6231C011B90A44F005ABB0D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C001B90A44F005ABB0D /* libz.tbd */; }; E6231C051B90A472005ABB0D /* libxml2.2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C041B90A472005ABB0D /* libxml2.2.tbd */; }; - E6231C071B90A712005ABB0D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C001B90A44F005ABB0D /* libz.tbd */; }; E6231C081B90A71E005ABB0D /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = E6231C001B90A44F005ABB0D /* libz.tbd */; }; E6327A641BF6438E008D12E0 /* DebugSettingsBundleOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6327A631BF6438E008D12E0 /* DebugSettingsBundleOptions.swift */; }; E633E2DA1C21EAF8001FFF6C /* PasswordDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E633E2D91C21EAF8001FFF6C /* PasswordDetailViewController.swift */; }; - E633E37A1C2204BE001FFF6C /* LoginManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E633E3791C2204BE001FFF6C /* LoginManagerTests.swift */; }; E63ED7D81BFCD9990097D08E /* LoginDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = E63ED7D71BFCD9990097D08E /* LoginDetailTableViewCell.swift */; }; E63ED8E11BFD25580097D08E /* PasswordManagerListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E63ED8E01BFD25580097D08E /* PasswordManagerListViewController.swift */; }; E65075541E37F6FC006961AC /* LegacyDynamicFontHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65075531E37F6FC006961AC /* LegacyDynamicFontHelper.swift */; }; @@ -1786,23 +1771,17 @@ E653422D1C5944F90039DD9E /* BrowserPrompts.swift in Sources */ = {isa = PBXBuildFile; fileRef = E653422C1C5944F90039DD9E /* BrowserPrompts.swift */; }; E660BDD91BB06521009AC090 /* TabsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E660BDD81BB06521009AC090 /* TabsButton.swift */; }; E663D5781BB341C4001EF30E /* ToggleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E663D5771BB341C4001EF30E /* ToggleButton.swift */; }; - E67422C51CFF2D39009E8373 /* youtube.ico in Resources */ = {isa = PBXBuildFile; fileRef = E67422C41CFF2D39009E8373 /* youtube.ico */; }; E683F0C21E93D4E90035D990 /* DictionaryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E683F0C11E93D4E90035D990 /* DictionaryExtensions.swift */; }; E68AEDB01B18F81A00133D99 /* SwipeAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E68AEDAF1B18F81A00133D99 /* SwipeAnimator.swift */; }; E68E39BE1C46F42000B85F42 /* AppSettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E68E39BD1C46F42000B85F42 /* AppSettingsTableViewController.swift */; }; E693F0D91E9D64BD0086DC17 /* OptionalExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E693F0D81E9D64BD0086DC17 /* OptionalExtensions.swift */; }; E698FFDA1B4AADF40001F623 /* TabScrollController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E698FFD91B4AADF40001F623 /* TabScrollController.swift */; }; E69922171B94E3EF007C480D /* Licenses.html in Resources */ = {isa = PBXBuildFile; fileRef = E69922121B94E3EF007C480D /* Licenses.html */; }; - E6A92ADB1C52A8DA00743291 /* LoginInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6A92ADA1C52A8DA00743291 /* LoginInputTests.swift */; }; - E6B4C3D81C68F55C001F97E8 /* JSPrompt.html in Resources */ = {isa = PBXBuildFile; fileRef = E6B4C3D71C68F55C001F97E8 /* JSPrompt.html */; }; E6CF28E71CB43B7900151AB3 /* SensitiveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6CF28E61CB43B7900151AB3 /* SensitiveViewController.swift */; }; - E6EAC5961B29CB3A00E1DE1E /* scrollablePage.html in Resources */ = {isa = PBXBuildFile; fileRef = E6EAC5951B29CB3A00E1DE1E /* scrollablePage.html */; }; E6F9653C1B2F1D5D0034B023 /* NSURLExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F9653B1B2F1D5D0034B023 /* NSURLExtensionsTests.swift */; }; E6FF6ACA1D873CFF0070C294 /* PageMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6FF6AC91D873CFF0070C294 /* PageMetadata.swift */; }; EB07F860240D696000924860 /* PushNotificationSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB07F85E240D695F00924860 /* PushNotificationSetup.swift */; }; EB1C84BF212EFFBF001489DF /* BrowserViewController+ReaderMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB1C84B6212EFFBF001489DF /* BrowserViewController+ReaderMode.swift */; }; - EB3A38A02032673E004C6E67 /* DatabaseFixtureTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB3A38912032673D004C6E67 /* DatabaseFixtureTest.swift */; }; - EB63CA51228F0539005FD0EF /* DesktopModeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB63CA4F228F0538005FD0EF /* DesktopModeTests.swift */; }; EB6E0C60207E6C3100FBFF7E /* SendToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6E0C5F207E6C3000FBFF7E /* SendToDevice.swift */; }; EB7FFFC820A9D38D003E1E34 /* AlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7FFFBF20A9D38C003E1E34 /* AlertController.swift */; }; EB912B9A22722B5F00DF585A /* Deferred.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB912B8E22722A7800DF585A /* Deferred.swift */; }; @@ -1810,15 +1789,12 @@ EB912B9C22722B6800DF585A /* LockProtected.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB912B9122722A7800DF585A /* LockProtected.swift */; }; EB9407492081353100702E05 /* UXConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB940747208134AF00702E05 /* UXConstants.swift */; }; EB94075320850C9F00702E05 /* photon-colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C49854D206173C800893DAE /* photon-colors.swift */; }; - EB9854FF2422686F0040F24B /* AppDelegate+PushNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9854FD2422686F0040F24B /* AppDelegate+PushNotifications.swift */; }; EB98550124226EF70040F24B /* AppDelegate+SyncSentTabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB98550024226EF60040F24B /* AppDelegate+SyncSentTabs.swift */; }; EB9A179D20E69A7F00B12184 /* LegacyTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9A179A20E69A7E00B12184 /* LegacyTheme.swift */; }; EB9A179F20E6C1A200B12184 /* ThemedTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB9A179E20E6C1A200B12184 /* ThemedTableViewController.swift */; }; EBA1CC1E214AB8FD009E6B06 /* StorageTestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 289A4C121C4EB90600A460E3 /* StorageTestUtils.swift */; }; EBA3B2C32268F16300728BDB /* PhotonActionSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBA3B2C22268F16300728BDB /* PhotonActionSheetView.swift */; }; EBA3B2D22268F57E00728BDB /* BadgeWithBackdrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBA3B2D12268F57E00728BDB /* BadgeWithBackdrop.swift */; }; - EBB6CA622140823900077CFA /* firefoxScheme.html in Resources */ = {isa = PBXBuildFile; fileRef = EBB6CA592140823900077CFA /* firefoxScheme.html */; }; - EBB6CAA1214197DB00077CFA /* blobURL.html in Resources */ = {isa = PBXBuildFile; fileRef = EBB6CA98214197DA00077CFA /* blobURL.html */; }; EBB89504219398E500EB91A0 /* TrackingProtectionPageStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB894F5219398E500EB91A0 /* TrackingProtectionPageStats.swift */; }; EBB89506219398E500EB91A0 /* TabContentBlocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB894F7219398E500EB91A0 /* TabContentBlocker.swift */; }; EBB89507219398E500EB91A0 /* ContentBlocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBB894F8219398E500EB91A0 /* ContentBlocker.swift */; }; @@ -1844,6 +1820,7 @@ ED45893E2CC800D9006F2C0B /* SearchEngineSelectionViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED45893D2CC800D9006F2C0B /* SearchEngineSelectionViewControllerTests.swift */; }; ED4589402CC8220A006F2C0B /* MockSearchEngineSelectionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED45893F2CC8220A006F2C0B /* MockSearchEngineSelectionCoordinator.swift */; }; ED55DC8C2CC2D7DA00E3FE3A /* SearchEngineSelectionCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED55DC8B2CC2D7DA00E3FE3A /* SearchEngineSelectionCoordinatorTests.swift */; }; + ED6C8DAC2CE6A4BB00D7F7F3 /* Sentry-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = ED6C8DAB2CE6A4BB00D7F7F3 /* Sentry-Dynamic */; }; EDC3C2562CCAC9CB005A047F /* SearchEnginesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC3C2552CCAC9CB005A047F /* SearchEnginesManager.swift */; }; EDC3D34F2CB5E70500C62DE3 /* SearchEngineTestAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EDC3D34E2CB5E70500C62DE3 /* SearchEngineTestAssets.xcassets */; }; EDC3D3552CB70A3F00C62DE3 /* OpenSearchEngineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC3D3542CB70A3F00C62DE3 /* OpenSearchEngineTests.swift */; }; @@ -1855,17 +1832,7 @@ F1BC457E2A40F6D2005541D5 /* EnhancedTrackingProtectionCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F18859512A3E46020004AA7B /* EnhancedTrackingProtectionCoordinatorTests.swift */; }; F605DD582CC73469009A671B /* TabDisplayDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F605DD572CC73469009A671B /* TabDisplayDiffableDataSource.swift */; }; F80D53CF2A09A3350047ED14 /* RustSyncManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80D53CD2A09A30F0047ED14 /* RustSyncManagerTests.swift */; }; - F80DF73F27034F6400E4C37D /* LegacyDynamicFontHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65075531E37F6FC006961AC /* LegacyDynamicFontHelper.swift */; }; - F80DF7412703BC8E00E4C37D /* CredentialPasscodeRequirementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80DF7402703BC8E00E4C37D /* CredentialPasscodeRequirementViewController.swift */; }; - F80DF74B270CB9CA00E4C37D /* AppAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65D89171C8647420006EA35 /* AppAuthenticator.swift */; }; - F8324A072649A188007E4BFA /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8324A062649A188007E4BFA /* AuthenticationServices.framework */; }; - F8324A0A2649A188007E4BFA /* CredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8324A092649A188007E4BFA /* CredentialProviderViewController.swift */; }; - F8324A122649A188007E4BFA /* CredentialProvider.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = F8324A052649A188007E4BFA /* CredentialProvider.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; F8324A7C2649A5A5007E4BFA /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8324A062649A188007E4BFA /* AuthenticationServices.framework */; }; - F8324AF62649B540007E4BFA /* Shared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288A2D861AB8B3260023ABC3 /* Shared.framework */; }; - F8324AFA2649B540007E4BFA /* Sync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2827315E1ABC9BE600AA1954 /* Sync.framework */; }; - F8324B122649B707007E4BFA /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34DC84D1A16C40C00D49B7B /* Profile.swift */; }; - F8324B262649B76E007E4BFA /* SyncDisplayState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E60D03171D511398002FE3F6 /* SyncDisplayState.swift */; }; F84B21DA1A090F8100AAB793 /* ClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84B21D91A090F8100AAB793 /* ClientTests.swift */; }; F84B22041A0910F600AAB793 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84B21E51A0910F600AAB793 /* AppDelegate.swift */; }; F84B220B1A0910F600AAB793 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F84B21EF1A0910F600AAB793 /* Images.xcassets */; }; @@ -1876,27 +1843,18 @@ F85C7F0E2711C556004BDBA4 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85C7F0D2711C555004BDBA4 /* SettingsViewController.swift */; }; F85C7F0F271DD154004BDBA4 /* AppAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E65D89171C8647420006EA35 /* AppAuthenticator.swift */; }; F85C7F122721048E004BDBA4 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85C7F112721048E004BDBA4 /* Layout.swift */; }; - F85C7F14272104E3004BDBA4 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85C7F112721048E004BDBA4 /* Layout.swift */; }; F8708D2E1A0970B70051AB07 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8708D251A0970990051AB07 /* Images.xcassets */; }; F8708D321A0970B70051AB07 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8708D291A0970990051AB07 /* ShareViewController.swift */; }; F886218C270CD3B8007F4562 /* DevicePasscodeRequiredViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F886218B270CD3B8007F4562 /* DevicePasscodeRequiredViewController.swift */; }; F886BFBD29AD56A400F77224 /* RustSyncManagerAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F886BFBC29AD56A400F77224 /* RustSyncManagerAPI.swift */; }; F8A0B08229AD61FA0091C75B /* RustSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A0B08129AD61FA0091C75B /* RustSyncManager.swift */; }; - F8A0B08329AD64790091C75B /* RustSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A0B08129AD61FA0091C75B /* RustSyncManager.swift */; }; F8A0B08429AD64790091C75B /* RustSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A0B08129AD61FA0091C75B /* RustSyncManager.swift */; }; - F8A0B08529AD647B0091C75B /* RustSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A0B08129AD61FA0091C75B /* RustSyncManager.swift */; }; F8AAC1B429663619000BCDEC /* RustAutofill.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AAC1B329663618000BCDEC /* RustAutofill.swift */; }; F8AAC1B7296637CE000BCDEC /* RustAutofillTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AAC1B5296637B7000BCDEC /* RustAutofillTests.swift */; }; F8B18F5529EE01A2008724A8 /* RustSyncManagerAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B18F5429EE01A1008724A8 /* RustSyncManagerAPITests.swift */; }; F8B7109E2ABE380B0029726E /* RustErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B7109D2ABE380B0029726E /* RustErrors.swift */; }; - F8B7109F2ABE38970029726E /* RustErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B7109D2ABE380B0029726E /* RustErrors.swift */; }; F8B710A02ABE38980029726E /* RustErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B7109D2ABE380B0029726E /* RustErrors.swift */; }; - F8B710A12ABE38980029726E /* RustErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B7109D2ABE380B0029726E /* RustErrors.swift */; }; - F8DEACC52A3D43DA00C3B19D /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = F8DEACC42A3D43DA00C3B19D /* Sentry */; }; - F98CB66E2A4123F1005F38E9 /* EnhancedTrackingProtectionMenuVMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98CB66C2A4123E7005F38E9 /* EnhancedTrackingProtectionMenuVMTests.swift */; }; FA6B2AC21D41F02D00429414 /* String+Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6B2AC11D41F02D00429414 /* String+Punycode.swift */; }; - FA6B2AC41D41F02D00429414 /* String+Punycode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA6B2AC11D41F02D00429414 /* String+Punycode.swift */; }; - FA9293D41D6580E100AC8D33 /* QRCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA9293D31D6580E100AC8D33 /* QRCodeViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1928,6 +1886,55 @@ remoteGlobalIDString = F84B21BD1A090F8100AAB793; remoteInfo = Client; }; + 2C1298A92BF5EE23005AE4E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2FCAE2191ABB51F800877008; + remoteInfo = Storage; + }; + 2C6C90862C614A17007D9B43 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F84B21BD1A090F8100AAB793; + remoteInfo = Client; + }; + 2CFE99732D45329400B25CE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F84B21BD1A090F8100AAB793; + remoteInfo = Client; + }; + 2CFE99792D45329500B25CE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2CFE99652D45329200B25CE0; + remoteInfo = Ecosia; + }; + 2CFE9FC42D4535FD00B25CE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2CFE99652D45329200B25CE0; + remoteInfo = Ecosia; + }; + 2CFE9FE52D453F0600B25CE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2CFE99652D45329200B25CE0; + remoteInfo = Ecosia; + }; + 2CFEA0B82D46427600B25CE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F84B21B61A090F8100AAB793 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 43BE5774278BA4D900491291; + remoteInfo = RustMozillaAppServices; + }; 2F11EE4F1ABCAE910083902D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -1984,13 +1991,6 @@ remoteGlobalIDString = 288A2D851AB8B3260023ABC3; remoteInfo = Shared; }; - 397848E01ED86605004C0C0B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 397848DA1ED86605004C0C0B; - remoteInfo = NotificationService; - }; 3B43E3D51D95C48D00BBA9DB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -1998,13 +1998,6 @@ remoteGlobalIDString = F84B21BD1A090F8100AAB793; remoteInfo = Client; }; - 3BFE4B0C1D342FB900DDF53F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F84B21BD1A090F8100AAB793; - remoteInfo = Client; - }; 43118CDE251A9CAD00F24376 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -2012,13 +2005,6 @@ remoteGlobalIDString = 288A2D851AB8B3260023ABC3; remoteInfo = Shared; }; - 435A2FF6278F51CD00C94282 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F8324A042649A188007E4BFA; - remoteInfo = CredentialProvider; - }; 45C9D1C72A323263009CDD7A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -2054,13 +2040,6 @@ remoteGlobalIDString = 2827315D1ABC9BE600AA1954; remoteInfo = Sync; }; - C82043D72523DDC000740B71 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2827315D1ABC9BE600AA1954; - remoteInfo = Sync; - }; D09A0CF01FAA2C7E009A0273 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -2075,20 +2054,6 @@ remoteGlobalIDString = 2827315D1ABC9BE600AA1954; remoteInfo = Sync; }; - D0EA58161FA836D200D03ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 288A2D851AB8B3260023ABC3; - remoteInfo = Shared; - }; - D39FA1651A83E0EC00EE869C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = F84B21BD1A090F8100AAB793; - remoteInfo = Client; - }; D40B30A821F718CC003C02A7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -2117,20 +2082,6 @@ remoteGlobalIDString = F84B21BD1A090F8100AAB793; remoteInfo = Client; }; - F8324AF82649B540007E4BFA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 288A2D851AB8B3260023ABC3; - remoteInfo = Shared; - }; - F8324AFC2649B540007E4BFA /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = F84B21B61A090F8100AAB793 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 2827315D1ABC9BE600AA1954; - remoteInfo = Sync; - }; F84B21D41A090F8100AAB793 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F84B21B61A090F8100AAB793 /* Project object */; @@ -2158,12 +2109,33 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - 4368F838279665E00013419B /* Embed Frameworks */ = { + 2CFE9FC62D4535FD00B25CE0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 2CFE9FE72D453F0600B25CE0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 2CFEA0BA2D46427600B25CE0 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( + 2CFEA0B72D46427600B25CE0 /* RustMozillaAppServices.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -2184,6 +2156,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 1251623D2D59FB31005CB958 /* Ecosia.framework in Copy Frameworks */, 43017ECB278E0C6700CED011 /* RustMozillaAppServices.framework in Copy Frameworks */, C82043C32523DD6A00740B71 /* Sync.framework in Copy Frameworks */, C87703D225223EA5006E38EB /* Shared.framework in Copy Frameworks */, @@ -2198,8 +2171,6 @@ dstSubfolderSpec = 13; files = ( 047F9B3224E1FE1F00CD7DF7 /* WidgetKitExtension.appex in Embed App Extensions */, - 397848E21ED86605004C0C0B /* NotificationService.appex in Embed App Extensions */, - F8324A122649A188007E4BFA /* CredentialProvider.appex in Embed App Extensions */, F84B22541A0920C600AAB793 /* ShareTo.appex in Embed App Extensions */, ); name = "Embed App Extensions"; @@ -2274,6 +2245,7 @@ 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingProtectionViewController.swift; sourceTree = ""; }; 0A574D09BF8D9E37D6C9C654 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 0A5B4CE9B0996AE804491134 /* an */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = an; path = an.lproj/Shared.strings; sourceTree = ""; }; + 0A686B3B2CDB70DC0090E146 /* MainMenuTelemetryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTelemetryTests.swift; sourceTree = ""; }; 0A6875142C91886A00606F53 /* CertificatesHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatesHeaderView.swift; sourceTree = ""; }; 0A734328A164466314ECE4BE /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Menu.strings; sourceTree = ""; }; 0A7693602C7DD19500103A6D /* CertificatesViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatesViewModelTests.swift; sourceTree = ""; }; @@ -2291,8 +2263,8 @@ 0AFF7F672C78989000265214 /* CertificatesHeaderItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatesHeaderItem.swift; sourceTree = ""; }; 0AFF7F692C7C7BB800265214 /* CertificatesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesCell.swift; sourceTree = ""; }; 0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesViewController.swift; sourceTree = ""; }; - 0B11AF002CB412D100AD51D5 /* Metrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Metrics.swift; sourceTree = ""; }; 0B11AF032CB4130F00AD51D5 /* Metrics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Metrics.swift; sourceTree = ""; }; + 0B1C58D02CE5019A00F498F0 /* UserAgentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentTests.swift; sourceTree = ""; }; 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksTests.swift; sourceTree = ""; }; 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTest.swift; sourceTree = ""; }; 0B3F8C5D2CA4471C00DB5367 /* EditBookmarkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBookmarkViewModel.swift; sourceTree = ""; }; @@ -2314,6 +2286,7 @@ 0B9D40781E8D5AC80059E664 /* XCUITests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCUITests-Bridging-Header.h"; sourceTree = ""; }; 0BA02DB12942605600C92603 /* FormAutofillHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormAutofillHelper.swift; sourceTree = ""; }; 0BA049CE9929DAA536A2CB2F /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/3DTouchActions.strings"; sourceTree = ""; }; + 0BA1524F2CE78DAD0090B869 /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = ""; }; 0BA1E00D1B03FB0B007675AF /* NetError.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = NetError.html; sourceTree = ""; }; 0BA1E02F1B051A07007675AF /* NetError.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = NetError.css; sourceTree = ""; }; 0BA896491A250E6500C1010C /* ProfileTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileTest.swift; sourceTree = ""; }; @@ -2371,6 +2344,7 @@ 0EC57CE42CA31E59002E3F04 /* PasswordGeneratorStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorStateTests.swift; sourceTree = ""; }; 0EC57D072CA6FCA5002E3F04 /* PasswordGeneratorHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorHeaderView.swift; sourceTree = ""; }; 0EC57D092CA6FCC5002E3F04 /* PasswordGeneratorPasswordFieldView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorPasswordFieldView.swift; sourceTree = ""; }; + 0ECB6B672CCFE663006A7C82 /* DateGroupedTableDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateGroupedTableDataTests.swift; sourceTree = ""; }; 0EEE4B2AA4EEEF77B6F992F7 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/FindInPage.strings; sourceTree = ""; }; 0F894AD0807E49799E2534E6 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Today.strings; sourceTree = ""; }; 0FB44A1B9437AF9EABCD398E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/3DTouchActions.strings; sourceTree = ""; }; @@ -2391,11 +2365,57 @@ 109E4D959F25FF2F13848417 /* jv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = jv; path = jv.lproj/AuthenticationManager.strings; sourceTree = ""; }; 10CD44F0A402C84BB31E5474 /* gu-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "gu-IN"; path = "gu-IN.lproj/Intro.strings"; sourceTree = ""; }; 11F747589EB8A55A47647C93 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/ClearPrivateData.strings; sourceTree = ""; }; + 120059172F3C8BD800D65907 /* BrowserViewController+EcosiaNavigationHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+EcosiaNavigationHandling.swift"; sourceTree = ""; }; + 120EBBA12F3F30FA0056F398 /* SpotlightToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotlightToast.swift; sourceTree = ""; }; + 120EBBA32F3F31140056F398 /* ProductTourSpotlightCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductTourSpotlightCoordinator.swift; sourceTree = ""; }; + 120EBBB52F3F35F60056F398 /* BrowserViewController+ProductTourSpotlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+ProductTourSpotlight.swift"; sourceTree = ""; }; + 120ED1352F446A260056F398 /* NTPFirstSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPFirstSearchView.swift; sourceTree = ""; }; 120F42119EB30F217AB9493E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/FindInPage.strings; sourceTree = ""; }; + 121416712D6373BA0097788B /* AddressFormManager.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = AddressFormManager.js; sourceTree = ""; }; + 121416722D6373BA0097788B /* AllFramesAtDocumentEnd.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = AllFramesAtDocumentEnd.js; sourceTree = ""; }; + 121416732D6373BA0097788B /* AllFramesAtDocumentStart.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = AllFramesAtDocumentStart.js; sourceTree = ""; }; + 121416742D6373BA0097788B /* AutofillAllFramesAtDocumentStart.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = AutofillAllFramesAtDocumentStart.js; sourceTree = ""; }; + 121416792D6375960097788B /* MainFrameAtDocumentEnd.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = MainFrameAtDocumentEnd.js; sourceTree = ""; }; + 1214167A2D6375960097788B /* MainFrameAtDocumentStart.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = MainFrameAtDocumentStart.js; sourceTree = ""; }; + 12141E682D686C030097788B /* EcosiaNetError.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = EcosiaNetError.html; sourceTree = ""; }; + 12141E6A2D686C1C0097788B /* EcosiaNetError.css */ = {isa = PBXFileReference; lastKnownFileType = text.css; path = EcosiaNetError.css; sourceTree = ""; }; + 121C6E172EC1EF870060319A /* BrowserViewController+WelcomeTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+WelcomeTransition.swift"; sourceTree = ""; }; 123045959E0F295753B4B4DB /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Today.strings; sourceTree = ""; }; + 123283EC2F210C10009EA5BC /* NTPFirstSearchCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPFirstSearchCell.swift; sourceTree = ""; }; + 123283EE2F210C23009EA5BC /* NTPFirstSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPFirstSearchViewModel.swift; sourceTree = ""; }; + 123475CB2DA7D6580017B0C2 /* BeforeOrAfterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeforeOrAfterView.swift; sourceTree = ""; }; + 12348FF92DB8D2CC0017B0C2 /* PrefsKeys+Ecosia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrefsKeys+Ecosia.swift"; sourceTree = ""; }; + 123CEC4B2E7814A2009EB379 /* FoundersGroteskCondensed-Semibold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "FoundersGroteskCondensed-Semibold.ttf"; sourceTree = ""; }; 12674A038346A46589A0AC0B /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = "el.lproj/Default Browser.strings"; sourceTree = ""; }; 126A40A4A5AFDFD655B0FDF4 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 126F44CCB14373DC7813DE1F /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ClearPrivateDataConfirm.strings; sourceTree = ""; }; + 128D685F2F2A3AE700F4B3EE /* LocalizedSearchSuggestions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedSearchSuggestions.swift; sourceTree = ""; }; + 128D6E812F2A94CF00F4B3EE /* SearchSuggestionFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSuggestionFlowLayout.swift; sourceTree = ""; }; + 129F18FD2EC5F04200E870C0 /* Task+Sleep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Sleep.swift"; sourceTree = ""; }; + 12AEB1252D3A51670035D7D8 /* LegacySessionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySessionData.swift; sourceTree = ""; }; + 12AEB1272D3A568D0035D7D8 /* SessionRestoreHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestoreHandler.swift; sourceTree = ""; }; + 12C11E132D28128000E4DDBF /* Common.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Common.xcconfig; path = Configuration/Common.xcconfig; sourceTree = ""; }; + 12C11E142D2812B200E4DDBF /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configuration/Debug.xcconfig; sourceTree = ""; }; + 12C11E152D2812B200E4DDBF /* EcosiaBeta.ShareTo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaBeta.ShareTo.xcconfig; path = Configuration/EcosiaBeta.ShareTo.xcconfig; sourceTree = ""; }; + 12C11E162D2812B200E4DDBF /* EcosiaBeta.WidgetKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaBeta.WidgetKit.xcconfig; path = Configuration/EcosiaBeta.WidgetKit.xcconfig; sourceTree = ""; }; + 12C11E172D2812B200E4DDBF /* EcosiaBetaDebug.ShareTo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaBetaDebug.ShareTo.xcconfig; path = Configuration/EcosiaBetaDebug.ShareTo.xcconfig; sourceTree = ""; }; + 12C11E182D2812B200E4DDBF /* EcosiaBetaDebug.WidgetKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaBetaDebug.WidgetKit.xcconfig; path = Configuration/EcosiaBetaDebug.WidgetKit.xcconfig; sourceTree = ""; }; + 12C11E192D2812B200E4DDBF /* EcosiaDebug.ShareTo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaDebug.ShareTo.xcconfig; path = Configuration/EcosiaDebug.ShareTo.xcconfig; sourceTree = ""; }; + 12C11E1A2D2812B200E4DDBF /* EcosiaDebug.WidgetKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaDebug.WidgetKit.xcconfig; path = Configuration/EcosiaDebug.WidgetKit.xcconfig; sourceTree = ""; }; + 12C11E1B2D2812B200E4DDBF /* Fennec.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Fennec.xcconfig; path = Configuration/Fennec.xcconfig; sourceTree = ""; }; + 12C11E1C2D2812B200E4DDBF /* Fennec.enterprise.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Fennec.enterprise.xcconfig; path = Configuration/Fennec.enterprise.xcconfig; sourceTree = ""; }; + 12C11E1D2D2812B200E4DDBF /* Firefox.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Firefox.xcconfig; path = Configuration/Firefox.xcconfig; sourceTree = ""; }; + 12C11E1E2D2812B200E4DDBF /* FirefoxBeta.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = FirefoxBeta.xcconfig; path = Configuration/FirefoxBeta.xcconfig; sourceTree = ""; }; + 12C11E1F2D2812B200E4DDBF /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configuration/Release.xcconfig; sourceTree = ""; }; + 12C11E202D2812B200E4DDBF /* Staging.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Staging.xcconfig; path = Configuration/Staging.xcconfig; sourceTree = ""; }; + 12C11E212D281B7200E4DDBF /* EcosiaHomepageSectionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaHomepageSectionType.swift; sourceTree = ""; }; + 12C11E222D281B7A00E4DDBF /* EcosiaTopSiteItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaTopSiteItemCell.swift; sourceTree = ""; }; + 12C11E9C2D28289900E4DDBF /* UIButton+Ecosia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Ecosia.swift"; sourceTree = ""; }; + 12C11E9D2D2828AA00E4DDBF /* SimpleToast+Ecosia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SimpleToast+Ecosia.swift"; sourceTree = ""; }; + 12C11EA02D2828EA00E4DDBF /* DispatchQueueHelper+BuildChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DispatchQueueHelper+BuildChannel.swift"; sourceTree = ""; }; + 12C11F662D2C298600E4DDBF /* Ecosia.ShareTo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Ecosia.ShareTo.xcconfig; path = Configuration/Ecosia.ShareTo.xcconfig; sourceTree = ""; }; + 12C11F672D2C299D00E4DDBF /* Ecosia.WidgetKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Ecosia.WidgetKit.xcconfig; path = Configuration/Ecosia.WidgetKit.xcconfig; sourceTree = ""; }; + 12C11FF32D2D76CD00E4DDBF /* FxNimbusMessaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxNimbusMessaging.swift; sourceTree = ""; }; 12EA4881BFBE296298150D4A /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/LoginManager.strings; sourceTree = ""; }; 12F949169C30744CCC749588 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/HistoryPanel.strings; sourceTree = ""; }; 1323403C8071FC19BB79C191 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -2629,7 +2649,7 @@ 21E78A6F28F9A8C500F8D687 /* MockUIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUIDevice.swift; sourceTree = ""; }; 21E78A7128F9A93100F8D687 /* UIDeviceInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDeviceInterface.swift; sourceTree = ""; }; 21EA46692B04130500AAAB2D /* TabsPanelState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsPanelState.swift; sourceTree = ""; }; - 21ED80B22AF2E43A0065D4C7 /* TabDisplayViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabDisplayViewTests.swift; sourceTree = ""; }; + 21ED80B22AF2E43A0065D4C7 /* TabDisplayDiffableDataSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabDisplayDiffableDataSourceTests.swift; sourceTree = ""; }; 21F2A2D12B0BC85200626AEC /* InactiveTabsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InactiveTabsModel.swift; sourceTree = ""; }; 21F2A2D32B0D194A00626AEC /* TabsPanelStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsPanelStateTests.swift; sourceTree = ""; }; 21FA8FAD2AE856460013B815 /* TabsCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsCoordinatorTests.swift; sourceTree = ""; }; @@ -2733,34 +2753,138 @@ 2B31494CBFF9DF6FA41953B2 /* ses */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ses; path = ses.lproj/FindInPage.strings; sourceTree = ""; }; 2BAA411FAF1D49B9990A7720 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Shared.strings"; sourceTree = ""; }; 2BAB4A40A318F5A577488909 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = mr.lproj/InfoPlist.strings; sourceTree = ""; }; + 2C00E15D2E299AE800811A76 /* InvisibleTabSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvisibleTabSession.swift; sourceTree = ""; }; + 2C00E1622E29A70500811A76 /* InvisibleTabManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvisibleTabManager.swift; sourceTree = ""; }; + 2C00E38F2E29B0E700811A76 /* EcosiaAuthFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaAuthFlow.swift; sourceTree = ""; }; 2C26401D9CCB8C2671EA2431 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/3DTouchActions.strings; sourceTree = ""; }; 2C2A5EF31E68469500F02659 /* PrivateBrowsingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateBrowsingTest.swift; sourceTree = ""; }; 2C2A91281FA2410D002E36BD /* HistoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryTests.swift; sourceTree = ""; }; + 2C2F32012D46A53000977F55 /* EcosiaTesting.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaTesting.xcconfig; path = Configuration/EcosiaTesting.xcconfig; sourceTree = ""; }; + 2C2F32172D46AA2C00977F55 /* EcosiaTesting.ShareTo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaTesting.ShareTo.xcconfig; path = Configuration/EcosiaTesting.ShareTo.xcconfig; sourceTree = ""; }; + 2C2F32182D46AA2C00977F55 /* EcosiaTesting.WidgetKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaTesting.WidgetKit.xcconfig; path = Configuration/EcosiaTesting.WidgetKit.xcconfig; sourceTree = ""; }; 2C31A7A81E8BFB2200DAC646 /* ReadingListTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadingListTests.swift; sourceTree = ""; }; 2C31A8461E8D447F00DAC646 /* HomePageSettingsUITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomePageSettingsUITest.swift; sourceTree = ""; }; 2C33410CB7E921A125379D7C /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; 2C3406C71E719F00000FD889 /* SettingsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = ""; }; + 2C406F052E82D1880067303E /* EcosiaPreview.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaPreview.xcconfig; path = Configuration/EcosiaPreview.xcconfig; sourceTree = ""; }; + 2C406F062E82D1880067303E /* EcosiaPreview.ShareTo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaPreview.ShareTo.xcconfig; path = Configuration/EcosiaPreview.ShareTo.xcconfig; sourceTree = ""; }; + 2C406F072E82D1880067303E /* EcosiaPreview.WidgetKit.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = EcosiaPreview.WidgetKit.xcconfig; path = Configuration/EcosiaPreview.WidgetKit.xcconfig; sourceTree = ""; }; 2C473BCF209778900008C853 /* DownloadsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsTests.swift; sourceTree = ""; }; 2C49854D206173C800893DAE /* photon-colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "photon-colors.swift"; sourceTree = ""; }; 2C4A07DB20246EAD0083E320 /* DragAndDropTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragAndDropTests.swift; sourceTree = ""; }; 2C4B6BF220349EB800A009C2 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = ""; }; + 2C4BFFC02D470E4100707D2F /* disconnect-block-advertising.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-advertising.json"; path = "../ContentBlockingLists/disconnect-block-advertising.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC12D470E4100707D2F /* disconnect-block-analytics.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-analytics.json"; path = "../ContentBlockingLists/disconnect-block-analytics.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC22D470E4100707D2F /* disconnect-block-content.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-content.json"; path = "../ContentBlockingLists/disconnect-block-content.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC32D470E4100707D2F /* disconnect-block-cookies-advertising.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-cookies-advertising.json"; path = "../ContentBlockingLists/disconnect-block-cookies-advertising.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC42D470E4100707D2F /* disconnect-block-cookies-analytics.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-cookies-analytics.json"; path = "../ContentBlockingLists/disconnect-block-cookies-analytics.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC52D470E4100707D2F /* disconnect-block-cookies-content.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-cookies-content.json"; path = "../ContentBlockingLists/disconnect-block-cookies-content.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC62D470E4100707D2F /* disconnect-block-cookies-social.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-cookies-social.json"; path = "../ContentBlockingLists/disconnect-block-cookies-social.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC72D470E4100707D2F /* disconnect-block-cryptomining.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-cryptomining.json"; path = "../ContentBlockingLists/disconnect-block-cryptomining.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC82D470E4100707D2F /* disconnect-block-fingerprinting.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-fingerprinting.json"; path = "../ContentBlockingLists/disconnect-block-fingerprinting.json"; sourceTree = SOURCE_ROOT; }; + 2C4BFFC92D470E4100707D2F /* disconnect-block-social.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = "disconnect-block-social.json"; path = "../ContentBlockingLists/disconnect-block-social.json"; sourceTree = SOURCE_ROOT; }; 2C6045859589979C43AF09E0 /* jv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = jv; path = jv.lproj/ClearPrivateDataConfirm.strings; sourceTree = ""; }; + 2C6C90822C614A16007D9B43 /* EcosiaSnapshotTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EcosiaSnapshotTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 2C6E44099EACF7BE5438CEB6 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Today.strings; sourceTree = ""; }; + 2C7518AA2D8F025800B12F1F /* Production.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Production.xcconfig; path = Configuration/Production.xcconfig; sourceTree = ""; }; + 2C8723952EA9313D0085902C /* TabAutoCloseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabAutoCloseManager.swift; sourceTree = ""; }; + 2C8723972EA931490085902C /* BrowserViewController+EcosiaErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+EcosiaErrorHandling.swift"; sourceTree = ""; }; + 2C8723982EA931490085902C /* Tab+InvisibleTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Tab+InvisibleTab.swift"; sourceTree = ""; }; + 2C8723992EA931490085902C /* TabManager+InvisibleTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+InvisibleTab.swift"; sourceTree = ""; }; + 2C87239D2EA931DB0085902C /* AccountsProviderWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsProviderWrapper.swift; sourceTree = ""; }; 2C8C07761E7800EA00DC1237 /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; 2C9144B0B15218D8A0FCD538 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/ClearPrivateData.strings"; sourceTree = ""; }; 2C97EC701E72C80E0092EC18 /* TopTabsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopTabsTest.swift; sourceTree = ""; }; 2CA16FDD1E5F089100332277 /* SearchTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchTest.swift; sourceTree = ""; }; 2CAE4511992E91A32AB7D7C7 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Today.strings; sourceTree = ""; }; 2CB1A6591FDEA8B60084E96D /* NewTabSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabSettings.swift; sourceTree = ""; }; + 2CB288CF2B07C6F300A8FCB3 /* EcosiaDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = EcosiaDebug.xcconfig; path = Configuration/EcosiaDebug.xcconfig; sourceTree = ""; }; + 2CB288D02B07C6F300A8FCB3 /* EcosiaBeta.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = EcosiaBeta.xcconfig; path = Configuration/EcosiaBeta.xcconfig; sourceTree = ""; }; + 2CB288D12B07C6F300A8FCB3 /* EcosiaBetaDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = EcosiaBetaDebug.xcconfig; path = Configuration/EcosiaBetaDebug.xcconfig; sourceTree = ""; }; + 2CB288D32B07C6F400A8FCB3 /* Ecosia.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Ecosia.xcconfig; path = Configuration/Ecosia.xcconfig; sourceTree = ""; }; + 2CB289252B07C8F000A8FCB3 /* EcosiaDebugSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EcosiaDebugSettings.swift; sourceTree = ""; }; + 2CB289262B07C8F000A8FCB3 /* EcosiaSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EcosiaSettings.swift; sourceTree = ""; }; + 2CB289272B07C8F000A8FCB3 /* NTPCustomizationSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NTPCustomizationSettingsViewController.swift; sourceTree = ""; }; + 2CB289422B07C8F000A8FCB3 /* WebsiteConnectionStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebsiteConnectionStatus.swift; sourceTree = ""; }; + 2CB289442B07C8F000A8FCB3 /* BrowserViewController+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+Ecosia.swift"; sourceTree = ""; }; + 2CB289452B07C8F000A8FCB3 /* UIFont+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+Ecosia.swift"; sourceTree = ""; }; + 2CB289462B07C8F000A8FCB3 /* ErrorPageHandler+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ErrorPageHandler+Ecosia.swift"; sourceTree = ""; }; + 2CB289472B07C8F000A8FCB3 /* SnapKit+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SnapKit+Ecosia.swift"; sourceTree = ""; }; + 2CB289482B07C8F000A8FCB3 /* HomepageViewController+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HomepageViewController+Ecosia.swift"; sourceTree = ""; }; + 2CB2894A2B07C8F000A8FCB3 /* UIView+maskedCorners.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+maskedCorners.swift"; sourceTree = ""; }; + 2CB2894C2B07C8F000A8FCB3 /* NumberFormatter+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+Ecosia.swift"; sourceTree = ""; }; + 2CB2894D2B07C8F000A8FCB3 /* URL+Ecosia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL+Ecosia.swift"; sourceTree = ""; }; + 2CB289542B07C8F000A8FCB3 /* BookmarksExchange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksExchange.swift; sourceTree = ""; }; 2CB56E3E1E926BFB00AF7586 /* ToolbarTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToolbarTest.swift; sourceTree = ""; }; + 2CB5C33E2E45F02D00B1048F /* SearchViewController+Ecosia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SearchViewController+Ecosia.swift"; sourceTree = ""; }; 2CC1B3EF1E9B861400814EEC /* DomainAutocompleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainAutocompleteTests.swift; sourceTree = ""; }; + 2CC2465D2D520EF90098467A /* EcosiaDarkTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaDarkTheme.swift; sourceTree = ""; }; + 2CC2465E2D520EF90098467A /* EcosiaLightTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaLightTheme.swift; sourceTree = ""; }; 2CCB296620A99C9500121DD8 /* LoginsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginsTests.swift; sourceTree = ""; }; 2CCF17522105E4FD00705AE5 /* DisplaySettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplaySettingsTests.swift; sourceTree = ""; }; + 2CE48ED92E29B5C400CE84CE /* EcosiaAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaAuth.swift; sourceTree = ""; }; + 2CE9E8042E43927000141C6D /* NTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPHeader.swift; sourceTree = ""; }; + 2CE9E8052E43927000141C6D /* NTPHeaderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPHeaderViewModel.swift; sourceTree = ""; }; 2CEA6F781E93E3A600D4100E /* SearchSettingsUITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchSettingsUITest.swift; sourceTree = ""; }; 2CEDADA120207EC400223A89 /* SyncFAUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncFAUITests.swift; sourceTree = ""; }; 2CF21D0820A4A163000D08B7 /* PocketTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PocketTests.swift; sourceTree = ""; }; + 2CF2206E2B72B0530038157D /* AppSettingsTableViewController+Ecosia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppSettingsTableViewController+Ecosia.swift"; sourceTree = ""; }; 2CF449A41E7BFE2C00FD7595 /* NavigationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationTest.swift; sourceTree = ""; }; 2CF9D9A920067FA10083DF2A /* BrowsingPDFTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowsingPDFTests.swift; sourceTree = ""; }; + 2CFE99662D45329200B25CE0 /* Ecosia.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Ecosia.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2CFE996F2D45329300B25CE0 /* EcosiaTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EcosiaTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 2CFE9FEB2D4557EF00B25CE0 /* Metrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metrics.swift; sourceTree = ""; }; + 2CFE9FED2D4557EF00B25CE0 /* FxNimbus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxNimbus.swift; sourceTree = ""; }; + 2CFE9FEE2D4557EF00B25CE0 /* FxNimbusMessaging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxNimbusMessaging.swift; sourceTree = ""; }; + 2CFE9FF92D455BD500B25CE0 /* Ecosia.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Ecosia.xcassets; sourceTree = ""; }; + 2CFE9FFA2D455BD500B25CE0 /* EcosiaFindInPageBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaFindInPageBar.swift; sourceTree = ""; }; + 2CFE9FFB2D455BD500B25CE0 /* EcosiaNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaNavigation.swift; sourceTree = ""; }; + 2CFE9FFC2D455BD500B25CE0 /* EcosiaPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaPrimaryButton.swift; sourceTree = ""; }; + 2CFE9FFD2D455BD500B25CE0 /* EmptyBookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyBookmarksView.swift; sourceTree = ""; }; + 2CFE9FFE2D455BD500B25CE0 /* EmptyBookmarksViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyBookmarksViewDelegate.swift; sourceTree = ""; }; + 2CFE9FFF2D455BD500B25CE0 /* EmptyHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyHeader.swift; sourceTree = ""; }; + 2CFEA0002D455BD500B25CE0 /* EmptyReadingListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyReadingListView.swift; sourceTree = ""; }; + 2CFEA0012D455BD500B25CE0 /* FilterController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterController.swift; sourceTree = ""; }; + 2CFEA0022D455BD500B25CE0 /* EcosiaLaunchScreen.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EcosiaLaunchScreen.xib; sourceTree = ""; }; + 2CFEA0032D455BD500B25CE0 /* EcosiaLaunchScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaLaunchScreenView.swift; sourceTree = ""; }; + 2CFEA0052D455BD500B25CE0 /* LoadingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingScreen.swift; sourceTree = ""; }; + 2CFEA0062D455BD500B25CE0 /* MarketsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsController.swift; sourceTree = ""; }; + 2CFEA0072D455BD500B25CE0 /* MultiplyImpact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiplyImpact.swift; sourceTree = ""; }; + 2CFEA0082D455BD500B25CE0 /* MultiplyImpactStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiplyImpactStep.swift; sourceTree = ""; }; + 2CFEA01A2D455BD500B25CE0 /* CustomizableNTPSettingConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizableNTPSettingConfig.swift; sourceTree = ""; }; + 2CFEA01B2D455BD500B25CE0 /* NTPCustomizationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPCustomizationCell.swift; sourceTree = ""; }; + 2CFEA01C2D455BD500B25CE0 /* NTPCustomizationCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPCustomizationCellViewModel.swift; sourceTree = ""; }; + 2CFEA01E2D455BD500B25CE0 /* ClimateImpactInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClimateImpactInfo.swift; sourceTree = ""; }; + 2CFEA01F2D455BD500B25CE0 /* NTPImpactCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPImpactCell.swift; sourceTree = ""; }; + 2CFEA0202D455BD500B25CE0 /* NTPImpactCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPImpactCellViewModel.swift; sourceTree = ""; }; + 2CFEA0212D455BD500B25CE0 /* NTPImpactDividerFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPImpactDividerFooter.swift; sourceTree = ""; }; + 2CFEA0222D455BD500B25CE0 /* NTPImpactRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPImpactRowView.swift; sourceTree = ""; }; + 2CFEA0232D455BD500B25CE0 /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressView.swift; sourceTree = ""; }; + 2CFEA0252D455BD500B25CE0 /* NTPLibaryCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPLibaryCellViewModel.swift; sourceTree = ""; }; + 2CFEA0262D455BD500B25CE0 /* NTPLibraryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPLibraryCell.swift; sourceTree = ""; }; + 2CFEA0272D455BD500B25CE0 /* NTPLibraryShortcutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPLibraryShortcutView.swift; sourceTree = ""; }; + 2CFEA0292D455BD500B25CE0 /* NTPLogoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPLogoCell.swift; sourceTree = ""; }; + 2CFEA02B2D455BD500B25CE0 /* NewsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsController.swift; sourceTree = ""; }; + 2CFEA02C2D455BD500B25CE0 /* NTPNewsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPNewsCell.swift; sourceTree = ""; }; + 2CFEA02D2D455BD500B25CE0 /* NTPNewsCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPNewsCellViewModel.swift; sourceTree = ""; }; + 2CFEA02F2D455BD500B25CE0 /* NTPConfigurableNudgeCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPConfigurableNudgeCardCell.swift; sourceTree = ""; }; + 2CFEA0302D455BD500B25CE0 /* NTPConfigurableNudgeCardCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPConfigurableNudgeCardCellViewModel.swift; sourceTree = ""; }; + 2CFEA0322D455BD500B25CE0 /* CircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleButton.swift; sourceTree = ""; }; + 2CFEA0332D455BD500B25CE0 /* DefaultBrowserViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowserViewController.swift; sourceTree = ""; }; + 2CFEA0342D455BD500B25CE0 /* NTPLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPLayout.swift; sourceTree = ""; }; + 2CFEA0352D455BD500B25CE0 /* NTPTooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPTooltip.swift; sourceTree = ""; }; + 2CFEA0362D455BD500B25CE0 /* NTPTooltip.Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPTooltip.Highlight.swift; sourceTree = ""; }; + 2CFEA0372D455BD500B25CE0 /* NTPTooltipDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPTooltipDelegate.swift; sourceTree = ""; }; + 2CFEA0432D455BD500B25CE0 /* PageActionMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageActionMenu.swift; sourceTree = ""; }; + 2CFEA0442D455BD500B25CE0 /* PageActionMenuCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageActionMenuCell.swift; sourceTree = ""; }; + 2CFEA0452D455BD500B25CE0 /* PageActionsShortcutsHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageActionsShortcutsHeader.swift; sourceTree = ""; }; + 2CFEA04A2D455BD500B25CE0 /* EcosiaThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EcosiaThemeManager.swift; sourceTree = ""; }; + 2CFEA04C2D455BD500B25CE0 /* WhatsNewDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewDataProvider.swift; sourceTree = ""; }; + 2CFEA04D2D455BD500B25CE0 /* WhatsNewLocalDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewLocalDataProvider.swift; sourceTree = ""; }; + 2CFEA04F2D455BD500B25CE0 /* WhatsNewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewCell.swift; sourceTree = ""; }; + 2CFEA0502D455BD500B25CE0 /* WhatsNewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewItem.swift; sourceTree = ""; }; + 2CFEA0512D455BD500B25CE0 /* WhatsNewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewViewController.swift; sourceTree = ""; }; + 2CFEA0522D455BD500B25CE0 /* WhatsNewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhatsNewViewModel.swift; sourceTree = ""; }; 2D434B33B5B653C6602FB516 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; 2D4D4136886E42550B1AD716 /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/Localizable.strings; sourceTree = ""; }; 2D5D468686FEA0B04D7F04CE /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Search.strings; sourceTree = ""; }; @@ -2885,9 +3009,7 @@ 3964B0991EA8F06F00F2EEF4 /* FeatureSwitch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureSwitch.swift; sourceTree = ""; }; 3964B09B1EA8F32C00F2EEF4 /* FeatureSwitchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeatureSwitchTests.swift; sourceTree = ""; }; 3964F5FB2656D2B500065278 /* initial_experiments.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = initial_experiments.json; path = Client/Experiments/initial_experiments.json; sourceTree = SOURCE_ROOT; }; - 39673BC02B6D82F400653F4A /* FxNimbusMessaging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FxNimbusMessaging.swift; sourceTree = ""; }; 396CDB54203C5B870034A3A3 /* TabTrayController+KeyCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabTrayController+KeyCommands.swift"; sourceTree = ""; }; - 397848DB1ED86605004C0C0B /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 397848DD1ED86605004C0C0B /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 397848DF1ED86605004C0C0B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39A359E31BFCCE94006B9E87 /* UserActivityHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UserActivityHandler.swift; path = Helpers/UserActivityHandler.swift; sourceTree = ""; }; @@ -2927,14 +3049,12 @@ 3B9A4BDF8865C6533212FC6C /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Menu.strings; sourceTree = ""; }; 3BB50E101D6274CD004B33DF /* TopSiteItemCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopSiteItemCell.swift; sourceTree = ""; }; 3BB50E1F1D627539004B33DF /* LegacyHomepageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyHomepageViewController.swift; sourceTree = ""; }; - 3BC659481E5BA4AE006D560F /* TopSites */ = {isa = PBXFileReference; lastKnownFileType = folder; path = TopSites; sourceTree = ""; }; 3BCC4716A1AC7645F49D316A /* uz */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uz; path = uz.lproj/InfoPlist.strings; sourceTree = ""; }; 3BCE6D3B1CEB9E4D0080928C /* ThirdPartySearchAlerts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThirdPartySearchAlerts.swift; sourceTree = ""; }; 3BF4B8E81D38497A00493393 /* BaseTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestCase.swift; sourceTree = ""; }; 3BF56D261CDBBE1F00AC4D75 /* SimpleToast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimpleToast.swift; sourceTree = ""; }; 3BF646DF88995BFE702F9A54 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Today.strings; sourceTree = ""; }; 3BF9438BA7306EFAF4752A76 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/ErrorPages.strings; sourceTree = ""; }; - 3BFE4B071D342FB800DDF53F /* XCUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XCUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3BFE4B0B1D342FB900DDF53F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3BFE4B4F1D34673D00DDF53F /* ThirdPartySearchTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThirdPartySearchTest.swift; sourceTree = ""; }; 3C084844BB8DEB6B0E704A62 /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/ErrorPages.strings; sourceTree = ""; }; @@ -3021,6 +3141,7 @@ 43010A4429C876B900EC5098 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/ErrorState.strings; sourceTree = ""; }; 43010A4529C876B900EC5098 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Settings.strings; sourceTree = ""; }; 43010A4629C876B900EC5098 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/SnackBar.strings; sourceTree = ""; }; + 43010EBC2CDE55B800468396 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/LibraryPanel.strings"; sourceTree = ""; }; 4301276E29F69A8300DD29A8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Notification.strings; sourceTree = ""; }; 4301276F29F69A8300DD29A8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/ZoomPageBar.strings; sourceTree = ""; }; 430132382ADD559F00B1FAD7 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -3055,17 +3176,35 @@ 4301946A2BB1927600D1303E /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/BottomSheet.strings; sourceTree = ""; }; 4301E1542AC1A47000DA05CE /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Share.strings; sourceTree = ""; }; 4301E1552AC1A47000DA05CE /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/TabLocation.strings; sourceTree = ""; }; + 430250992CEB80E500936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/ActivityStream.strings; sourceTree = ""; }; + 4302509A2CEB80E500936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/AddressToolbar.strings; sourceTree = ""; }; + 4302509B2CEB80E500936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Alert.strings; sourceTree = ""; }; + 4302509C2CEB80E500936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/BiometricAuthentication.strings; sourceTree = ""; }; + 4302509D2CEB80E500936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Bookmarks.strings; sourceTree = ""; }; + 4302509E2CEB80E500936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/BottomSheet.strings; sourceTree = ""; }; + 4302509F2CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/EditAddress.strings; sourceTree = ""; }; + 430250A02CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/EditCard.strings; sourceTree = ""; }; + 430250A12CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/FirefoxLogins.strings; sourceTree = ""; }; + 430250A22CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/FirefoxSync.strings; sourceTree = ""; }; + 430250A32CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Footer.strings; sourceTree = ""; }; + 430250A42CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/MainMenu.strings; sourceTree = ""; }; + 430250A52CEB80E600936045 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Microsurvey.strings; sourceTree = ""; }; 430257B02A124D5C003CD3FF /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Upgrade.strings; sourceTree = ""; }; 43025C9C2C3C0359009AE751 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/EditAddress.strings"; sourceTree = ""; }; 43025C9D2C3C0359009AE751 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/ScanQRCode.strings"; sourceTree = ""; }; 430284E62B8392D8000B2810 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; 430284E72B8392D8000B2810 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/PasswordAutofill.strings"; sourceTree = ""; }; 430296C72AEFC76500244298 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Shopping.strings; sourceTree = ""; }; + 4302D2BA2CDE54D700420BC8 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/FxANotification.strings; sourceTree = ""; }; + 4302D2BB2CDE54D700420BC8 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/LibraryPanel.strings; sourceTree = ""; }; 4302DBD3293E0A9100978ADF /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Alerts.strings; sourceTree = ""; }; 4302DBD4293E0A9100978ADF /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/TabsTray.strings; sourceTree = ""; }; 4303068D2A655B02007DAD0D /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/SelectCreditCard.strings; sourceTree = ""; }; 4303181D2A52E46F00F9EA4F /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/CustomizeFirefoxHome.strings"; sourceTree = ""; }; 4303181E2A52E47000F9EA4F /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 430386F62CEB830A006A9852 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/FxANotification.strings; sourceTree = ""; }; + 430386F72CEB830A006A9852 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/LibraryPanel.strings; sourceTree = ""; }; + 430386F82CEB830B006A9852 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 43039F4029E4275000C4600A /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = "it.lproj/Edit Card.strings"; sourceTree = ""; }; 4303AEF32A6557FC0043D90C /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/Alert.strings; sourceTree = ""; }; 4303AEF42A6557FC0043D90C /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/BiometricAuthentication.strings; sourceTree = ""; }; @@ -3107,6 +3246,7 @@ 4304053E2C298AEC00EB6E76 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/AddressToolbar.strings; sourceTree = ""; }; 4304053F2C298AEC00EB6E76 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 430405402C298AEC00EB6E76 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/QRCode.strings; sourceTree = ""; }; + 430407CC2CDE52F40091115A /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/LibraryPanel.strings; sourceTree = ""; }; 4304147B2C8F111A0055AA15 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Bookmarks.strings; sourceTree = ""; }; 4304147C2C8F111A0055AA15 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/MainMenu.strings; sourceTree = ""; }; 4304147D2C8F111A0055AA15 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/SuggestedSites.strings; sourceTree = ""; }; @@ -3138,6 +3278,7 @@ 43069EFA2C32C72A0026D514 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/QRCode.strings; sourceTree = ""; }; 4306A0272B83908D00E9E38E /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; 4306A0282B83908D00E9E38E /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/PasswordAutofill.strings"; sourceTree = ""; }; + 4306CE3F2CDE53C500B2F7BF /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/LibraryPanel.strings"; sourceTree = ""; }; 4306EE1028B39BAF00E3443B /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/JumpBackIn.strings"; sourceTree = ""; }; 4306EE1128B39BAF00E3443B /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/ToolbarLocation.strings"; sourceTree = ""; }; 43073EFF2A2DFA04008E6025 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -3166,6 +3307,7 @@ 430968E92A5C20600057EEE3 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/SelectCreditCard.strings"; sourceTree = ""; }; 43096D112B5549A80015CDFD /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/TabToolbar.strings"; sourceTree = ""; }; 4309A7C82A24C08800524A6B /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/DisplayCard.strings; sourceTree = ""; }; + 4309AE1F2CDE58AF0074AB97 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/LibraryPanel.strings; sourceTree = ""; }; 430A161728C6133D00D5B9B6 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/JumpBackIn.strings"; sourceTree = ""; }; 430A161828C6133D00D5B9B6 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/ToolbarLocation.strings"; sourceTree = ""; }; 430A4A412A0BCCB0009D4128 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/FirefoxSync.strings; sourceTree = ""; }; @@ -3300,6 +3442,7 @@ 431431A52C3C014B00E870F4 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/EditAddress.strings; sourceTree = ""; }; 431431A62C3C014B00E870F4 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/ScanQRCode.strings; sourceTree = ""; }; 431445022BE8F29E006B02CC /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Microsurvey.strings; sourceTree = ""; }; + 43144C9F2CDE54F10026437B /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LibraryPanel.strings; sourceTree = ""; }; 43144D3E2CBD337F00A4A888 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/PasswordGenerator.strings; sourceTree = ""; }; 4314C48D2C2051CF009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/ActivityStream.strings; sourceTree = ""; }; 4314C48E2C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/AddressToolbar.strings; sourceTree = ""; }; @@ -3314,6 +3457,8 @@ 4315265729DAECFD006F8057 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/ResearchSurface.strings; sourceTree = ""; }; 4315265829DAECFD006F8057 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Settings.strings; sourceTree = ""; }; 4315265929DAECFD006F8057 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/SnackBar.strings; sourceTree = ""; }; + 431568C22CD8EA0E009DF3B9 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/FxANotification.strings; sourceTree = ""; }; + 431568C32CD8EA0E009DF3B9 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 431586682A52E4FA006119F1 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 431586692A52E4FA006119F1 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/SelectCreditCard.strings; sourceTree = ""; }; 43158BD02A2DFC41000C3744 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Footer.strings"; sourceTree = ""; }; @@ -3369,6 +3514,85 @@ 431853AA2A0911440099B0E0 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = "gl.lproj/Default Browser.strings"; sourceTree = ""; }; 431853AB2A0911440099B0E0 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/Today.strings; sourceTree = ""; }; 431853AC2A0911440099B0E0 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/BookmarkPanelDeleteConfirm.strings; sourceTree = ""; }; + 431867B52CDE58C000BEDD7F /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/FxANotification.strings; sourceTree = ""; }; + 431867B62CDE58C000BEDD7F /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/LibraryPanel.strings; sourceTree = ""; }; + 431867B72CDE58C000BEDD7F /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/SearchEngineSelection.strings; sourceTree = ""; }; + 431889802CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/InfoPlist.strings; sourceTree = ""; }; + 431889812CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/3DTouchActions.strings; sourceTree = ""; }; + 431889822CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/AuthenticationManager.strings; sourceTree = ""; }; + 431889832CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; + 431889842CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ClearPrivateData.strings; sourceTree = ""; }; + 431889852CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ClearPrivateDataConfirm.strings; sourceTree = ""; }; + 431889862CD8E1A100365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = "ab.lproj/Default Browser.strings"; sourceTree = ""; }; + 431889872CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ErrorPages.strings; sourceTree = ""; }; + 431889882CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/FindInPage.strings; sourceTree = ""; }; + 431889892CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/HistoryPanel.strings; sourceTree = ""; }; + 4318898A2CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Intro.strings; sourceTree = ""; }; + 4318898B2CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Localizable.strings; sourceTree = ""; }; + 4318898C2CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/LoginManager.strings; sourceTree = ""; }; + 4318898D2CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Menu.strings; sourceTree = ""; }; + 4318898E2CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/PrivateBrowsing.strings; sourceTree = ""; }; + 4318898F2CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Search.strings; sourceTree = ""; }; + 431889902CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Shared.strings; sourceTree = ""; }; + 431889912CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Storage.strings; sourceTree = ""; }; + 431889922CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Today.strings; sourceTree = ""; }; + 431889932CD8E1A200365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ActivityStream.strings; sourceTree = ""; }; + 431889942CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/AddressToolbar.strings; sourceTree = ""; }; + 431889952CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Alert.strings; sourceTree = ""; }; + 431889962CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Alerts.strings; sourceTree = ""; }; + 431889972CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/BiometricAuthentication.strings; sourceTree = ""; }; + 431889982CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 431889992CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/BookmarkPanelDeleteConfirm.strings; sourceTree = ""; }; + 4318899A2CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Bookmarks.strings; sourceTree = ""; }; + 4318899B2CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/BottomSheet.strings; sourceTree = ""; }; + 4318899C2CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ContextualHints.strings; sourceTree = ""; }; + 4318899D2CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/CredentialProvider.strings; sourceTree = ""; }; + 4318899E2CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Credentials.strings; sourceTree = ""; }; + 4318899F2CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; + 431889A02CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/DisplayCard.strings; sourceTree = ""; }; + 431889A12CD8E1A300365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = "ab.lproj/Edit Card.strings"; sourceTree = ""; }; + 431889A22CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/EditAddress.strings; sourceTree = ""; }; + 431889A32CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/EditCard.strings; sourceTree = ""; }; + 431889A42CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/EngagementNotification.strings; sourceTree = ""; }; + 431889A52CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 431889A62CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ErrorState.strings; sourceTree = ""; }; + 431889A72CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 431889A82CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/FirefoxLogins.strings; sourceTree = ""; }; + 431889A92CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/FirefoxSync.strings; sourceTree = ""; }; + 431889AA2CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Footer.strings; sourceTree = ""; }; + 431889AB2CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/FxANotification.strings; sourceTree = ""; }; + 431889AC2CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/JumpBackIn.strings; sourceTree = ""; }; + 431889AD2CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 431889AE2CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/LoginsHelper.strings; sourceTree = ""; }; + 431889AF2CD8E1A400365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/MainMenu.strings; sourceTree = ""; }; + 431889B02CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Microsurvey.strings; sourceTree = ""; }; + 431889B12CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 431889B22CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Notification.strings; sourceTree = ""; }; + 431889B32CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Onboarding.strings; sourceTree = ""; }; + 431889B42CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 431889B52CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/PasswordGenerator.strings; sourceTree = ""; }; + 431889B62CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/QRCode.strings; sourceTree = ""; }; + 431889B72CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/RememberCard.strings; sourceTree = ""; }; + 431889B82CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ResearchSurface.strings; sourceTree = ""; }; + 431889B92CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ScanQRCode.strings; sourceTree = ""; }; + 431889BA2CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/SearchEngineSelection.strings; sourceTree = ""; }; + 431889BB2CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 431889BC2CD8E1A500365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/SelectCreditCard.strings; sourceTree = ""; }; + 431889BD2CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Settings.strings; sourceTree = ""; }; + 431889BE2CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Share.strings; sourceTree = ""; }; + 431889BF2CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Shopping.strings; sourceTree = ""; }; + 431889C02CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/SnackBar.strings; sourceTree = ""; }; + 431889C12CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/SuggestedSites.strings; sourceTree = ""; }; + 431889C22CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/TabLocation.strings; sourceTree = ""; }; + 431889C32CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/TabsTray.strings; sourceTree = ""; }; + 431889C42CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/TabToolbar.strings; sourceTree = ""; }; + 431889C52CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Toolbar.strings; sourceTree = ""; }; + 431889C62CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 431889C72CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/UpdateCard.strings; sourceTree = ""; }; + 431889C82CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Upgrade.strings; sourceTree = ""; }; + 431889C92CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 431889CA2CD8E1A600365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/WidgetIntents.strings; sourceTree = ""; }; + 431889CB2CD8E1A700365FD8 /* ab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ab; path = ab.lproj/Localizable.strings; sourceTree = ""; }; 4318EF0B2C3C036900DD0A86 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/EditAddress.strings"; sourceTree = ""; }; 4318EF0C2C3C036900DD0A86 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/ScanQRCode.strings"; sourceTree = ""; }; 4319051429DAEDD000A6BDCA /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Alert.strings; sourceTree = ""; }; @@ -3385,6 +3609,7 @@ 431913212A2DFA6900A00E1C /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/UpdateCard.strings; sourceTree = ""; }; 431922B32A5C20B500F8C966 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 431952D42BB195EF00836A91 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/BottomSheet.strings; sourceTree = ""; }; + 4319845A2CDE59D90051D065 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/LibraryPanel.strings"; sourceTree = ""; }; 4319E79529C87A43006C11A0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Alert.strings; sourceTree = ""; }; 4319E79629C87A43006C11A0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 4319E79729C87A43006C11A0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/EditCard.strings; sourceTree = ""; }; @@ -3393,6 +3618,9 @@ 4319E79A29C87A43006C11A0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/SnackBar.strings; sourceTree = ""; }; 4319FF1E2A52E53900EE8C47 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/CustomizeFirefoxHome.strings"; sourceTree = ""; }; 4319FF1F2A52E53900EE8C47 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 431A04882CDE584500C552E8 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LibraryPanel.strings; sourceTree = ""; }; + 431A77A42CD8E2DD00CB7F5D /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/FxANotification.strings; sourceTree = ""; }; + 431A77A52CD8E2DD00CB7F5D /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 431ACABC2A24C180002960D0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/DisplayCard.strings; sourceTree = ""; }; 431B75792A2DFB2D00D3DD0E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Footer.strings; sourceTree = ""; }; 431B757A2A2DFB2D00D3DD0E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/RememberCard.strings; sourceTree = ""; }; @@ -3406,6 +3634,8 @@ 431B95FB2C453A8A003B17BF /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/ScanQRCode.strings"; sourceTree = ""; }; 431BAAF7293E0D3500F58007 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Alerts.strings"; sourceTree = ""; }; 431BAAF8293E0D3500F58007 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/TabsTray.strings"; sourceTree = ""; }; + 431BB0A72CD8E855001238B5 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/FxANotification.strings"; sourceTree = ""; }; + 431BB0A82CD8E855001238B5 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/SearchEngineSelection.strings"; sourceTree = ""; }; 431BBDE72ACADE7E003BF226 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Share.strings; sourceTree = ""; }; 431C0CA825C890E500395CE4 /* DefaultBrowserOnboardingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowserOnboardingViewModel.swift; sourceTree = ""; }; 431C0D1D25C9DC4D00395CE4 /* DefaultBrowserOnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowserOnboardingTests.swift; sourceTree = ""; }; @@ -3443,6 +3673,8 @@ 431DF6C72C20532200ADD05A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 431DF6C82C20532200ADD05A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/QRCode.strings; sourceTree = ""; }; 431DF9AE2C3C04000057331C /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/EditAddress.strings; sourceTree = ""; }; + 431DFF4D2CD8E55100FF0946 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/FxANotification.strings; sourceTree = ""; }; + 431DFF4E2CD8E55100FF0946 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 431E1A042A6559630096284A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 431E1A052A6559630096284A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SelectCreditCard.strings; sourceTree = ""; }; 431E1BD029E426A5000E3B68 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = "fi.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -3495,6 +3727,7 @@ 432325162C20515200EE1210 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/QRCode.strings; sourceTree = ""; }; 43233C9E29BF3BBD002E6B6D /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Onboarding.strings; sourceTree = ""; }; 43233C9F29BF3BBD002E6B6D /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/ResearchSurface.strings; sourceTree = ""; }; + 432360BE2CDE5388007CA02B /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/LibraryPanel.strings; sourceTree = ""; }; 432361962A77CE0000027CA4 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/DisplayCard.strings"; sourceTree = ""; }; 432361972A77CE0000027CA4 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/FirefoxSync.strings"; sourceTree = ""; }; 432361982A77CE0000027CA4 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/Footer.strings"; sourceTree = ""; }; @@ -3527,6 +3760,9 @@ 432596062C85D88C005EE33C /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/MainMenu.strings; sourceTree = ""; }; 432596072C85D88C005EE33C /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/SuggestedSites.strings; sourceTree = ""; }; 4325A14B2ACAE0BC00010E11 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Share.strings"; sourceTree = ""; }; + 4325EDFA2CEB8412008DE78E /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/FxANotification.strings; sourceTree = ""; }; + 4325EDFB2CEB8412008DE78E /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/LibraryPanel.strings; sourceTree = ""; }; + 4325EDFC2CEB8412008DE78E /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 43262C382A52E54200A5A25F /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 43262C392A52E54200A5A25F /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/SelectCreditCard.strings; sourceTree = ""; }; 43263DA82CBD3A12000C2C5F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Bookmarks.strings"; sourceTree = ""; }; @@ -3559,6 +3795,8 @@ 43284257293E0A3D001A9851 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/TabsTray.strings; sourceTree = ""; }; 432849BD2A52E48C008008A1 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/CustomizeFirefoxHome.strings"; sourceTree = ""; }; 432849BE2A52E48C008008A1 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 4328526A2CDE5553008AD0EE /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/LibraryPanel.strings; sourceTree = ""; }; + 43287A7B2CDE59EC009DE122 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/LibraryPanel.strings"; sourceTree = ""; }; 43288E3629DAECDC00E19634 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Alert.strings; sourceTree = ""; }; 43288E3729DAECDC00E19634 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 43288E3829DAECDC00E19634 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/EditCard.strings; sourceTree = ""; }; @@ -3597,6 +3835,7 @@ 432AFD492B3061C600ED5F7E /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/FirefoxLogins.strings"; sourceTree = ""; }; 432AFD4A2B3061C600ED5F7E /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/LoginsHelper.strings"; sourceTree = ""; }; 432B292829225AF8001391E6 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 432B9A562CDE596400B3FF06 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/LibraryPanel.strings; sourceTree = ""; }; 432C01572B83900600392A88 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 432C01592B83900600392A88 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/PasswordAutofill.strings; sourceTree = ""; }; 432C25DA2A49AB2400C6945C /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Footer.strings; sourceTree = ""; }; @@ -3791,6 +4030,7 @@ 433AB4332ADD526500A7AC6D /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/TabLocation.strings; sourceTree = ""; }; 433B17B6293E0B4D004270FF /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Alerts.strings; sourceTree = ""; }; 433B17B7293E0B4D004270FF /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/TabsTray.strings; sourceTree = ""; }; + 433B2BF72CDE54170088A11C /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/LibraryPanel.strings"; sourceTree = ""; }; 433B535C28B39E5E0045FE4C /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/JumpBackIn.strings; sourceTree = ""; }; 433B8E962B554D0D00BB67DC /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/TabToolbar.strings"; sourceTree = ""; }; 433B981A2B55496D00940F00 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/TabToolbar.strings; sourceTree = ""; }; @@ -3822,6 +4062,7 @@ 433D64C72C60EE1100CEC389 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Toolbar.strings; sourceTree = ""; }; 433D70502CCFA8E2008704C8 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/FxANotification.strings"; sourceTree = ""; }; 433D70512CCFA8E2008704C8 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/SearchEngineSelection.strings"; sourceTree = ""; }; + 433DAEB72CDE577C00B4E660 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/LibraryPanel.strings; sourceTree = ""; }; 433DD5FB2AEFC6EB00C80D13 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Shopping.strings; sourceTree = ""; }; 433E0DEC26FCE37C00B78CC5 /* ne-NP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ne-NP"; path = "ne-NP.lproj/Default Browser.strings"; sourceTree = ""; }; 433E11402A124A2C00B4C650 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/FirefoxSync.strings; sourceTree = ""; }; @@ -3836,6 +4077,7 @@ 433F1040293E0BB200E9F73F /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Alerts.strings; sourceTree = ""; }; 433F1041293E0BB200E9F73F /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/TabsTray.strings; sourceTree = ""; }; 433F1B752CA1870A005F831A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/PasswordGenerator.strings; sourceTree = ""; }; + 433F5BFE2CEB81C10036A99F /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/LibraryPanel.strings; sourceTree = ""; }; 433FB1892C205098002A5074 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/ActivityStream.strings; sourceTree = ""; }; 433FB18A2C205098002A5074 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/AddressToolbar.strings; sourceTree = ""; }; 433FB18B2C205098002A5074 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -3923,6 +4165,7 @@ 4347545129C87819000D9710 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ErrorState.strings; sourceTree = ""; }; 4347545229C87819000D9710 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Settings.strings; sourceTree = ""; }; 4347545329C87819000D9710 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/SnackBar.strings; sourceTree = ""; }; + 4347676F2CEB825A00D97A8B /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/LibraryPanel.strings; sourceTree = ""; }; 43477CAA2B83902B00A2BF95 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43477CAB2B83902B00A2BF95 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/PasswordAutofill.strings; sourceTree = ""; }; 4347B397298D6D7B0045F677 /* CreditCardTableViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardTableViewModel.swift; sourceTree = ""; }; @@ -3978,6 +4221,10 @@ 434E1FDC299A549B00F79A7B /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/Today.strings; sourceTree = ""; }; 434E1FDD299A549B00F79A7B /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/Localizable.strings; sourceTree = ""; }; 434E733625EED32E006D3BDE /* BrowserViewController+URLBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+URLBarDelegate.swift"; sourceTree = ""; }; + 434E78E22CEB7D640095EE6A /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/FxANotification.strings; sourceTree = ""; }; + 434E78E32CEB7D640095EE6A /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 434E78E42CEB7D640095EE6A /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/LibraryPanel.strings; sourceTree = ""; }; + 434E78E52CEB7D640095EE6A /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/SuggestedSites.strings; sourceTree = ""; }; 434EA84429225CDE0039FBE1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 434ED7EC2C57B1D4000C86E4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Toolbar.strings; sourceTree = ""; }; 434EE0B429225A31005D030A /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; @@ -4049,6 +4296,9 @@ 4355C91C2B554AD800B90EF1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/TabToolbar.strings; sourceTree = ""; }; 435632DD2AC1A4A600C01BA7 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Share.strings; sourceTree = ""; }; 435632DE2AC1A4A600C01BA7 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/TabLocation.strings; sourceTree = ""; }; + 43564C422CDE557A0098FB48 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/FxANotification.strings; sourceTree = ""; }; + 43564C432CDE557A0098FB48 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/LibraryPanel.strings; sourceTree = ""; }; + 43564C442CDE557A0098FB48 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 43564CC72A52E6CE00EEE7E7 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 43564CC82A52E6CE00EEE7E7 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/SelectCreditCard.strings; sourceTree = ""; }; 4356596329DAEDC400F86EF9 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Alert.strings"; sourceTree = ""; }; @@ -4224,6 +4474,7 @@ 4361333E2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Settings.strings; sourceTree = ""; }; 4361333F2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/SnackBar.strings; sourceTree = ""; }; 4361916A2C7C9D72005B31E3 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/NativeErrorPage.strings"; sourceTree = ""; }; + 4361B1E82CDE536100B79FD9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/LibraryPanel.strings; sourceTree = ""; }; 4361DF292C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/BottomSheet.strings"; sourceTree = ""; }; 4361DF2A2C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/ContextualHints.strings"; sourceTree = ""; }; 4361DF2B2C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/CredentialProvider.strings"; sourceTree = ""; }; @@ -4379,6 +4630,7 @@ 436CBD222A52E5EC0088B658 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 436CBD232A52E5EC0088B658 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/SelectCreditCard.strings; sourceTree = ""; }; 436D24FF2AEFC85600237EAA /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Shopping.strings; sourceTree = ""; }; + 436D303F2CEB854F00E485B3 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/LibraryPanel.strings; sourceTree = ""; }; 436D62A029D1B32F00079AA8 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Alert.strings"; sourceTree = ""; }; 436D62A129D1B32F00079AA8 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; 436D62A229D1B32F00079AA8 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/EditCard.strings"; sourceTree = ""; }; @@ -4425,10 +4677,14 @@ 436F42FD2A52E6E100E4C2A2 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/SelectCreditCard.strings; sourceTree = ""; }; 436F68302AE68E3700BF46C9 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Share.strings; sourceTree = ""; }; 436F84692A4070C800279DAE /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Onboarding.strings; sourceTree = ""; }; + 436F91E92CEB7DFF00587C77 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/FxANotification.strings; sourceTree = ""; }; + 436F91EA2CEB7DFF00587C77 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/LibraryPanel.strings; sourceTree = ""; }; + 436F91EB2CEB7DFF00587C77 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 436FC4912C7C9D810083D324 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/NativeErrorPage.strings; sourceTree = ""; }; 436FC6A82A3734F30010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Footer.strings; sourceTree = ""; }; 436FC6A92A3734F30010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/RememberCard.strings; sourceTree = ""; }; 436FC6AA2A3734F40010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/UpdateCard.strings; sourceTree = ""; }; + 437016EA2CDE53730068D6EC /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/LibraryPanel.strings; sourceTree = ""; }; 4370179D2A37351700D9003D /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Footer.strings; sourceTree = ""; }; 4370179E2A37351700D9003D /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/RememberCard.strings; sourceTree = ""; }; 4370179F2A37351700D9003D /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -4532,6 +4788,7 @@ 437761242C85D6060094010A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Bookmarks.strings; sourceTree = ""; }; 437761252C85D6060094010A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/MainMenu.strings; sourceTree = ""; }; 437761262C85D6060094010A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/SuggestedSites.strings; sourceTree = ""; }; + 437797DC2CDE57D00017226F /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/LibraryPanel.strings"; sourceTree = ""; }; 4377C2022AEFC6720014E570 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/Shopping.strings; sourceTree = ""; }; 4377C98C2AC1A1BB001826A6 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Share.strings"; sourceTree = ""; }; 4377C98D2AC1A1BB001826A6 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/TabLocation.strings"; sourceTree = ""; }; @@ -4648,6 +4905,7 @@ 437EA6282B83915700DE9E71 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 437EA6292B83915700DE9E71 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/PasswordAutofill.strings; sourceTree = ""; }; 437F339929EF08FC003E82BC /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = "rm.lproj/Edit Card.strings"; sourceTree = ""; }; + 437F677B2CDE58E4002D482A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/LibraryPanel.strings; sourceTree = ""; }; 437F6A312C85D4FD00C6043B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Bookmarks.strings; sourceTree = ""; }; 437F6A322C85D4FE00C6043B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/MainMenu.strings; sourceTree = ""; }; 437F6A332C85D4FE00C6043B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/NativeErrorPage.strings; sourceTree = ""; }; @@ -4671,6 +4929,7 @@ 4381241E2BE8F46E00DE6900 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Microsurvey.strings; sourceTree = ""; }; 43815AC029F69D3B00D19265 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Notification.strings; sourceTree = ""; }; 43815AC129F69D3B00D19265 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 4381BA622CEB7F5100B754B1 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/LibraryPanel.strings; sourceTree = ""; }; 43820CFD28B39AA700A779CA /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/BookmarkPanel.strings; sourceTree = ""; }; 438225C52A1B87570098584D /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/FirefoxSync.strings; sourceTree = ""; }; 438225C62A1B87570098584D /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Upgrade.strings; sourceTree = ""; }; @@ -4705,6 +4964,23 @@ 4383320B29BF3D4000DCE236 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/ResearchSurface.strings; sourceTree = ""; }; 43838CCA28B39DB500238A7F /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/BookmarkPanel.strings; sourceTree = ""; }; 4383E28B2C453EAA00B775A3 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/ScanQRCode.strings; sourceTree = ""; }; + 4383E6E52CDE58590044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/FirefoxHomepage.strings"; sourceTree = ""; }; + 4383E6E62CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/FirefoxLogins.strings"; sourceTree = ""; }; + 4383E6E72CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/FxANotification.strings"; sourceTree = ""; }; + 4383E6E82CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; + 4383E6E92CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/LibraryPanel.strings"; sourceTree = ""; }; + 4383E6EA2CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/LoginsHelper.strings"; sourceTree = ""; }; + 4383E6EB2CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/MainMenu.strings"; sourceTree = ""; }; + 4383E6EC2CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Microsurvey.strings"; sourceTree = ""; }; + 4383E6ED2CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/NativeErrorPage.strings"; sourceTree = ""; }; + 4383E6EE2CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/PasswordAutofill.strings"; sourceTree = ""; }; + 4383E6EF2CDE585A0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/PasswordGenerator.strings"; sourceTree = ""; }; + 4383E6F02CDE585B0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/QRCode.strings"; sourceTree = ""; }; + 4383E6F12CDE585B0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/ScanQRCode.strings"; sourceTree = ""; }; + 4383E6F22CDE585B0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/SearchEngineSelection.strings"; sourceTree = ""; }; + 4383E6F32CDE585B0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Share.strings"; sourceTree = ""; }; + 4383E6F42CDE585B0044260D /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Shopping.strings"; sourceTree = ""; }; + 4384249B2CEB8240005DFC7A /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/LibraryPanel.strings; sourceTree = ""; }; 4384310A2A124D2900E52121 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/FirefoxSync.strings; sourceTree = ""; }; 4384310B2A124D2900E52121 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Upgrade.strings; sourceTree = ""; }; 4384513F28C6127B0043E51B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -4786,6 +5062,7 @@ 438ACD492C85D6920010BDA0 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Bookmarks.strings; sourceTree = ""; }; 438ACD4A2C85D6920010BDA0 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/MainMenu.strings; sourceTree = ""; }; 438ACD4B2C85D6920010BDA0 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/SuggestedSites.strings; sourceTree = ""; }; + 438AE2222CDE5619009BF451 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/LibraryPanel.strings; sourceTree = ""; }; 438AEF732AC1A29C00908F58 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/TabLocation.strings; sourceTree = ""; }; 438B138E28B39FB900E53858 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/JumpBackIn.strings; sourceTree = ""; }; 438B138F28B39FB900E53858 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ToolbarLocation.strings; sourceTree = ""; }; @@ -4824,6 +5101,9 @@ 438CAA3B2A52E67C00857596 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 438CAA3C2A52E67C00857596 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SelectCreditCard.strings; sourceTree = ""; }; 438CC1982C453A43000BC4CF /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/EditAddress.strings; sourceTree = ""; }; + 438D01A92CEB814E000DEDE9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/FxANotification.strings; sourceTree = ""; }; + 438D01AA2CEB814F000DEDE9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/LibraryPanel.strings; sourceTree = ""; }; + 438D01AB2CEB814F000DEDE9 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 438D18022C8F0F0B002DFFFC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/Bookmarks.strings"; sourceTree = ""; }; 438D18032C8F0F0B002DFFFC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/MainMenu.strings"; sourceTree = ""; }; 438D18042C8F0F0C002DFFFC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/NativeErrorPage.strings"; sourceTree = ""; }; @@ -4833,6 +5113,21 @@ 438D71902BE8F3010097636A /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Microsurvey.strings; sourceTree = ""; }; 438D9DC728BCD610003CDEBA /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/JumpBackIn.strings; sourceTree = ""; }; 438D9DC828BCD610003CDEBA /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 438DB4D02CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 438DB4D12CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/FxANotification.strings; sourceTree = ""; }; + 438DB4D22CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 438DB4D32CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/LoginsHelper.strings; sourceTree = ""; }; + 438DB4D42CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/Microsurvey.strings; sourceTree = ""; }; + 438DB4D52CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 438DB4D62CDE551C00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 438DB4D72CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/SearchEngineSelection.strings; sourceTree = ""; }; + 438DB4D82CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/SelectCreditCard.strings; sourceTree = ""; }; + 438DB4D92CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/Share.strings; sourceTree = ""; }; + 438DB4DA2CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/Shopping.strings; sourceTree = ""; }; + 438DB4DB2CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/SuggestedSites.strings; sourceTree = ""; }; + 438DB4DC2CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/TabLocation.strings; sourceTree = ""; }; + 438DB4DD2CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/TabToolbar.strings; sourceTree = ""; }; + 438DB4DE2CDE551D00A6F1BE /* gd */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gd; path = gd.lproj/Toolbar.strings; sourceTree = ""; }; 438DF00B2959B85B005E4C8F /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 438DF00C2959B85B005E4C8F /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/TabsTray.strings"; sourceTree = ""; }; 438E69C22BA85A1200DE4241 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/BottomSheet.strings; sourceTree = ""; }; @@ -4844,6 +5139,10 @@ 438EA1342C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 438EA1352C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/QRCode.strings; sourceTree = ""; }; 438EE30D2B554C5E00423F74 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/TabToolbar.strings; sourceTree = ""; }; + 438F8BC92CEB7DB600AC873C /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/FxANotification.strings; sourceTree = ""; }; + 438F8BCA2CEB7DB600AC873C /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/LibraryPanel.strings; sourceTree = ""; }; + 438F8BCB2CEB7DB600AC873C /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/PasswordGenerator.strings; sourceTree = ""; }; + 438F8BCC2CEB7DB600AC873C /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 438FAA9A28CF4BBE00F6C86B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/JumpBackIn.strings; sourceTree = ""; }; 438FAA9B28CF4BBE00F6C86B /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/ToolbarLocation.strings; sourceTree = ""; }; 438FE8632988ABA600155B10 /* CreditCardTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardTableViewController.swift; sourceTree = ""; }; @@ -4916,6 +5215,7 @@ 43948A742B0B75CE0024C394 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 439532AC28BE8D9B00B79237 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/JumpBackIn.strings; sourceTree = ""; }; 439532AD28BE8D9B00B79237 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 439563912CDE57F5008C1FC9 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/LibraryPanel.strings"; sourceTree = ""; }; 4395CEEA2C8F0EBA00D9846E /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Bookmarks.strings; sourceTree = ""; }; 4395CEEB2C8F0EBA00D9846E /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/MainMenu.strings; sourceTree = ""; }; 4395CEEC2C8F0EBA00D9846E /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/SuggestedSites.strings; sourceTree = ""; }; @@ -5024,6 +5324,9 @@ 439D3CDF2A37350E00E22F7D /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Footer.strings; sourceTree = ""; }; 439D3CE02A37350E00E22F7D /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/RememberCard.strings; sourceTree = ""; }; 439D3CE12A37350E00E22F7D /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/UpdateCard.strings; sourceTree = ""; }; + 439D70DD2CEB84610075AD08 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/SuggestedSites.strings"; sourceTree = ""; }; + 439D70DE2CEB84610075AD08 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/TabToolbar.strings"; sourceTree = ""; }; + 439D70DF2CEB84610075AD08 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Toolbar.strings"; sourceTree = ""; }; 439D965F29D1B0BE00F0A156 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Alert.strings; sourceTree = ""; }; 439D966029D1B0BE00F0A156 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 439D966129D1B0BE00F0A156 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/EditCard.strings; sourceTree = ""; }; @@ -5085,6 +5388,7 @@ 43A173182A24C0B5009C10B1 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Upgrade.strings; sourceTree = ""; }; 43A1F6A62BB19295008E5581 /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/BottomSheet.strings; sourceTree = ""; }; 43A2001C2BE8F5F800BBF27C /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Microsurvey.strings"; sourceTree = ""; }; + 43A2035F2CDE544200ABAD9B /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/LibraryPanel.strings"; sourceTree = ""; }; 43A27A372A3734FC00DE9AE0 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Footer.strings; sourceTree = ""; }; 43A27A382A3734FD00DE9AE0 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/RememberCard.strings; sourceTree = ""; }; 43A27A392A3734FD00DE9AE0 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -5139,6 +5443,7 @@ 43A7CA142A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Footer.strings"; sourceTree = ""; }; 43A7CA152A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/RememberCard.strings"; sourceTree = ""; }; 43A7CA162A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/UpdateCard.strings"; sourceTree = ""; }; + 43A80F902CEB85C200E21DFF /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/LibraryPanel.strings; sourceTree = ""; }; 43A878162B838FF90039D6B7 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/PasswordAutofill.strings; sourceTree = ""; }; 43A9086F2C57B3C300698450 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Toolbar.strings"; sourceTree = ""; }; 43A9B1E62A124A3800FB58C6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/FirefoxSync.strings; sourceTree = ""; }; @@ -5196,6 +5501,7 @@ 43AE0B4E2B30608200BCF6CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/CredentialProvider.strings; sourceTree = ""; }; 43AE0B4F2B30608200BCF6CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43AE0B502B30608200BCF6CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/LoginsHelper.strings; sourceTree = ""; }; + 43AE1B392CEB7DE500744617 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/LibraryPanel.strings; sourceTree = ""; }; 43AEF4462C205281006842B1 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/ActivityStream.strings; sourceTree = ""; }; 43AEF4472C205281006842B1 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/AddressToolbar.strings; sourceTree = ""; }; 43AEF4482C205281006842B1 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -5256,6 +5562,7 @@ 43B2DC8A29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/EngagementNotification.strings; sourceTree = ""; }; 43B2DC8B29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Onboarding.strings; sourceTree = ""; }; 43B2DC8C29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ResearchSurface.strings; sourceTree = ""; }; + 43B2FC742CEB8607006CB485 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/LibraryPanel.strings; sourceTree = ""; }; 43B3869C2C57B446007728B6 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ScanQRCode.strings; sourceTree = ""; }; 43B3869D2C57B446007728B6 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Toolbar.strings; sourceTree = ""; }; 43B388562C57B1830004C424 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Toolbar.strings"; sourceTree = ""; }; @@ -5294,6 +5601,7 @@ 43B627752C3C001E00A188E8 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/ScanQRCode.strings; sourceTree = ""; }; 43B658D829CE251C00C9EF08 /* CreditCardInputViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardInputViewModelTests.swift; sourceTree = ""; }; 43B68F092C1690270012DAE2 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Microsurvey.strings; sourceTree = ""; }; + 43B6A1A42CDE558C000E1445 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/LibraryPanel.strings; sourceTree = ""; }; 43B6D8182AEFC73C00B499C4 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Shopping.strings; sourceTree = ""; }; 43B6E77A2AC1A4F600D81CF5 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/TabLocation.strings"; sourceTree = ""; }; 43B715C62AEFC69100C18469 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Shopping.strings"; sourceTree = ""; }; @@ -5398,6 +5706,7 @@ 43BDB3392991174F009C4BFF /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/TabsTray.strings; sourceTree = ""; }; 43BDB33A2991174F009C4BFF /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43BDBBFD2752FA8600254DE4 /* LegacyTabCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyTabCell.swift; sourceTree = ""; }; + 43BDE5722CDE55FF005ADA27 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/LibraryPanel.strings; sourceTree = ""; }; 43BDEEBE2947458800C2FD7E /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Alerts.strings; sourceTree = ""; }; 43BDEEBF2947458800C2FD7E /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/TabsTray.strings; sourceTree = ""; }; 43BDEEC02947458800C2FD7E /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/ToolbarLocation.strings; sourceTree = ""; }; @@ -5409,6 +5718,7 @@ 43BF08252CAABE87003A6B92 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/PasswordGenerator.strings; sourceTree = ""; }; 43BF382B2B8CA9C400EFDAF3 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43BF3C5F2C7C9C380017A926 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 43BF9B3C2CEB7F0600ABB6FB /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LibraryPanel.strings; sourceTree = ""; }; 43BFD4502C3C04B400B76DE5 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/EditAddress.strings; sourceTree = ""; }; 43BFE5BA2C57B54100CFB4C5 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Toolbar.strings"; sourceTree = ""; }; 43C09A7A2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ActivityStream.strings; sourceTree = ""; }; @@ -5470,9 +5780,14 @@ 43C5D15429FFD7FC0099B67D /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Notification.strings; sourceTree = ""; }; 43C5D15529FFD7FC0099B67D /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43C5F9D42AC1A43B00927685 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/TabLocation.strings; sourceTree = ""; }; + 43C63CC62CDE5808003FBE8A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/LibraryPanel.strings"; sourceTree = ""; }; 43C680D22B554A9D00960168 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/TabToolbar.strings"; sourceTree = ""; }; 43C6A09628B39E8600DFC656 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/LoginManager.strings; sourceTree = ""; }; 43C6A09728B39E8600DFC656 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43C6A82E2CEB7D9A00FDAC33 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/FxANotification.strings; sourceTree = ""; }; + 43C6A82F2CEB7D9A00FDAC33 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/LibraryPanel.strings; sourceTree = ""; }; + 43C6A8302CEB7D9A00FDAC33 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/PasswordGenerator.strings; sourceTree = ""; }; + 43C6A8312CEB7D9A00FDAC33 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 43C6E56529F69AB000C42AEB /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Notification.strings"; sourceTree = ""; }; 43C6E56629F69AB000C42AEB /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/ZoomPageBar.strings"; sourceTree = ""; }; 43C6FAAE2C8F147B001C64E9 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Bookmarks.strings; sourceTree = ""; }; @@ -5486,6 +5801,7 @@ 43C7BDC92B0B777300841983 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 43C7DE1D2B55494A00E532FF /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/TabToolbar.strings; sourceTree = ""; }; 43C87C852A24BEDE0076467D /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/DisplayCard.strings; sourceTree = ""; }; + 43C8D8152CDE568E00573AD9 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/LibraryPanel.strings; sourceTree = ""; }; 43C8ED3B2ADD51F8003E7DF0 /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/Share.strings; sourceTree = ""; }; 43C8F3722C85D96B00A140F0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Bookmarks.strings; sourceTree = ""; }; 43C8F3732C85D96B00A140F0 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/MainMenu.strings; sourceTree = ""; }; @@ -5600,6 +5916,9 @@ 43D07B142C8F10AB00536CA1 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43D07B152C8F10AC00536CA1 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/SuggestedSites.strings; sourceTree = ""; }; 43D09B062B14B21200118FE3 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 43D0EA972CEB83C50045E291 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/FxANotification.strings; sourceTree = ""; }; + 43D0EA982CEB83C50045E291 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/LibraryPanel.strings; sourceTree = ""; }; + 43D0EA992CEB83C50045E291 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/SearchEngineSelection.strings; sourceTree = ""; }; 43D107F028B39AC6008DA9B8 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43D11E812B8CAABD0066674A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43D11E822B8CAABD0066674A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/PasswordAutofill.strings; sourceTree = ""; }; @@ -5763,6 +6082,7 @@ 43D99CD92C298E7F002BB0A5 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/QRCode.strings"; sourceTree = ""; }; 43D9AA50293E0C9300B77CA4 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Alerts.strings; sourceTree = ""; }; 43D9AA51293E0C9300B77CA4 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/TabsTray.strings; sourceTree = ""; }; + 43D9CE312CEB857D00DAA539 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/LibraryPanel.strings; sourceTree = ""; }; 43DAB1CD2922597600BB49F0 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43DACCA82C85D42600CA4B33 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Bookmarks.strings; sourceTree = ""; }; 43DACCA92C85D42600CA4B33 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = ""; }; @@ -5989,6 +6309,7 @@ 43E6BE132C57AF7600C2152C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/ScanQRCode.strings; sourceTree = ""; }; 43E6BE152C57AF7600C2152C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Toolbar.strings; sourceTree = ""; }; 43E7071E2C6A26FC00A29AE4 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Toolbar.strings; sourceTree = ""; }; + 43E71BC12CDE55A1002FF15A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/LibraryPanel.strings; sourceTree = ""; }; 43E730C22C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/ActivityStream.strings; sourceTree = ""; }; 43E730C32C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/AddressToolbar.strings; sourceTree = ""; }; 43E730C42C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -6085,6 +6406,7 @@ 43EC79AD2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Share.strings"; sourceTree = ""; }; 43EC79AE2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Shopping.strings"; sourceTree = ""; }; 43ECB7C52BE8F3410024824F /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Microsurvey.strings"; sourceTree = ""; }; + 43ECF55E2CDE589B00D9A8C4 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/LibraryPanel.strings; sourceTree = ""; }; 43ED215A2C57AFEC0014F10F /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Toolbar.strings; sourceTree = ""; }; 43ED29612B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/ContextualHints.strings; sourceTree = ""; }; 43ED29622B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/CredentialProvider.strings; sourceTree = ""; }; @@ -6106,6 +6428,7 @@ 43EDB31A29C876E600EFC1F3 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Settings.strings"; sourceTree = ""; }; 43EDB31B29C876E600EFC1F3 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/SnackBar.strings"; sourceTree = ""; }; 43EDD4312CA1844100B6CFF3 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/PasswordGenerator.strings"; sourceTree = ""; }; + 43EDDCCE2CDE532B001E5EDB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/LibraryPanel.strings; sourceTree = ""; }; 43EE0E1928B39F57002EB545 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/JumpBackIn.strings; sourceTree = ""; }; 43EE0E1A28B39F57002EB545 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43EE2FFB29FFD7D700C97C08 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Notification.strings; sourceTree = ""; }; @@ -6215,6 +6538,7 @@ 43F6CC4929EF074A00121D35 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Edit Card.strings"; sourceTree = ""; }; 43F6E7622C453DF70018EEA4 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/EditAddress.strings; sourceTree = ""; }; 43F6E7632C453DF70018EEA4 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43F74BFD2CDE53AF0066EB74 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/LibraryPanel.strings"; sourceTree = ""; }; 43F78D602A24BF6600E8A9EC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/DisplayCard.strings; sourceTree = ""; }; 43F7952425795F69005AEE40 /* SearchTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTelemetry.swift; sourceTree = ""; }; 43F7DAE62C20539100A29EA3 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/ActivityStream.strings"; sourceTree = ""; }; @@ -6238,7 +6562,7 @@ 43F8D46A2A2DF96300B45993 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/DisplayCard.strings; sourceTree = ""; }; 43F8FBF028B906190033F2FC /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/JumpBackIn.strings"; sourceTree = ""; }; 43F8FBF128B906190033F2FC /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/ToolbarLocation.strings"; sourceTree = ""; }; - 43F92B3729E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = AutofillAllFramesAtDocumentStart.js; sourceTree = ""; }; + 43F976DF2CDE579200239DC4 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/LibraryPanel.strings; sourceTree = ""; }; 43F977B52BA857CF00B065B7 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/BottomSheet.strings; sourceTree = ""; }; 43F9B46D2C85D63C003CAA5D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Bookmarks.strings; sourceTree = ""; }; 43F9B46E2C85D63C003CAA5D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = ""; }; @@ -6289,6 +6613,7 @@ 43FDF02F29225B3F00FB775B /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43FE017D2AC1A3B20058D206 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/TabLocation.strings; sourceTree = ""; }; 43FE740A2B14B0340019DACE /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/FirefoxHomepage.strings"; sourceTree = ""; }; + 43FEA1492CDE55D10040E31F /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/LibraryPanel.strings; sourceTree = ""; }; 43FEA2AE28B39B0D00B26B88 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43FEAB4F2BE8F60400EBB526 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Microsurvey.strings"; sourceTree = ""; }; 43FEB816293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Alerts.strings; sourceTree = ""; }; @@ -6305,6 +6630,7 @@ 43FF69D42C2050EC00D4B9C3 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/QRCode.strings"; sourceTree = ""; }; 43FF6D3828B9039900FA2316 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/JumpBackIn.strings; sourceTree = ""; }; 43FF6D3928B9039900FA2316 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 43FFB0752CD8E94C00F3E51A /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Bookmarks.strings; sourceTree = ""; }; 43FFC1312CA18889009BDF12 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/PasswordGenerator.strings; sourceTree = ""; }; 43FFC57529507FFD00ABD305 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Alerts.strings; sourceTree = ""; }; 43FFC57629507FFD00ABD305 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/TabsTray.strings; sourceTree = ""; }; @@ -6323,7 +6649,6 @@ 45355B252A269EAC00B1EA8E /* PushConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushConfiguration.swift; sourceTree = ""; }; 453F47E08C70126FD7288132 /* km */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = km; path = km.lproj/ErrorPages.strings; sourceTree = ""; }; 45524DDB8CC5BE8373DF589C /* anp */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = anp; path = anp.lproj/LoginManager.strings; sourceTree = ""; }; - 455880A027B42F720078DEBB /* FxNimbus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FxNimbus.swift; sourceTree = ""; }; 4590912D2A2E4F7700061F0C /* AutopushTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutopushTests.swift; sourceTree = ""; }; 459C47419B3E2BD1BF4393F2 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/InfoPlist.strings; sourceTree = ""; }; 45AD4685A96876B6D2682E0F /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/FindInPage.strings; sourceTree = ""; }; @@ -6512,7 +6837,6 @@ 5A9F83412B2B796800272819 /* TabPeekState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPeekState.swift; sourceTree = ""; }; 5A9F83432B2B8CE900272819 /* TabPeekModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPeekModel.swift; sourceTree = ""; }; 5AA0CC652A4B8F6100014E2A /* PasswordManagerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordManagerCoordinator.swift; sourceTree = ""; }; - 5AA1D8262BC09ECB00957516 /* TabTrayAnimationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabTrayAnimationQueue.swift; sourceTree = ""; }; 5AA75A622A46272000533F8D /* MockThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockThemeManager.swift; sourceTree = ""; }; 5AB4237B28A1947A003BC40C /* MockNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockNotificationCenter.swift; sourceTree = ""; }; 5AB4237D28A2BA9C003BC40C /* HistoryHighlightsDataAdaptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryHighlightsDataAdaptor.swift; sourceTree = ""; }; @@ -6520,6 +6844,12 @@ 5AC24B85BF0D2FCC2F1EB6D3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/LoginManager.strings; sourceTree = ""; }; 5AC549C0BA565EB2B08B87EC /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 5AC7110629F822E60011ED11 /* MockTabSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTabSessionStore.swift; sourceTree = ""; }; + 5AD3B6732CF625A300AFA1FE /* DefaultBrowserUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowserUtil.swift; sourceTree = ""; }; + 5AD3B6772CF650D000AFA1FE /* DefaultBrowserUitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultBrowserUitTests.swift; sourceTree = ""; }; + 5AD3B6792CF653A200AFA1FE /* MockLocale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLocale.swift; sourceTree = ""; }; + 5AD3B67B2CF65DE300AFA1FE /* LocaleInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocaleInterface.swift; sourceTree = ""; }; + 5AD3B67D2CF665AE00AFA1FE /* UIApplicationInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationInterface.swift; sourceTree = ""; }; + 5AD3B67F2CF6674B00AFA1FE /* MockUIApplication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUIApplication.swift; sourceTree = ""; }; 5AE371812A4DD0D70092A760 /* PasswordManagerCoordinatorDelegateMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordManagerCoordinatorDelegateMock.swift; sourceTree = ""; }; 5AE371832A4DD6F50092A760 /* PasswordManagerListViewControllerSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordManagerListViewControllerSpy.swift; sourceTree = ""; }; 5AE371862A4E11750092A760 /* AboutSettingsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutSettingsDelegate.swift; sourceTree = ""; }; @@ -6785,7 +7115,6 @@ 7B24DC9B1B67B3590005766B /* ClearPrivateDataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClearPrivateDataTests.swift; sourceTree = ""; }; 7B3632D31C2983F000D12AF9 /* L10nSuite2SnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = L10nSuite2SnapshotTests.swift; sourceTree = ""; }; 7B3B4D7F93D9654D82E3642B /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Intro.strings; sourceTree = ""; }; - 7B60B0071BDE3AE10090C984 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; }; 7B844E3C1BBDDB9D00E733A2 /* ChevronView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChevronView.swift; sourceTree = ""; }; 7B864D89B9D9C309484081D1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/PrivateBrowsing.strings; sourceTree = ""; }; 7B8A47F51D01D3B400C07734 /* PassKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PassKit.framework; path = System/Library/Frameworks/PassKit.framework; sourceTree = SDKROOT; }; @@ -6985,9 +7314,6 @@ 8A2825342760399B00395E66 /* KeyboardPressesHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPressesHandlerTests.swift; sourceTree = ""; }; 8A285B07294A5D4C00149B0F /* HomepageHeroImageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageHeroImageViewModel.swift; sourceTree = ""; }; 8A28C627291028870078A81A /* CanRemoveQuickActionBookmarkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanRemoveQuickActionBookmarkTests.swift; sourceTree = ""; }; - 8A2B1A5A28216C4C0061216B /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Configuration/Debug.xcconfig; sourceTree = ""; }; - 8A2B1A5B28216C4C0061216B /* Common.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Common.xcconfig; path = Configuration/Common.xcconfig; sourceTree = ""; }; - 8A2B1A5C28216C4D0061216B /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Configuration/Release.xcconfig; sourceTree = ""; }; 8A2D593D27DC0AA100713EC9 /* TopSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSite.swift; sourceTree = ""; }; 8A2DAD4A2CC02AA00067ECD0 /* LabelButtonHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelButtonHeaderView.swift; sourceTree = ""; }; 8A3233FB286270CF003E1C33 /* FxBookmarkNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxBookmarkNode.swift; sourceTree = ""; }; @@ -6995,16 +7321,6 @@ 8A32DD4F28B419B300D57C60 /* HomepageMessageCardViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageMessageCardViewModelTests.swift; sourceTree = ""; }; 8A33221E27DFE318008F809E /* TopSitesDataAdaptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopSitesDataAdaptorTests.swift; sourceTree = ""; }; 8A33222127DFE658008F809E /* NimbusMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NimbusMock.swift; sourceTree = ""; }; - 8A3345572BA499B6008C52AB /* disconnect-block-fingerprinting.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-fingerprinting.json"; path = "../../../ContentBlockingLists/disconnect-block-fingerprinting.json"; sourceTree = ""; }; - 8A3345582BA499B6008C52AB /* disconnect-block-advertising.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-advertising.json"; path = "../../../ContentBlockingLists/disconnect-block-advertising.json"; sourceTree = ""; }; - 8A3345592BA499B6008C52AB /* disconnect-block-cookies-content.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-cookies-content.json"; path = "../../../ContentBlockingLists/disconnect-block-cookies-content.json"; sourceTree = ""; }; - 8A33455A2BA499B6008C52AB /* disconnect-block-analytics.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-analytics.json"; path = "../../../ContentBlockingLists/disconnect-block-analytics.json"; sourceTree = ""; }; - 8A33455B2BA499B7008C52AB /* disconnect-block-cookies-advertising.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-cookies-advertising.json"; path = "../../../ContentBlockingLists/disconnect-block-cookies-advertising.json"; sourceTree = ""; }; - 8A33455C2BA499B7008C52AB /* disconnect-block-content.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-content.json"; path = "../../../ContentBlockingLists/disconnect-block-content.json"; sourceTree = ""; }; - 8A33455D2BA499B7008C52AB /* disconnect-block-cookies-analytics.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-cookies-analytics.json"; path = "../../../ContentBlockingLists/disconnect-block-cookies-analytics.json"; sourceTree = ""; }; - 8A33455E2BA499B7008C52AB /* disconnect-block-social.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-social.json"; path = "../../../ContentBlockingLists/disconnect-block-social.json"; sourceTree = ""; }; - 8A33455F2BA499B7008C52AB /* disconnect-block-cookies-social.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-cookies-social.json"; path = "../../../ContentBlockingLists/disconnect-block-cookies-social.json"; sourceTree = ""; }; - 8A3345602BA499B7008C52AB /* disconnect-block-cryptomining.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "disconnect-block-cryptomining.json"; path = "../../../ContentBlockingLists/disconnect-block-cryptomining.json"; sourceTree = ""; }; 8A359EF22A1FD449004A5BB7 /* AdjustWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjustWrapper.swift; sourceTree = ""; }; 8A359EF52A1FE840004A5BB7 /* MockAdjustWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAdjustWrapper.swift; sourceTree = ""; }; 8A36AC2B2886F27F00CDC0AD /* MockTabManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTabManager.swift; sourceTree = ""; }; @@ -7825,7 +8141,6 @@ BA1C68BB2B7ED153000D9397 /* MockWebKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockWebKit.swift; sourceTree = ""; }; BA354251BD3FFF63A161E385 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; BA7A14832C2CCEB3008DF1D9 /* EditAddressWebViewManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAddressWebViewManager.swift; sourceTree = ""; }; - BA8E197E2BF2FB1900590B5F /* AddressFormManager.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = AddressFormManager.js; sourceTree = ""; }; BA904A3B89BC820A7A802D55 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Storage.strings"; sourceTree = ""; }; BAA64356B54F1CD258764620 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/FindInPage.strings; sourceTree = ""; }; BAF14FEC94CB9DA4E08BA60C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Storage.strings; sourceTree = ""; }; @@ -7859,6 +8174,7 @@ BD7B4C6BBA14B1437A321E78 /* hi-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hi-IN"; path = "hi-IN.lproj/3DTouchActions.strings"; sourceTree = ""; }; BDB24F4C850C4A269C205FDF /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/LoginManager.strings; sourceTree = ""; }; BDB444F281416E39E6A11588 /* af */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = af; path = af.lproj/Menu.strings; sourceTree = ""; }; + BDD262552CE3AC8200DF2C62 /* PrivacyWindowHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyWindowHelper.swift; sourceTree = ""; }; BDF24048A0CB24CA7CBDB0AF /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/3DTouchActions.strings; sourceTree = ""; }; BE3B484FBA5F1ECC0994D694 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/ErrorPages.strings; sourceTree = ""; }; BE7C456B81F81CACE2AC654A /* an */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = an; path = an.lproj/3DTouchActions.strings; sourceTree = ""; }; @@ -8172,7 +8488,6 @@ D029A04820A62DB0001DB72F /* TemporaryDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemporaryDocument.swift; sourceTree = ""; }; D03DCD94211B63D000151ACA /* DateGroupedTableData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateGroupedTableData.swift; sourceTree = ""; }; D03F8F20200EABB0003C2224 /* __firefox__.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = __firefox__.js; sourceTree = ""; }; - D03F8F22200EAC1E003C2224 /* AllFramesAtDocumentStart.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = AllFramesAtDocumentStart.js; sourceTree = ""; }; D03F8F24200EACD8003C2224 /* ContextMenu.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = ContextMenu.js; sourceTree = ""; }; D04CD717215EBD85004FF5B0 /* SettingsLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLoadingView.swift; sourceTree = ""; }; D04CD747216CF86A004FF5B0 /* SiriShortcuts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SiriShortcuts.swift; path = Extensions/SiriShortcuts.swift; sourceTree = ""; }; @@ -8206,9 +8521,6 @@ D0E89A2820910917001CE5C7 /* DownloadsPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsPanel.swift; sourceTree = ""; }; D0FCF7F41FE45842004A7995 /* UserScriptManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserScriptManager.swift; sourceTree = ""; }; D0FCF7F61FE45E5D004A7995 /* PrintHandler.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = PrintHandler.js; sourceTree = ""; }; - D0FCF8031FE4772C004A7995 /* AllFramesAtDocumentEnd.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = AllFramesAtDocumentEnd.js; sourceTree = ""; }; - D0FCF8041FE4772D004A7995 /* MainFrameAtDocumentEnd.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = MainFrameAtDocumentEnd.js; sourceTree = ""; }; - D0FCF8051FE4772D004A7995 /* MainFrameAtDocumentStart.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = MainFrameAtDocumentStart.js; sourceTree = ""; }; D0FCF8091FE47B49004A7995 /* CustomSearchHandler.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = CustomSearchHandler.js; sourceTree = ""; }; D11142C4ABCD3CBBB8C749D0 /* hi-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hi-IN"; path = "hi-IN.lproj/PrivateBrowsing.strings"; sourceTree = ""; }; D1504B5491C90F2173E13F4D /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -8257,7 +8569,6 @@ D3968F241A38FE8500CEFD3B /* TabManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabManager.swift; sourceTree = ""; }; D3972BF11C22412B00035B87 /* ShareExtensionHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareExtensionHelper.swift; sourceTree = ""; }; D3972BF21C22412B00035B87 /* TitleActivityItemProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleActivityItemProvider.swift; sourceTree = ""; }; - D39FA15F1A83E0EC00EE869C /* UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D39FA1621A83E0EC00EE869C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D39FA16B1A83E17800EE869C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; D39FA16F1A83E62600EE869C /* UITests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UITests-Bridging-Header.h"; sourceTree = ""; }; @@ -8917,8 +9228,6 @@ E59A418F9208400903902F07 /* ses */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ses; path = ses.lproj/LoginManager.strings; sourceTree = ""; }; E5C24864B9BA6790B352962E /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Search.strings; sourceTree = ""; }; E5D343558F3C933F5CA7690C /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Storage.strings"; sourceTree = ""; }; - E60961861B62B8A700DD640F /* Fennec.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Fennec.xcconfig; path = Configuration/Fennec.xcconfig; sourceTree = ""; }; - E60961891B62B8C800DD640F /* Firefox.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Firefox.xcconfig; path = Configuration/Firefox.xcconfig; sourceTree = ""; }; E60D03171D511398002FE3F6 /* SyncDisplayState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncDisplayState.swift; sourceTree = ""; }; E6231C001B90A44F005ABB0D /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; E6231C041B90A472005ABB0D /* libxml2.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.2.tbd; path = usr/lib/libxml2.2.tbd; sourceTree = SDKROOT; }; @@ -8984,7 +9293,6 @@ E6C64CCF918A53B1EB0FAF01 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; E6CA4229B067C32CA10E19A9 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/Search.strings; sourceTree = ""; }; E6CF28E61CB43B7900151AB3 /* SensitiveViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensitiveViewController.swift; sourceTree = ""; }; - E6DCC1ED1DCBB6AA00CEC4B7 /* Fennec.enterprise.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Fennec.enterprise.xcconfig; path = Configuration/Fennec.enterprise.xcconfig; sourceTree = ""; }; E6DD408EAF352F4B541022BA /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = or.lproj/HistoryPanel.strings; sourceTree = ""; }; E6EAC5951B29CB3A00E1DE1E /* scrollablePage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = scrollablePage.html; sourceTree = ""; }; E6EC4E5DA51902A09A0F0957 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = "hr.lproj/Default Browser.strings"; sourceTree = ""; }; @@ -8994,7 +9302,6 @@ E6F9650C1B2F1CF20034B023 /* SharedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SharedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; E6F9650F1B2F1CF20034B023 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E6F9653B1B2F1D5D0034B023 /* NSURLExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSURLExtensionsTests.swift; sourceTree = ""; }; - E6FCC43C1C40565200DF6113 /* FirefoxBeta.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = FirefoxBeta.xcconfig; path = Configuration/FirefoxBeta.xcconfig; sourceTree = ""; }; E6FF6AC91D873CFF0070C294 /* PageMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageMetadata.swift; sourceTree = ""; }; E70049799CF2243911759906 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/Storage.strings; sourceTree = ""; }; E70B441A9D02B00C3000646B /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = te; path = te.lproj/Storage.strings; sourceTree = ""; }; @@ -9157,7 +9464,6 @@ F80D53CD2A09A30F0047ED14 /* RustSyncManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RustSyncManagerTests.swift; sourceTree = ""; }; F80DF7402703BC8E00E4C37D /* CredentialPasscodeRequirementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialPasscodeRequirementViewController.swift; sourceTree = ""; }; F82F4AB9A3C09B181AB61F73 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Shared.strings"; sourceTree = ""; }; - F8324A052649A188007E4BFA /* CredentialProvider.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = CredentialProvider.appex; sourceTree = BUILT_PRODUCTS_DIR; }; F8324A062649A188007E4BFA /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; }; F8324A092649A188007E4BFA /* CredentialProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderViewController.swift; sourceTree = ""; }; F8324A0E2649A188007E4BFA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -9241,6 +9547,62 @@ FFEB4308B5A82E56F1EEEC56 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/AuthenticationManager.strings; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 2CFE997D2D45329500B25CE0 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + publicHeaders = ( + Ecosia.h, + ); + target = 2CFE99652D45329200B25CE0 /* Ecosia */; + }; + 2CFE9FC12D4534A100B25CE0 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + SnapshotTests/DeviceType.swift, + SnapshotTests/environment.json, + SnapshotTests/LocaleRetriever.swift, + SnapshotTests/LocalizationOverrideTestingBundle.swift, + SnapshotTests/NTP/NTPComponentTests.swift, + SnapshotTests/NTP/NTPTests.swift, + SnapshotTests/Onboarding/OnboardingTests.swift, + SnapshotTests/snapshot_configuration.json, + SnapshotTests/SnapshotBaseTests.swift, + SnapshotTests/SnapshotTestHelper.swift, + SnapshotTests/SnapshotTests.xctestplan, + "SnapshotTests/String+Extension.swift", + UnitTest.xctestplan, + ); + target = 2CFE996E2D45329300B25CE0 /* EcosiaTests */; + }; + 2CFE9FD72D453C6800B25CE0 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Mocks/EcosiaMockThemeManager.swift, + Mocks/MockWelcomeDelegate.swift, + SnapshotTests/DeviceType.swift, + SnapshotTests/environment.json, + SnapshotTests/LocaleRetriever.swift, + SnapshotTests/LocalizationOverrideTestingBundle.swift, + SnapshotTests/NTP/NTPComponentTests.swift, + SnapshotTests/NTP/NTPTests.swift, + SnapshotTests/Onboarding/OnboardingTests.swift, + SnapshotTests/snapshot_configuration.json, + SnapshotTests/SnapshotBaseTests.swift, + SnapshotTests/SnapshotTestHelper.swift, + "SnapshotTests/String+Extension.swift", + ); + target = 2C6C90812C614A16007D9B43 /* EcosiaSnapshotTests */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 2CFE99672D45329200B25CE0 /* Ecosia */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2CFE997D2D45329500B25CE0 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Ecosia; sourceTree = ""; }; + 2CFE99752D45329400B25CE0 /* EcosiaTests */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2CFE9FD72D453C6800B25CE0 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 2CFE9FC12D4534A100B25CE0 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = EcosiaTests; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 047F9B2424E1FE1C00CD7DF7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -9285,6 +9647,7 @@ buildActionMask = 2147483647; files = ( 43A878FD27AB4A110071C372 /* RustMozillaAppServices.framework in Frameworks */, + 2CFE9FE32D453F0600B25CE0 /* Ecosia.framework in Frameworks */, 8A88815C2B2103AD009635AE /* WebEngine in Frameworks */, 8A88815A2B20FFE0009635AE /* GCDWebServers in Frameworks */, 5A9FF8492942454600DF9FBB /* Common in Frameworks */, @@ -9295,12 +9658,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1230CFF32EBB990900D7AC00 /* BrazeUI in Frameworks */, + 1230CFF22EBB990900D7AC00 /* BrazeKit in Frameworks */, 5A70EF0E295DFCCF00790249 /* Common in Frameworks */, 8A08EC6427EBDCAD00E119C7 /* AdServices.framework in Frameworks */, 8A08EC6227EBDCA400E119C7 /* iAd.framework in Frameworks */, 43BE580E278BABCF00491291 /* RustMozillaAppServices.framework in Frameworks */, C82043852523DBF600740B71 /* Sync.framework in Frameworks */, - 5A37861929A2C337006B3A34 /* Sentry in Frameworks */, 8AF2D0FC2A5F272A00C7DD69 /* ComponentLibrary in Frameworks */, 5A871490292EA3910039A5BD /* SiteImageView in Frameworks */, C877037A25222F30006E38EB /* Shared.framework in Frameworks */, @@ -9311,8 +9675,10 @@ E17798A02BD8043800F6F0EB /* ToolbarKit in Frameworks */, E6231C011B90A44F005ABB0D /* libz.tbd in Frameworks */, E4B3348A1BBF23F9004E2BFF /* AdSupport.framework in Frameworks */, + ED6C8DAC2CE6A4BB00D7F7F3 /* Sentry-Dynamic in Frameworks */, 5A984D362C8A3407007938C9 /* Kingfisher in Frameworks */, 819656172C80C6F300E62323 /* MenuKit in Frameworks */, + 1251623C2D59FB30005CB958 /* Ecosia.framework in Frameworks */, 8AB30EC82B6C038600BD9A9B /* Lottie in Frameworks */, 5A871488292EA1440039A5BD /* Fuzi in Frameworks */, 432BD0242790EBD000A0F3C3 /* Adjust in Frameworks */, @@ -9322,18 +9688,53 @@ 433F87CE2788EAB600693368 /* GCDWebServers in Frameworks */, 5A06135A29D6052E008F3D38 /* TabDataStore in Frameworks */, 216A0D762A40E7AB008077BA /* Redux in Frameworks */, + 12C25BED2D27F0A40048BADA /* SnowplowTracker in Frameworks */, 7B8A47F61D01D3B400C07734 /* PassKit.framework in Frameworks */, 5A8FD0EC293A7D5E00333AA7 /* SnapKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; + 2C6C907F2C614A16007D9B43 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C6C908F2C614A6C007D9B43 /* SnapshotTesting in Frameworks */, + 2C69DA7B2C6225C400D7F69F /* Common in Frameworks */, + 2CFEA0B62D46427600B25CE0 /* RustMozillaAppServices.framework in Frameworks */, + 12A536372D3E89BF00924CB0 /* GCDWebServers in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2CFE99632D45329200B25CE0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2CFE9FD02D45364100B25CE0 /* SnowplowTracker in Frameworks */, + 2CFE9FCC2D45363500B25CE0 /* BrazeKit in Frameworks */, + 2CFE9FCE2D45363500B25CE0 /* BrazeUI in Frameworks */, + 2CFE9FD22D45364600B25CE0 /* Common in Frameworks */, + 2CFE9FEA2D45404800B25CE0 /* SwiftSoup in Frameworks */, + 2C8723942EA930C50085902C /* Auth0 in Frameworks */, + 2C2EA77F2DA3F2990085F5BC /* Lottie in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2CFE996C2D45329300B25CE0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2CFE9FC02D45348700B25CE0 /* RustMozillaAppServices.framework in Frameworks */, + 2CFE9FBF2D45348200B25CE0 /* SnowplowTracker in Frameworks */, + 2CC0C2AD2DA804EA006FE9B7 /* ViewInspector in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2FA435F71ABB83B4008031D1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 43BE5820278BE68100491291 /* RustMozillaAppServices.framework in Frameworks */, 8A8BAE122B2107E400D774EB /* GCDWebServers in Frameworks */, - F8DEACC52A3D43DA00C3B19D /* Sentry in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9349,6 +9750,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2CFE9FC22D4535FD00B25CE0 /* Ecosia.framework in Frameworks */, 5A984D312C89FD88007938C9 /* SiteImageView in Frameworks */, 43BE5809278BA9D700491291 /* RustMozillaAppServices.framework in Frameworks */, 8A88815E2B21071E009635AE /* GCDWebServers in Frameworks */, @@ -9365,31 +9767,11 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 397848D81ED86605004C0C0B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 43BE5825278BE7EF00491291 /* RustMozillaAppServices.framework in Frameworks */, - C82043AE2523DC8B00740B71 /* Sync.framework in Frameworks */, - C877039725222FE6006E38EB /* Shared.framework in Frameworks */, - 5A37861D29A2C37C006B3A34 /* Common in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 3BFE4B041D342FB800DDF53F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 43C6A47F27A0679300C79856 /* MappaMundi in Frameworks */, - 8A05B0052A69A0C40011B622 /* Common in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 43BE577D278BA4D900491291 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 43A878FC27AB498E0071C372 /* MozillaAppServices in Frameworks */, + 12C25BD62D27D9510048BADA /* MozillaAppServices in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9418,31 +9800,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D39FA15C1A83E0EC00EE869C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 8A05B0072A69A25C0011B622 /* Common in Frameworks */, - E6231C071B90A712005ABB0D /* libz.tbd in Frameworks */, - 433F87D52788F01500693368 /* KIF in Frameworks */, - D36998891AD70A0A00650C6C /* IOKit.framework in Frameworks */, - D39FA16C1A83E17800EE869C /* CoreGraphics.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F8324A022649A188007E4BFA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 43BE5816278BE53E00491291 /* RustMozillaAppServices.framework in Frameworks */, - F8324A072649A188007E4BFA /* AuthenticationServices.framework in Frameworks */, - 5A8FD0F2293A7D9000333AA7 /* SnapKit in Frameworks */, - F8324AF62649B540007E4BFA /* Shared.framework in Frameworks */, - F8324AFA2649B540007E4BFA /* Sync.framework in Frameworks */, - 5A70EF16295DFD9000790249 /* Common in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; F84B21D01A090F8100AAB793 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -9500,14 +9857,6 @@ path = SearchQuickLinksMedium; sourceTree = ""; }; - 0B11AF062CB416FE00AD51D5 /* Metrics */ = { - isa = PBXGroup; - children = ( - 0B11AF002CB412D100AD51D5 /* Metrics.swift */, - ); - path = Metrics; - sourceTree = ""; - }; 0B7CF8842CAC1B7000DC02F8 /* Legacy */ = { isa = PBXGroup; children = ( @@ -9609,6 +9958,35 @@ path = View; sourceTree = ""; }; + 123283EB2F210389009EA5BC /* FirstSearch */ = { + isa = PBXGroup; + children = ( + 128D685F2F2A3AE700F4B3EE /* LocalizedSearchSuggestions.swift */, + 123283EC2F210C10009EA5BC /* NTPFirstSearchCell.swift */, + 123283EE2F210C23009EA5BC /* NTPFirstSearchViewModel.swift */, + 128D6E812F2A94CF00F4B3EE /* SearchSuggestionFlowLayout.swift */, + 120ED1352F446A260056F398 /* NTPFirstSearchView.swift */, + ); + path = FirstSearch; + sourceTree = ""; + }; + 123CEC4A2E780AC2009EB379 /* Font */ = { + isa = PBXGroup; + children = ( + 123CEC4B2E7814A2009EB379 /* FoundersGroteskCondensed-Semibold.ttf */, + ); + path = Font; + sourceTree = ""; + }; + 124DAE782D2FD0C90050104C /* RecoveredFromUpgrade */ = { + isa = PBXGroup; + children = ( + 12AEB1252D3A51670035D7D8 /* LegacySessionData.swift */, + 12AEB1272D3A568D0035D7D8 /* SessionRestoreHandler.swift */, + ); + path = RecoveredFromUpgrade; + sourceTree = ""; + }; 1D7B78952ADF324E0011E9F2 /* Event Queue */ = { isa = PBXGroup; children = ( @@ -9842,7 +10220,7 @@ 1D8487B52AD6038100F7527C /* RemoteTabPanelStateTests.swift */, 1D3C90872ACE1AF400304C87 /* RemoteTabPanelTests.swift */, 219935F02B07DFA200E5966F /* TabDisplayPanelTests.swift */, - 21ED80B22AF2E43A0065D4C7 /* TabDisplayViewTests.swift */, + 21ED80B22AF2E43A0065D4C7 /* TabDisplayDiffableDataSourceTests.swift */, 21F2A2D32B0D194A00626AEC /* TabsPanelStateTests.swift */, 21D8EA922ABE04F7003FF16E /* TabTrayViewControllerTests.swift */, ); @@ -9902,7 +10280,6 @@ 21C5B3582AF2A7130093F366 /* Views */, 21E77E512AA8BE5C00FABA10 /* TabTrayFlagManager.swift */, 21B548942B1E5F1400DC1DF8 /* InactiveTabsManager.swift */, - 5AA1D8262BC09ECB00957516 /* TabTrayAnimationQueue.swift */, ); path = Tabs; sourceTree = ""; @@ -10020,132 +10397,474 @@ path = Extensions; sourceTree = ""; }; - 2F44FC551A9E83E200FD20CC /* Settings */ = { + 2C00E1632E29A70500811A76 /* TabManagement */ = { isa = PBXGroup; children = ( - A5519CF32B5D572A0062BECB /* SearchSettings */, - 216A0D772A40E83F008077BA /* ThemeSettings */, - 8A3EF7ED2A2FCEC900796E3A /* Main */, - EBB895322193FFF400EB91A0 /* ContentBlockerSettingViewController.swift */, - 8D8251721F4DE67E00780643 /* AdvancedAccountSettingViewController.swift */, - 0B62EFD11AD63CD100ACB9CD /* Clearables.swift */, - D3E8EEE71B97A87A001900FB /* ClearPrivateDataTableViewController.swift */, - 3B39EDCA1E16E1AA00EF029F /* CustomSearchViewController.swift */, - BCFF93F32AAF9879005B5B71 /* FirefoxSuggestSettingsViewController.swift */, - DFACBF83277B9B36003D5F41 /* HomepageSettings */, - C8656D76270F858900E199EA /* TabsSettingsViewControler.swift */, - C82F4C2A29AE2DF0005BD116 /* NotificationsSettingsViewController.swift */, - D81E45121F82C56C004EFFBA /* NewTabContentSettingsViewController.swift */, - 2F44FCCA1A9E972E00FD20CC /* SearchEnginePicker.swift */, - 2F44FCC61A9E8CF500FD20CC /* SearchSettingsTableViewController.swift */, - 74E36D771B71323500D69DA1 /* SettingsContentViewController.swift */, - D04CD717215EBD85004FF5B0 /* SettingsLoadingView.swift */, - 2F44FC711A9E840300FD20CC /* SettingsNavigationController.swift */, - 2F44FCC41A9E85E900FD20CC /* SettingsTableViewController.swift */, - F85C7F0D2711C555004BDBA4 /* SettingsViewController.swift */, - D821E9052141B71C00452C55 /* SiriSettingsViewController.swift */, - CEFA977D1FAA6B490016F365 /* SyncContentSettingsViewController.swift */, - 8AE1E1CE27B191160024C45E /* SearchBar */, - C22753422A3CA25100B9C0D1 /* WebsiteDataManagement */, - 211046CC2A7D842A00A7309F /* TPAccessoryInfo.swift */, + 2C8723952EA9313D0085902C /* TabAutoCloseManager.swift */, + 2C00E1622E29A70500811A76 /* InvisibleTabManager.swift */, ); - path = Settings; + path = TabManagement; sourceTree = ""; }; - 2FA435FC1ABB83B4008031D1 /* Account */ = { + 2C0174952E26E5DA00136A6F /* Auth */ = { isa = PBXGroup; children = ( - 2F14E1391ABB890800FF98DB /* Account-Bridging-Header.h */, - 3905B4D41E8E7A6B0027D953 /* FxAPushMessageHandler.swift */, - 2FA435FD1ABB83B4008031D1 /* Supporting Files */, + 2CE48ED92E29B5C400CE84CE /* EcosiaAuth.swift */, + 2C00E38F2E29B0E700811A76 /* EcosiaAuthFlow.swift */, + 2C00E1632E29A70500811A76 /* TabManagement */, + 2C00E15D2E299AE800811A76 /* InvisibleTabSession.swift */, ); - path = Account; + path = Auth; sourceTree = ""; }; - 2FA435FD1ABB83B4008031D1 /* Supporting Files */ = { + 2C0174962E26E5DA00136A6F /* Account */ = { isa = PBXGroup; children = ( - 2FA435FE1ABB83B4008031D1 /* Info.plist */, + 2C87239D2EA931DB0085902C /* AccountsProviderWrapper.swift */, + 2C0174952E26E5DA00136A6F /* Auth */, ); - name = "Supporting Files"; + path = Account; sourceTree = ""; }; - 2FA4360B1ABB83B4008031D1 /* AccountTests */ = { + 2C1296EC2B0BA5FC00FC0A04 /* Frontend */ = { isa = PBXGroup; children = ( - 39B0646D1E7ADA4B000BE173 /* Push */, - 2FA4360C1ABB83B4008031D1 /* Supporting Files */, + 2C1296ED2B0BA60400FC0A04 /* Home */, ); - path = AccountTests; + path = Frontend; sourceTree = ""; }; - 2FA4360C1ABB83B4008031D1 /* Supporting Files */ = { + 2C1296ED2B0BA60400FC0A04 /* Home */ = { isa = PBXGroup; children = ( - 2FA4360D1ABB83B4008031D1 /* Info.plist */, + 12C11E212D281B7200E4DDBF /* EcosiaHomepageSectionType.swift */, + 2C7DC2342B15F6C700C049C8 /* TopSites */, ); - name = "Supporting Files"; + path = Home; sourceTree = ""; }; - 2FCAE21B1ABB51F800877008 /* Storage */ = { + 2C7DC2332B15F6BB00C049C8 /* Cell */ = { isa = PBXGroup; children = ( - D37DE2821CA2047500A5EC69 /* CertStore.swift */, - 28C4AB711AD42D4300D9ACE3 /* Clients.swift */, - 2FCAE2411ABB531100877008 /* Cursor.swift */, - 28302E3F1AF0747800521E2E /* DatabaseError.swift */, - 394CF6CE1BAA493C00906917 /* DefaultSuggestedSites.swift */, - D3BF8CBA1B7425570007AFE6 /* DiskImageStore.swift */, - E65075C11E37F956006961AC /* ExtensionUtils.swift */, - 2FCAE2431ABB531100877008 /* FileAccessor.swift */, - 45CC573D28AD8B64006D55AA /* Generated */, - 45CC573828AD8881006D55AA /* metrics.yaml */, - E6FF6AC91D873CFF0070C294 /* PageMetadata.swift */, - 45D5EDCB292D839700311934 /* PinnedSites.swift */, - 285D3B671B4380B70035FD22 /* Queue.swift */, - D076971E206AC60900FACFD8 /* ReadingList.swift */, - 74B195431CF503FC007F36EF /* RecentlyClosedTabs.swift */, - 2FCAE2471ABB531100877008 /* RemoteTabs.swift */, - D057B2AC22022617000614E0 /* Rust */, - 2829D39F1C2F0AD400DCF931 /* Sharing.swift */, - 2FCAE2481ABB531100877008 /* Site.swift */, - 2FCAE2491ABB531100877008 /* SQL */, - 2FCAE33D1ABB5F1800877008 /* Storage-Bridging-Header.h */, - 0B54BD181B698B7C004C822C /* SuggestedSites.swift */, - 2FCAE25A1ABB531100877008 /* ThirdParty */, - 2FCAE25C1ABB531100877008 /* Visit.swift */, - BD6CC84129CDDA3400546A5D /* ZoomLevelStore.swift */, + 12C11E222D281B7A00E4DDBF /* EcosiaTopSiteItemCell.swift */, ); - path = Storage; + path = Cell; sourceTree = ""; }; - 2FCAE22A1ABB51F800877008 /* StorageTests */ = { + 2C7DC2342B15F6C700C049C8 /* TopSites */ = { isa = PBXGroup; children = ( - D37DE2C61CA356D800A5EC69 /* CertTests.swift */, - D3BF8CBC1B7472FA0007AFE6 /* DiskImageStoreTests.swift */, - 2FCAE2791ABB533A00877008 /* MockFiles.swift */, - F8AAC1B5296637B7000BCDEC /* RustAutofillTests.swift */, - D0148A01220C8C93008A6EEF /* RustLoginsTests.swift */, - D980063C26D8308700321BC1 /* RustPlacesTests.swift */, - 158241272820698B00956B39 /* RustRemoteTabsTests.swift */, - 289A4C121C4EB90600A460E3 /* StorageTestUtils.swift */, - 2FCAE22B1ABB51F800877008 /* Supporting Files */, - 281B02991C037C1F005202C3 /* TestBrowserDB.swift */, - D37DE2C81CA356F900A5EC69 /* testcert1.pem */, - D37DE2C91CA356F900A5EC69 /* testcert2.pem */, - 45D5EDCF292D854000311934 /* TestSQLitePinnedSites.swift */, - D0B693D4206C5BB9008A8B11 /* TestSQLiteReadingList.swift */, - D32CACEC1AE04DA1000658EB /* TestSwiftData.swift */, - BD57D9A629D4C42B00039394 /* ZoomLevelStoreTests.swift */, + 2C7DC2332B15F6BB00C049C8 /* Cell */, ); - path = StorageTests; + path = TopSites; sourceTree = ""; }; - 2FCAE22B1ABB51F800877008 /* Supporting Files */ = { + 2CB288D92B07C8EF00A8FCB3 /* Ecosia */ = { isa = PBXGroup; children = ( - 2891F2B91F991185001B105E /* fixtures */, + 2C0174962E26E5DA00136A6F /* Account */, + 2CFE9FEF2D4557EF00B25CE0 /* PersistedGenerated */, + 124DAE782D2FD0C90050104C /* RecoveredFromUpgrade */, + 2CB288DC2B07C8EF00A8FCB3 /* UI */, + 2CB289242B07C8F000A8FCB3 /* Settings */, + 2C1296EC2B0BA5FC00FC0A04 /* Frontend */, + 2CB289412B07C8F000A8FCB3 /* Network */, + 2CB289432B07C8F000A8FCB3 /* Extensions */, + 2CB289532B07C8F000A8FCB3 /* Bookmarks */, + ); + path = Ecosia; + sourceTree = ""; + }; + 2CB288DC2B07C8EF00A8FCB3 /* UI */ = { + isa = PBXGroup; + children = ( + 123CEC4A2E780AC2009EB379 /* Font */, + 2CFE9FF92D455BD500B25CE0 /* Ecosia.xcassets */, + 123475CB2DA7D6580017B0C2 /* BeforeOrAfterView.swift */, + 2CFE9FFA2D455BD500B25CE0 /* EcosiaFindInPageBar.swift */, + 2CFE9FFB2D455BD500B25CE0 /* EcosiaNavigation.swift */, + 2CFE9FFC2D455BD500B25CE0 /* EcosiaPrimaryButton.swift */, + 2CFE9FFD2D455BD500B25CE0 /* EmptyBookmarksView.swift */, + 2CFE9FFE2D455BD500B25CE0 /* EmptyBookmarksViewDelegate.swift */, + 2CFE9FFF2D455BD500B25CE0 /* EmptyHeader.swift */, + 2CFEA0002D455BD500B25CE0 /* EmptyReadingListView.swift */, + 2CFEA0012D455BD500B25CE0 /* FilterController.swift */, + 2CFEA0042D455BD500B25CE0 /* LaunchScreen */, + 2CFEA0052D455BD500B25CE0 /* LoadingScreen.swift */, + 2CFEA0062D455BD500B25CE0 /* MarketsController.swift */, + 2CFEA0092D455BD500B25CE0 /* MultiplyImpact */, + 2CFEA0382D455BD500B25CE0 /* NTP */, + 2CFEA0422D455BD500B25CE0 /* ProductTour */, + 2CFEA0462D455BD500B25CE0 /* PageAction */, + 2CFEA04B2D455BD500B25CE0 /* Theme */, + 2CFEA0532D455BD500B25CE0 /* WhatsNew */, + ); + path = UI; + sourceTree = ""; + }; + 2CB289242B07C8F000A8FCB3 /* Settings */ = { + isa = PBXGroup; + children = ( + 2CB289252B07C8F000A8FCB3 /* EcosiaDebugSettings.swift */, + 2CB289262B07C8F000A8FCB3 /* EcosiaSettings.swift */, + 2CB289272B07C8F000A8FCB3 /* NTPCustomizationSettingsViewController.swift */, + ); + path = Settings; + sourceTree = ""; + }; + 2CB289412B07C8F000A8FCB3 /* Network */ = { + isa = PBXGroup; + children = ( + 2CB289422B07C8F000A8FCB3 /* WebsiteConnectionStatus.swift */, + ); + path = Network; + sourceTree = ""; + }; + 2CB289432B07C8F000A8FCB3 /* Extensions */ = { + isa = PBXGroup; + children = ( + 2C8723972EA931490085902C /* BrowserViewController+EcosiaErrorHandling.swift */, + 120059172F3C8BD800D65907 /* BrowserViewController+EcosiaNavigationHandling.swift */, + 2C8723982EA931490085902C /* Tab+InvisibleTab.swift */, + 2C8723992EA931490085902C /* TabManager+InvisibleTab.swift */, + 2CB5C33E2E45F02D00B1048F /* SearchViewController+Ecosia.swift */, + 2CB289442B07C8F000A8FCB3 /* BrowserViewController+Ecosia.swift */, + 12C11E9C2D28289900E4DDBF /* UIButton+Ecosia.swift */, + 2CB289452B07C8F000A8FCB3 /* UIFont+Ecosia.swift */, + 2CB289462B07C8F000A8FCB3 /* ErrorPageHandler+Ecosia.swift */, + 2CB289472B07C8F000A8FCB3 /* SnapKit+Ecosia.swift */, + 2CB289482B07C8F000A8FCB3 /* HomepageViewController+Ecosia.swift */, + 12C11E9D2D2828AA00E4DDBF /* SimpleToast+Ecosia.swift */, + 2CB2894A2B07C8F000A8FCB3 /* UIView+maskedCorners.swift */, + 2CB2894C2B07C8F000A8FCB3 /* NumberFormatter+Ecosia.swift */, + 2CB2894D2B07C8F000A8FCB3 /* URL+Ecosia.swift */, + 2CF2206E2B72B0530038157D /* AppSettingsTableViewController+Ecosia.swift */, + 12C11EA02D2828EA00E4DDBF /* DispatchQueueHelper+BuildChannel.swift */, + 12348FF92DB8D2CC0017B0C2 /* PrefsKeys+Ecosia.swift */, + 129F18FD2EC5F04200E870C0 /* Task+Sleep.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 2CB289532B07C8F000A8FCB3 /* Bookmarks */ = { + isa = PBXGroup; + children = ( + 2CB289542B07C8F000A8FCB3 /* BookmarksExchange.swift */, + ); + path = Bookmarks; + sourceTree = ""; + }; + 2CC242722D520DE80098467A /* Recovered References */ = { + isa = PBXGroup; + children = ( + ); + name = "Recovered References"; + sourceTree = ""; + }; + 2CE9E8062E43927000141C6D /* Header */ = { + isa = PBXGroup; + children = ( + 2CE9E8042E43927000141C6D /* NTPHeader.swift */, + 2CE9E8052E43927000141C6D /* NTPHeaderViewModel.swift */, + ); + path = Header; + sourceTree = ""; + }; + 2CFE9FEC2D4557EF00B25CE0 /* Metrics */ = { + isa = PBXGroup; + children = ( + 2CFE9FEB2D4557EF00B25CE0 /* Metrics.swift */, + ); + path = Metrics; + sourceTree = ""; + }; + 2CFE9FEF2D4557EF00B25CE0 /* PersistedGenerated */ = { + isa = PBXGroup; + children = ( + 2CFE9FEC2D4557EF00B25CE0 /* Metrics */, + 2CFE9FED2D4557EF00B25CE0 /* FxNimbus.swift */, + 2CFE9FEE2D4557EF00B25CE0 /* FxNimbusMessaging.swift */, + ); + path = PersistedGenerated; + sourceTree = ""; + }; + 2CFEA0042D455BD500B25CE0 /* LaunchScreen */ = { + isa = PBXGroup; + children = ( + 2CFEA0022D455BD500B25CE0 /* EcosiaLaunchScreen.xib */, + 2CFEA0032D455BD500B25CE0 /* EcosiaLaunchScreenView.swift */, + ); + path = LaunchScreen; + sourceTree = ""; + }; + 2CFEA0092D455BD500B25CE0 /* MultiplyImpact */ = { + isa = PBXGroup; + children = ( + 2CFEA0072D455BD500B25CE0 /* MultiplyImpact.swift */, + 2CFEA0082D455BD500B25CE0 /* MultiplyImpactStep.swift */, + ); + path = MultiplyImpact; + sourceTree = ""; + }; + 2CFEA01D2D455BD500B25CE0 /* Customization */ = { + isa = PBXGroup; + children = ( + 2CFEA01A2D455BD500B25CE0 /* CustomizableNTPSettingConfig.swift */, + 2CFEA01B2D455BD500B25CE0 /* NTPCustomizationCell.swift */, + 2CFEA01C2D455BD500B25CE0 /* NTPCustomizationCellViewModel.swift */, + ); + path = Customization; + sourceTree = ""; + }; + 2CFEA0242D455BD500B25CE0 /* Impact */ = { + isa = PBXGroup; + children = ( + 2CFEA01E2D455BD500B25CE0 /* ClimateImpactInfo.swift */, + 2CFEA01F2D455BD500B25CE0 /* NTPImpactCell.swift */, + 2CFEA0202D455BD500B25CE0 /* NTPImpactCellViewModel.swift */, + 2CFEA0212D455BD500B25CE0 /* NTPImpactDividerFooter.swift */, + 2CFEA0222D455BD500B25CE0 /* NTPImpactRowView.swift */, + 2CFEA0232D455BD500B25CE0 /* ProgressView.swift */, + ); + path = Impact; + sourceTree = ""; + }; + 2CFEA0282D455BD500B25CE0 /* Library */ = { + isa = PBXGroup; + children = ( + 2CFEA0252D455BD500B25CE0 /* NTPLibaryCellViewModel.swift */, + 2CFEA0262D455BD500B25CE0 /* NTPLibraryCell.swift */, + 2CFEA0272D455BD500B25CE0 /* NTPLibraryShortcutView.swift */, + ); + path = Library; + sourceTree = ""; + }; + 2CFEA02A2D455BD500B25CE0 /* Logo */ = { + isa = PBXGroup; + children = ( + 2CFEA0292D455BD500B25CE0 /* NTPLogoCell.swift */, + ); + path = Logo; + sourceTree = ""; + }; + 2CFEA02E2D455BD500B25CE0 /* News */ = { + isa = PBXGroup; + children = ( + 2CFEA02B2D455BD500B25CE0 /* NewsController.swift */, + 2CFEA02C2D455BD500B25CE0 /* NTPNewsCell.swift */, + 2CFEA02D2D455BD500B25CE0 /* NTPNewsCellViewModel.swift */, + ); + path = News; + sourceTree = ""; + }; + 2CFEA0312D455BD500B25CE0 /* NudgeCards */ = { + isa = PBXGroup; + children = ( + 2CFEA02F2D455BD500B25CE0 /* NTPConfigurableNudgeCardCell.swift */, + 2CFEA0302D455BD500B25CE0 /* NTPConfigurableNudgeCardCellViewModel.swift */, + ); + path = NudgeCards; + sourceTree = ""; + }; + 2CFEA0382D455BD500B25CE0 /* NTP */ = { + isa = PBXGroup; + children = ( + 2CFEA01D2D455BD500B25CE0 /* Customization */, + 123283EB2F210389009EA5BC /* FirstSearch */, + 2CE9E8062E43927000141C6D /* Header */, + 2CFEA0242D455BD500B25CE0 /* Impact */, + 2CFEA0282D455BD500B25CE0 /* Library */, + 2CFEA02A2D455BD500B25CE0 /* Logo */, + 2CFEA02E2D455BD500B25CE0 /* News */, + 2CFEA0312D455BD500B25CE0 /* NudgeCards */, + 2CFEA0322D455BD500B25CE0 /* CircleButton.swift */, + 2CFEA0332D455BD500B25CE0 /* DefaultBrowserViewController.swift */, + 2CFEA0342D455BD500B25CE0 /* NTPLayout.swift */, + 2CFEA0352D455BD500B25CE0 /* NTPTooltip.swift */, + 2CFEA0362D455BD500B25CE0 /* NTPTooltip.Highlight.swift */, + 2CFEA0372D455BD500B25CE0 /* NTPTooltipDelegate.swift */, + ); + path = NTP; + sourceTree = ""; + }; + 2CFEA0422D455BD500B25CE0 /* ProductTour */ = { + isa = PBXGroup; + children = ( + 120EBBB52F3F35F60056F398 /* BrowserViewController+ProductTourSpotlight.swift */, + 121C6E172EC1EF870060319A /* BrowserViewController+WelcomeTransition.swift */, + 120EBBA32F3F31140056F398 /* ProductTourSpotlightCoordinator.swift */, + 120EBBA12F3F30FA0056F398 /* SpotlightToast.swift */, + ); + path = ProductTour; + sourceTree = ""; + }; + 2CFEA0462D455BD500B25CE0 /* PageAction */ = { + isa = PBXGroup; + children = ( + 2CFEA0432D455BD500B25CE0 /* PageActionMenu.swift */, + 2CFEA0442D455BD500B25CE0 /* PageActionMenuCell.swift */, + 2CFEA0452D455BD500B25CE0 /* PageActionsShortcutsHeader.swift */, + ); + path = PageAction; + sourceTree = ""; + }; + 2CFEA04B2D455BD500B25CE0 /* Theme */ = { + isa = PBXGroup; + children = ( + 2CC2465D2D520EF90098467A /* EcosiaDarkTheme.swift */, + 2CC2465E2D520EF90098467A /* EcosiaLightTheme.swift */, + 2CFEA04A2D455BD500B25CE0 /* EcosiaThemeManager.swift */, + ); + path = Theme; + sourceTree = ""; + }; + 2CFEA04E2D455BD500B25CE0 /* DataProvider */ = { + isa = PBXGroup; + children = ( + 2CFEA04C2D455BD500B25CE0 /* WhatsNewDataProvider.swift */, + 2CFEA04D2D455BD500B25CE0 /* WhatsNewLocalDataProvider.swift */, + ); + path = DataProvider; + sourceTree = ""; + }; + 2CFEA0532D455BD500B25CE0 /* WhatsNew */ = { + isa = PBXGroup; + children = ( + 2CFEA04E2D455BD500B25CE0 /* DataProvider */, + 2CFEA04F2D455BD500B25CE0 /* WhatsNewCell.swift */, + 2CFEA0502D455BD500B25CE0 /* WhatsNewItem.swift */, + 2CFEA0512D455BD500B25CE0 /* WhatsNewViewController.swift */, + 2CFEA0522D455BD500B25CE0 /* WhatsNewViewModel.swift */, + ); + path = WhatsNew; + sourceTree = ""; + }; + 2F44FC551A9E83E200FD20CC /* Settings */ = { + isa = PBXGroup; + children = ( + A5519CF32B5D572A0062BECB /* SearchSettings */, + 216A0D772A40E83F008077BA /* ThemeSettings */, + 8A3EF7ED2A2FCEC900796E3A /* Main */, + EBB895322193FFF400EB91A0 /* ContentBlockerSettingViewController.swift */, + 8D8251721F4DE67E00780643 /* AdvancedAccountSettingViewController.swift */, + 0B62EFD11AD63CD100ACB9CD /* Clearables.swift */, + D3E8EEE71B97A87A001900FB /* ClearPrivateDataTableViewController.swift */, + 3B39EDCA1E16E1AA00EF029F /* CustomSearchViewController.swift */, + BCFF93F32AAF9879005B5B71 /* FirefoxSuggestSettingsViewController.swift */, + DFACBF83277B9B36003D5F41 /* HomepageSettings */, + C8656D76270F858900E199EA /* TabsSettingsViewControler.swift */, + C82F4C2A29AE2DF0005BD116 /* NotificationsSettingsViewController.swift */, + D81E45121F82C56C004EFFBA /* NewTabContentSettingsViewController.swift */, + 2F44FCCA1A9E972E00FD20CC /* SearchEnginePicker.swift */, + 2F44FCC61A9E8CF500FD20CC /* SearchSettingsTableViewController.swift */, + 74E36D771B71323500D69DA1 /* SettingsContentViewController.swift */, + D04CD717215EBD85004FF5B0 /* SettingsLoadingView.swift */, + 2F44FC711A9E840300FD20CC /* SettingsNavigationController.swift */, + 2F44FCC41A9E85E900FD20CC /* SettingsTableViewController.swift */, + F85C7F0D2711C555004BDBA4 /* SettingsViewController.swift */, + D821E9052141B71C00452C55 /* SiriSettingsViewController.swift */, + CEFA977D1FAA6B490016F365 /* SyncContentSettingsViewController.swift */, + 8AE1E1CE27B191160024C45E /* SearchBar */, + C22753422A3CA25100B9C0D1 /* WebsiteDataManagement */, + 211046CC2A7D842A00A7309F /* TPAccessoryInfo.swift */, + ); + path = Settings; + sourceTree = ""; + }; + 2FA435FC1ABB83B4008031D1 /* Account */ = { + isa = PBXGroup; + children = ( + 2F14E1391ABB890800FF98DB /* Account-Bridging-Header.h */, + 3905B4D41E8E7A6B0027D953 /* FxAPushMessageHandler.swift */, + 2FA435FD1ABB83B4008031D1 /* Supporting Files */, + ); + path = Account; + sourceTree = ""; + }; + 2FA435FD1ABB83B4008031D1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 2FA435FE1ABB83B4008031D1 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 2FA4360B1ABB83B4008031D1 /* AccountTests */ = { + isa = PBXGroup; + children = ( + 39B0646D1E7ADA4B000BE173 /* Push */, + 2FA4360C1ABB83B4008031D1 /* Supporting Files */, + ); + path = AccountTests; + sourceTree = ""; + }; + 2FA4360C1ABB83B4008031D1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 2FA4360D1ABB83B4008031D1 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 2FCAE21B1ABB51F800877008 /* Storage */ = { + isa = PBXGroup; + children = ( + D37DE2821CA2047500A5EC69 /* CertStore.swift */, + 28C4AB711AD42D4300D9ACE3 /* Clients.swift */, + 2FCAE2411ABB531100877008 /* Cursor.swift */, + 28302E3F1AF0747800521E2E /* DatabaseError.swift */, + 394CF6CE1BAA493C00906917 /* DefaultSuggestedSites.swift */, + D3BF8CBA1B7425570007AFE6 /* DiskImageStore.swift */, + E65075C11E37F956006961AC /* ExtensionUtils.swift */, + 2FCAE2431ABB531100877008 /* FileAccessor.swift */, + 45CC573D28AD8B64006D55AA /* Generated */, + 45CC573828AD8881006D55AA /* metrics.yaml */, + E6FF6AC91D873CFF0070C294 /* PageMetadata.swift */, + 45D5EDCB292D839700311934 /* PinnedSites.swift */, + 285D3B671B4380B70035FD22 /* Queue.swift */, + D076971E206AC60900FACFD8 /* ReadingList.swift */, + 74B195431CF503FC007F36EF /* RecentlyClosedTabs.swift */, + 2FCAE2471ABB531100877008 /* RemoteTabs.swift */, + D057B2AC22022617000614E0 /* Rust */, + 2829D39F1C2F0AD400DCF931 /* Sharing.swift */, + 2FCAE2481ABB531100877008 /* Site.swift */, + 2FCAE2491ABB531100877008 /* SQL */, + 2FCAE33D1ABB5F1800877008 /* Storage-Bridging-Header.h */, + 0B54BD181B698B7C004C822C /* SuggestedSites.swift */, + 2FCAE25A1ABB531100877008 /* ThirdParty */, + 2FCAE25C1ABB531100877008 /* Visit.swift */, + BD6CC84129CDDA3400546A5D /* ZoomLevelStore.swift */, + ); + path = Storage; + sourceTree = ""; + }; + 2FCAE22A1ABB51F800877008 /* StorageTests */ = { + isa = PBXGroup; + children = ( + D37DE2C61CA356D800A5EC69 /* CertTests.swift */, + D3BF8CBC1B7472FA0007AFE6 /* DiskImageStoreTests.swift */, + 2FCAE2791ABB533A00877008 /* MockFiles.swift */, + F8AAC1B5296637B7000BCDEC /* RustAutofillTests.swift */, + D0148A01220C8C93008A6EEF /* RustLoginsTests.swift */, + D980063C26D8308700321BC1 /* RustPlacesTests.swift */, + 158241272820698B00956B39 /* RustRemoteTabsTests.swift */, + 289A4C121C4EB90600A460E3 /* StorageTestUtils.swift */, + 2FCAE22B1ABB51F800877008 /* Supporting Files */, + 281B02991C037C1F005202C3 /* TestBrowserDB.swift */, + D37DE2C81CA356F900A5EC69 /* testcert1.pem */, + D37DE2C91CA356F900A5EC69 /* testcert2.pem */, + 45D5EDCF292D854000311934 /* TestSQLitePinnedSites.swift */, + D0B693D4206C5BB9008A8B11 /* TestSQLiteReadingList.swift */, + D32CACEC1AE04DA1000658EB /* TestSwiftData.swift */, + BD57D9A629D4C42B00039394 /* ZoomLevelStoreTests.swift */, + ); + path = StorageTests; + sourceTree = ""; + }; + 2FCAE22B1ABB51F800877008 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 2891F2B91F991185001B105E /* fixtures */, 2FCAE22C1ABB51F800877008 /* Info.plist */, ); name = "Supporting Files"; @@ -10212,6 +10931,7 @@ 962F39492672D57A006BDA2A /* BookmarkItemsHelper.swift */, 39A359E31BFCCE94006B9E87 /* UserActivityHandler.swift */, E18259DE29B25E4F00E6BE76 /* UserNotificationCenterProtocol.swift */, + BDD262552CE3AC8200DF2C62 /* PrivacyWindowHelper.swift */, ); name = Helpers; sourceTree = ""; @@ -10611,6 +11331,7 @@ isa = PBXGroup; children = ( 5FDA464827F20C9C0060E924 /* FxNimbus.swift */, + 12C11FF32D2D76CD00E4DDBF /* FxNimbusMessaging.swift */, 5FDA464927F20C9C0060E924 /* Metrics.swift */, ); path = Generated; @@ -10682,7 +11403,6 @@ 7B3632E71C29879300D12AF9 /* Snapshot */ = { isa = PBXGroup; children = ( - 7B60B0071BDE3AE10090C984 /* SnapshotHelper.swift */, ); name = Snapshot; sourceTree = ""; @@ -10869,7 +11589,10 @@ 8A171A6029F82AD90085770E /* Application */ = { isa = PBXGroup; children = ( + 5AD3B67F2CF6674B00AFA1FE /* MockUIApplication.swift */, + 5AD3B6792CF653A200AFA1FE /* MockLocale.swift */, 8A13FA882AD82BC8007527AB /* AppSendTabDelegateTests.swift */, + 5AD3B6772CF650D000AFA1FE /* DefaultBrowserUitTests.swift */, ); path = Application; sourceTree = ""; @@ -11467,6 +12190,7 @@ children = ( 8AC225642B6D3FA400CDA7FD /* HomepageTelemetryTests.swift */, E1B9A2C32CADA78300F6A0E9 /* ToolbarTelemetryTests.swift */, + 0A686B3B2CDB70DC0090E146 /* MainMenuTelemetryTests.swift */, ); path = Telemetry; sourceTree = ""; @@ -11534,16 +12258,16 @@ 8AD984F12AF1554F00B9FDA4 /* ContentBlocker */ = { isa = PBXGroup; children = ( - 8A3345582BA499B6008C52AB /* disconnect-block-advertising.json */, - 8A33455A2BA499B6008C52AB /* disconnect-block-analytics.json */, - 8A33455C2BA499B7008C52AB /* disconnect-block-content.json */, - 8A33455B2BA499B7008C52AB /* disconnect-block-cookies-advertising.json */, - 8A33455D2BA499B7008C52AB /* disconnect-block-cookies-analytics.json */, - 8A3345592BA499B6008C52AB /* disconnect-block-cookies-content.json */, - 8A33455F2BA499B7008C52AB /* disconnect-block-cookies-social.json */, - 8A3345602BA499B7008C52AB /* disconnect-block-cryptomining.json */, - 8A3345572BA499B6008C52AB /* disconnect-block-fingerprinting.json */, - 8A33455E2BA499B7008C52AB /* disconnect-block-social.json */, + 2C4BFFC02D470E4100707D2F /* disconnect-block-advertising.json */, + 2C4BFFC12D470E4100707D2F /* disconnect-block-analytics.json */, + 2C4BFFC22D470E4100707D2F /* disconnect-block-content.json */, + 2C4BFFC32D470E4100707D2F /* disconnect-block-cookies-advertising.json */, + 2C4BFFC42D470E4100707D2F /* disconnect-block-cookies-analytics.json */, + 2C4BFFC52D470E4100707D2F /* disconnect-block-cookies-content.json */, + 2C4BFFC62D470E4100707D2F /* disconnect-block-cookies-social.json */, + 2C4BFFC72D470E4100707D2F /* disconnect-block-cryptomining.json */, + 2C4BFFC82D470E4100707D2F /* disconnect-block-fingerprinting.json */, + 2C4BFFC92D470E4100707D2F /* disconnect-block-social.json */, EBB894F8219398E500EB91A0 /* ContentBlocker.swift */, EBB894F9219398E500EB91A0 /* ContentBlocker+Safelist.swift */, EBB894F7219398E500EB91A0 /* TabContentBlocker.swift */, @@ -12294,21 +13018,11 @@ path = Mocks; sourceTree = ""; }; - C86E4F6F2493BA8E0087BFD9 /* Generated */ = { + C8742DB92729A69E00E724F6 /* LegacyThemeManager */ = { isa = PBXGroup; children = ( - 0B11AF062CB416FE00AD51D5 /* Metrics */, - 455880A027B42F720078DEBB /* FxNimbus.swift */, - 39673BC02B6D82F400653F4A /* FxNimbusMessaging.swift */, - ); - path = Generated; - sourceTree = ""; - }; - C8742DB92729A69E00E724F6 /* LegacyThemeManager */ = { - isa = PBXGroup; - children = ( - EB9A179A20E69A7E00B12184 /* LegacyTheme.swift */, - 2C49854D206173C800893DAE /* photon-colors.swift */, + EB9A179A20E69A7E00B12184 /* LegacyTheme.swift */, + 2C49854D206173C800893DAE /* photon-colors.swift */, ); path = LegacyThemeManager; sourceTree = ""; @@ -13325,6 +14039,7 @@ 43C50B4E2A0BCAA800C6A134 /* FirefoxSync.strings */, 43A7153B2A2DF94F00DD5747 /* Footer.strings */, E4E0BB171AFBC9E4008D6260 /* Info.plist */, + 430407CB2CDE52F40091115A /* LibraryPanel.strings */, 436033C72CCFA811000BBEA2 /* SearchEngineSelection.strings */, 436033C42CCFA811000BBEA2 /* FxANotification.strings */, 43E168912CA183A400E67516 /* PasswordGenerator.strings */, @@ -13370,14 +14085,34 @@ E60961841B62B7E100DD640F /* Configuration */ = { isa = PBXGroup; children = ( - 8A2B1A5B28216C4C0061216B /* Common.xcconfig */, - 8A2B1A5A28216C4C0061216B /* Debug.xcconfig */, - E6DCC1ED1DCBB6AA00CEC4B7 /* Fennec.enterprise.xcconfig */, - E60961861B62B8A700DD640F /* Fennec.xcconfig */, + 2C406F052E82D1880067303E /* EcosiaPreview.xcconfig */, + 2C406F062E82D1880067303E /* EcosiaPreview.ShareTo.xcconfig */, + 2C406F072E82D1880067303E /* EcosiaPreview.WidgetKit.xcconfig */, + 2C7518AA2D8F025800B12F1F /* Production.xcconfig */, + 2C2F32172D46AA2C00977F55 /* EcosiaTesting.ShareTo.xcconfig */, + 2C2F32182D46AA2C00977F55 /* EcosiaTesting.WidgetKit.xcconfig */, + 2C2F32012D46A53000977F55 /* EcosiaTesting.xcconfig */, + 12C11E132D28128000E4DDBF /* Common.xcconfig */, + 12C11E142D2812B200E4DDBF /* Debug.xcconfig */, + 2CB288D32B07C6F400A8FCB3 /* Ecosia.xcconfig */, + 12C11F662D2C298600E4DDBF /* Ecosia.ShareTo.xcconfig */, + 12C11F672D2C299D00E4DDBF /* Ecosia.WidgetKit.xcconfig */, + 2CB288D02B07C6F300A8FCB3 /* EcosiaBeta.xcconfig */, + 12C11E152D2812B200E4DDBF /* EcosiaBeta.ShareTo.xcconfig */, + 12C11E162D2812B200E4DDBF /* EcosiaBeta.WidgetKit.xcconfig */, + 2CB288D12B07C6F300A8FCB3 /* EcosiaBetaDebug.xcconfig */, + 12C11E172D2812B200E4DDBF /* EcosiaBetaDebug.ShareTo.xcconfig */, + 12C11E182D2812B200E4DDBF /* EcosiaBetaDebug.WidgetKit.xcconfig */, + 2CB288CF2B07C6F300A8FCB3 /* EcosiaDebug.xcconfig */, + 12C11E192D2812B200E4DDBF /* EcosiaDebug.ShareTo.xcconfig */, + 12C11E1A2D2812B200E4DDBF /* EcosiaDebug.WidgetKit.xcconfig */, + 12C11E1B2D2812B200E4DDBF /* Fennec.xcconfig */, + 12C11E1C2D2812B200E4DDBF /* Fennec.enterprise.xcconfig */, + 12C11E1D2D2812B200E4DDBF /* Firefox.xcconfig */, + 12C11E1E2D2812B200E4DDBF /* FirefoxBeta.xcconfig */, + 12C11E1F2D2812B200E4DDBF /* Release.xcconfig */, + 12C11E202D2812B200E4DDBF /* Staging.xcconfig */, 8AEDFE832C9DAB9C00821359 /* FennecTesting.xcconfig */, - E60961891B62B8C800DD640F /* Firefox.xcconfig */, - E6FCC43C1C40565200DF6113 /* FirefoxBeta.xcconfig */, - 8A2B1A5C28216C4D0061216B /* Release.xcconfig */, ); name = Configuration; sourceTree = ""; @@ -13409,6 +14144,8 @@ E65075551E37F714006961AC /* Utils */ = { isa = PBXGroup; children = ( + 5AD3B67D2CF665AE00AFA1FE /* UIApplicationInterface.swift */, + 5AD3B67B2CF65DE300AFA1FE /* LocaleInterface.swift */, 8A13FA8C2AD834FA007527AB /* BackgroundTabLoader.swift */, DFACBF80277B916B003D5F41 /* ConfigurableGradientView.swift */, 8ADED7ED276A7750009C19E6 /* CumulativeDaysOfUseCounter.swift */, @@ -13539,6 +14276,8 @@ E6F9650E1B2F1CF20034B023 /* Supporting Files */, E4E7EB6C1C4AED5E0094275D /* SupportUtilsTests.swift */, 28A6CE891AC082E200C1A2D4 /* UtilsTests.swift */, + 0B1C58D02CE5019A00F498F0 /* UserAgentTests.swift */, + 0BA1524F2CE78DAD0090B869 /* UserAgentBuilderTests.swift */, ); path = SharedTests; sourceTree = ""; @@ -13651,6 +14390,8 @@ 962C6C3F2971F5D400354BE8 /* Dangerfile.swift */, F8708D1E1A0970990051AB07 /* Extensions */, D4AFA84B2AFA4FC6000BFEAA /* firefox-ios-tests */, + 2CFE99672D45329200B25CE0 /* Ecosia */, + 2CFE99752D45329400B25CE0 /* EcosiaTests */, 7B604FC11C496005006EEEC3 /* Frameworks */, 45D5EDC1292D636000311934 /* nimbus.fml.yaml */, 962C6C39297054A700354BE8 /* Package.swift */, @@ -13667,6 +14408,7 @@ 047F9B2A24E1FE1C00CD7DF7 /* WidgetKit */, 43BE578B278BA4D900491291 /* RustMozillaAppServices-Info.plist */, 8ACD3B972BA8EA8300E73E9A /* PrivacyInfo.xcprivacy */, + 2CC242722D520DE80098467A /* Recovered References */, ); sourceTree = ""; }; @@ -13676,7 +14418,6 @@ F84B21BE1A090F8100AAB793 /* Client.app */, F84B21D31A090F8100AAB793 /* ClientTests.xctest */, F84B22491A0920C600AAB793 /* ShareTo.appex */, - D39FA15F1A83E0EC00EE869C /* UITests.xctest */, 288A2D861AB8B3260023ABC3 /* Shared.framework */, 2FCAE21A1ABB51F800877008 /* libStorage.a */, 2FCAE2241ABB51F800877008 /* StorageTests.xctest */, @@ -13686,13 +14427,13 @@ 282731681ABC9BE700AA1954 /* SyncTests.xctest */, E6F9650C1B2F1CF20034B023 /* SharedTests.xctest */, 7BEB644D1C7345600092C02E /* L10nSnapshotTests.xctest */, - 3BFE4B071D342FB800DDF53F /* XCUITests.xctest */, 3B43E3D01D95C48D00BBA9DB /* StoragePerfTests.xctest */, E69DB07D1E97DEA9008A67E6 /* SyncTelemetryTests.xctest */, - 397848DB1ED86605004C0C0B /* NotificationService.appex */, 047F9B2724E1FE1C00CD7DF7 /* WidgetKitExtension.appex */, - F8324A052649A188007E4BFA /* CredentialProvider.appex */, 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */, + 2C6C90822C614A16007D9B43 /* EcosiaSnapshotTests.xctest */, + 2CFE99662D45329200B25CE0 /* Ecosia.framework */, + 2CFE996F2D45329300B25CE0 /* EcosiaTests.xctest */, ); name = Products; sourceTree = ""; @@ -13700,6 +14441,7 @@ F84B21C01A090F8100AAB793 /* Client */ = { isa = PBXGroup; children = ( + 2CB288D92B07C8EF00A8FCB3 /* Ecosia */, F84B21E41A0910F600AAB793 /* Application */, F84B21EB1A0910F600AAB793 /* Assets */, E60961841B62B7E100DD640F /* Configuration */, @@ -13710,7 +14452,6 @@ 28EADE5C1AFC3A6D007FB2FB /* Extensions */, C82A94E3269CB74500624AA7 /* FeatureFlags */, F84B21F11A0910F600AAB793 /* Frontend */, - C86E4F6F2493BA8E0087BFD9 /* Generated */, 39A359BD1BFCCE7B006B9E87 /* Helpers */, F84B22431A09165600AAB793 /* Info.plist */, 7B2142FC1E5E055000CDD3FC /* InfoPlist.strings */, @@ -13821,6 +14562,7 @@ 1D74FF4D2B27962200FF01D0 /* WindowManagerTests.swift */, D3BA41671BD82F2200DA5457 /* XCTestCaseExtensions.swift */, 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */, + 0ECB6B672CCFE663006A7C82 /* DateGroupedTableDataTests.swift */, ); path = ClientTests; sourceTree = ""; @@ -13841,6 +14583,7 @@ F84B21E41A0910F600AAB793 /* Application */ = { isa = PBXGroup; children = ( + 5AD3B6732CF625A300AFA1FE /* DefaultBrowserUtil.swift */, 8A46F5AA2C9E4389005B6422 /* RemoteSettings */, C84266742728462900382274 /* AccessibilityIdentifiers.swift */, 5A47CFF42860FB8900B2B7BF /* AppLaunchUtil.swift */, @@ -13871,25 +14614,26 @@ isa = PBXGroup; children = ( E699220D1B94E3EF007C480D /* About */, - D0FCF8031FE4772C004A7995 /* AllFramesAtDocumentEnd.js */, - D03F8F22200EAC1E003C2224 /* AllFramesAtDocumentStart.js */, - 43F92B3729E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js */, - BA8E197E2BF2FB1900590B5F /* AddressFormManager.js */, + 121416712D6373BA0097788B /* AddressFormManager.js */, + 121416722D6373BA0097788B /* AllFramesAtDocumentEnd.js */, + 121416732D6373BA0097788B /* AllFramesAtDocumentStart.js */, + 121416742D6373BA0097788B /* AutofillAllFramesAtDocumentStart.js */, 43879AFB287BDFB100B15D10 /* CC_Script */, D308EE551CBF0BF5006843F2 /* CertError.css */, D38A1EDF1CB458EC0080C842 /* CertError.html */, + 12141E6A2D686C1C0097788B /* EcosiaNetError.css */, + 12141E682D686C030097788B /* EcosiaNetError.html */, F84B22391A0914A300AAB793 /* Fonts */, D0E17FA7201F847600F1FCB5 /* FxASignIn.js */, F84B21EF1A0910F600AAB793 /* Images.xcassets */, - D0FCF8041FE4772D004A7995 /* MainFrameAtDocumentEnd.js */, - D0FCF8051FE4772D004A7995 /* MainFrameAtDocumentStart.js */, + 121416792D6375960097788B /* MainFrameAtDocumentEnd.js */, + 1214167A2D6375960097788B /* MainFrameAtDocumentStart.js */, 0BA1E02F1B051A07007675AF /* NetError.css */, 0BA1E00D1B03FB0B007675AF /* NetError.html */, 8AF4E76A2C41D60A00BAD91C /* RemoteSettingsData */, D30684F01C84F12A002D8D82 /* SearchPlugins */, 39A35AEC1C0662A3006B9E87 /* SpotlightHelper.js */, D37524861C6E8B5A00A5F6C2 /* topdomains.txt */, - 3BC659481E5BA4AE006D560F /* TopSites */, 4336FAD1264B169000A6B076 /* WebcompatAllFramesAtDocumentStart.js */, ); path = Assets; @@ -14024,6 +14768,16 @@ }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 2CFE99612D45329200B25CE0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ 047F9B2624E1FE1C00CD7DF7 /* WidgetKitExtension */ = { isa = PBXNativeTarget; @@ -14105,11 +14859,12 @@ 288A2D811AB8B3260023ABC3 /* Sources */, 288A2D821AB8B3260023ABC3 /* Frameworks */, 288A2D841AB8B3260023ABC3 /* Resources */, - 4368F838279665E00013419B /* Embed Frameworks */, + 2CFE9FE72D453F0600B25CE0 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 2CFE9FE62D453F0600B25CE0 /* PBXTargetDependency */, ); name = Shared; packageProductDependencies = ( @@ -14121,6 +14876,86 @@ productReference = 288A2D861AB8B3260023ABC3 /* Shared.framework */; productType = "com.apple.product-type.framework"; }; + 2C6C90812C614A16007D9B43 /* EcosiaSnapshotTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2C6C90882C614A17007D9B43 /* Build configuration list for PBXNativeTarget "EcosiaSnapshotTests" */; + buildPhases = ( + 2C6C907E2C614A16007D9B43 /* Sources */, + 2C6C907F2C614A16007D9B43 /* Frameworks */, + 2C6C90802C614A16007D9B43 /* Resources */, + 2CFEA0BA2D46427600B25CE0 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 2C6C90872C614A17007D9B43 /* PBXTargetDependency */, + 2CFEA0B92D46427600B25CE0 /* PBXTargetDependency */, + ); + name = EcosiaSnapshotTests; + packageProductDependencies = ( + 2C6C908E2C614A6C007D9B43 /* SnapshotTesting */, + 2C69DA7A2C6225C400D7F69F /* Common */, + 12A536362D3E89BF00924CB0 /* GCDWebServers */, + ); + productName = EcosiaSnapshotTests; + productReference = 2C6C90822C614A16007D9B43 /* EcosiaSnapshotTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 2CFE99652D45329200B25CE0 /* Ecosia */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2CFE997E2D45329500B25CE0 /* Build configuration list for PBXNativeTarget "Ecosia" */; + buildPhases = ( + 2CFE99612D45329200B25CE0 /* Headers */, + 2CFE99622D45329200B25CE0 /* Sources */, + 2CFE99632D45329200B25CE0 /* Frameworks */, + 2CFE99642D45329200B25CE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 2CFE99672D45329200B25CE0 /* Ecosia */, + ); + name = Ecosia; + packageProductDependencies = ( + 2CFE9FCB2D45363500B25CE0 /* BrazeKit */, + 2CFE9FCD2D45363500B25CE0 /* BrazeUI */, + 2CFE9FCF2D45364100B25CE0 /* SnowplowTracker */, + 2CFE9FD12D45364600B25CE0 /* Common */, + 2CFE9FE92D45404800B25CE0 /* SwiftSoup */, + 2C2EA77E2DA3F2990085F5BC /* Lottie */, + 2C8723932EA930C50085902C /* Auth0 */, + ); + productName = Ecosia; + productReference = 2CFE99662D45329200B25CE0 /* Ecosia.framework */; + productType = "com.apple.product-type.framework"; + }; + 2CFE996E2D45329300B25CE0 /* EcosiaTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2CFE99842D45329500B25CE0 /* Build configuration list for PBXNativeTarget "EcosiaTests" */; + buildPhases = ( + 2CFE996B2D45329300B25CE0 /* Sources */, + 2CFE996C2D45329300B25CE0 /* Frameworks */, + 2CFE996D2D45329300B25CE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2CFE99742D45329400B25CE0 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 2CFE99752D45329400B25CE0 /* EcosiaTests */, + ); + name = EcosiaTests; + packageProductDependencies = ( + 2CFE9FBE2D45348200B25CE0 /* SnowplowTracker */, + 2CC0C2AC2DA804EA006FE9B7 /* ViewInspector */, + ); + productName = EcosiaTests; + productReference = 2CFE996F2D45329300B25CE0 /* EcosiaTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 2FA435FA1ABB83B4008031D1 /* Account */ = { isa = PBXNativeTarget; buildConfigurationList = 2FA436141ABB83B4008031D1 /* Build configuration list for PBXNativeTarget "Account" */; @@ -14136,7 +14971,6 @@ ); name = Account; packageProductDependencies = ( - F8DEACC42A3D43DA00C3B19D /* Sentry */, 8A8BAE112B2107E400D774EB /* GCDWebServers */, ); productName = Account; @@ -14168,11 +15002,13 @@ 45CC573928AD89CB006D55AA /* Glean SDK Generator Script */, 2FCAE2151ABB51F800877008 /* Sources */, 2FCAE2161ABB51F800877008 /* Frameworks */, + 2CFE9FC62D4535FD00B25CE0 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 2FCAE23C1ABB520700877008 /* PBXTargetDependency */, + 2CFE9FC52D4535FD00B25CE0 /* PBXTargetDependency */, ); name = Storage; packageProductDependencies = ( @@ -14206,29 +15042,6 @@ productReference = 2FCAE2241ABB51F800877008 /* StorageTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 397848DA1ED86605004C0C0B /* NotificationService */ = { - isa = PBXNativeTarget; - buildConfigurationList = 397848F51ED86605004C0C0B /* Build configuration list for PBXNativeTarget "NotificationService" */; - buildPhases = ( - 397848D71ED86605004C0C0B /* Sources */, - 397848D81ED86605004C0C0B /* Frameworks */, - 397848D91ED86605004C0C0B /* Resources */, - 43338D07278F722700A23914 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - C82043D82523DDC000740B71 /* PBXTargetDependency */, - D0EA58171FA836D200D03ED1 /* PBXTargetDependency */, - ); - name = NotificationService; - packageProductDependencies = ( - 5A37861C29A2C37C006B3A34 /* Common */, - ); - productName = NotificationService; - productReference = 397848DB1ED86605004C0C0B /* NotificationService.appex */; - productType = "com.apple.product-type.app-extension"; - }; 3B43E3CF1D95C48D00BBA9DB /* StoragePerfTests */ = { isa = PBXNativeTarget; buildConfigurationList = 3B43E3E91D95C48E00BBA9DB /* Build configuration list for PBXNativeTarget "StoragePerfTests" */; @@ -14246,27 +15059,6 @@ productReference = 3B43E3D01D95C48D00BBA9DB /* StoragePerfTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 3BFE4B061D342FB800DDF53F /* XCUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3BFE4B201D342FB900DDF53F /* Build configuration list for PBXNativeTarget "XCUITests" */; - buildPhases = ( - 3BFE4B031D342FB800DDF53F /* Sources */, - 3BFE4B041D342FB800DDF53F /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 3BFE4B0D1D342FB900DDF53F /* PBXTargetDependency */, - ); - name = XCUITests; - packageProductDependencies = ( - 43C6A47E27A0679300C79856 /* MappaMundi */, - 8A05B0042A69A0C40011B622 /* Common */, - ); - productName = XCUITests; - productReference = 3BFE4B071D342FB800DDF53F /* XCUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; 43BE5774278BA4D900491291 /* RustMozillaAppServices */ = { isa = PBXNativeTarget; buildConfigurationList = 43BE5785278BA4D900491291 /* Build configuration list for PBXNativeTarget "RustMozillaAppServices" */; @@ -14277,11 +15069,10 @@ buildRules = ( ); dependencies = ( - 459F39E52A031D16008C3411 /* PBXTargetDependency */, ); name = RustMozillaAppServices; packageProductDependencies = ( - 43A878FB27AB498E0071C372 /* MozillaAppServices */, + 2CABD7152C11C9CC00A0750F /* MozillaAppServices */, ); productName = Shared; productReference = 43BE578A278BA4D900491291 /* RustMozillaAppServices.framework */; @@ -14308,28 +15099,6 @@ productReference = 7BEB644D1C7345600092C02E /* L10nSnapshotTests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; - D39FA15E1A83E0EC00EE869C /* UITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = D39FA1671A83E0EC00EE869C /* Build configuration list for PBXNativeTarget "UITests" */; - buildPhases = ( - D39FA15B1A83E0EC00EE869C /* Sources */, - D39FA15C1A83E0EC00EE869C /* Frameworks */, - D39FA15D1A83E0EC00EE869C /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - D39FA1661A83E0EC00EE869C /* PBXTargetDependency */, - ); - name = UITests; - packageProductDependencies = ( - 433F87D42788F01500693368 /* KIF */, - 8A05B0062A69A25C0011B622 /* Common */, - ); - productName = UITests; - productReference = D39FA15F1A83E0EC00EE869C /* UITests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; E69DB07C1E97DEA9008A67E6 /* SyncTelemetryTests */ = { isa = PBXNativeTarget; buildConfigurationList = E69DB0921E97DEAA008A67E6 /* Build configuration list for PBXNativeTarget "SyncTelemetryTests" */; @@ -14370,37 +15139,11 @@ productReference = E6F9650C1B2F1CF20034B023 /* SharedTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - F8324A042649A188007E4BFA /* CredentialProvider */ = { - isa = PBXNativeTarget; - buildConfigurationList = F8324A172649A189007E4BFA /* Build configuration list for PBXNativeTarget "CredentialProvider" */; - buildPhases = ( - F8324A012649A188007E4BFA /* Sources */, - F8324A022649A188007E4BFA /* Frameworks */, - F8324A032649A188007E4BFA /* Resources */, - 43338D0B278F727600A23914 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - F8324AF92649B540007E4BFA /* PBXTargetDependency */, - F8324AFD2649B540007E4BFA /* PBXTargetDependency */, - ); - name = CredentialProvider; - packageProductDependencies = ( - 5A8FD0F1293A7D9000333AA7 /* SnapKit */, - 5A70EF15295DFD9000790249 /* Common */, - ); - productName = CredentialProvider; - productReference = F8324A052649A188007E4BFA /* CredentialProvider.appex */; - productType = "com.apple.product-type.app-extension"; - }; F84B21BD1A090F8100AAB793 /* Client */ = { isa = PBXNativeTarget; buildConfigurationList = F84B21DD1A090F8100AAB793 /* Build configuration list for PBXNativeTarget "Client" */; buildPhases = ( C874A4E327F62C5B006F54E5 /* Swiftlint */, - 5FA2232B27F6FA00005B3D87 /* Glean SDK Generator Script */, - 5FA2232C27F6FA69005B3D87 /* Nimbus Feature Manifest Generator Script */, F84B21BA1A090F8100AAB793 /* Sources */, F84B21BC1A090F8100AAB793 /* Resources */, 28CE83DE1A1D1E7C00576538 /* Frameworks */, @@ -14412,12 +15155,11 @@ buildRules = ( ); dependencies = ( - 435A2FF7278F51CD00C94282 /* PBXTargetDependency */, C82043712523DBEB00740B71 /* PBXTargetDependency */, 288A2D9C1AB8B3260023ABC3 /* PBXTargetDependency */, - 397848E11ED86605004C0C0B /* PBXTargetDependency */, F84B22521A0920C600AAB793 /* PBXTargetDependency */, 047F9B3124E1FE1F00CD7DF7 /* PBXTargetDependency */, + 2CFE997A2D45329500B25CE0 /* PBXTargetDependency */, ); name = Client; packageProductDependencies = ( @@ -14428,7 +15170,6 @@ 5A87148F292EA3910039A5BD /* SiteImageView */, 5A8FD0EB293A7D5E00333AA7 /* SnapKit */, 5A70EF0D295DFCCF00790249 /* Common */, - 5A37861829A2C337006B3A34 /* Sentry */, 5A06135929D6052E008F3D38 /* TabDataStore */, 216A0D752A40E7AB008077BA /* Redux */, 8AF2D0FB2A5F272A00C7DD69 /* ComponentLibrary */, @@ -14437,6 +15178,10 @@ E177989F2BD8043800F6F0EB /* ToolbarKit */, 5A984D352C8A3407007938C9 /* Kingfisher */, 819656162C80C6F300E62323 /* MenuKit */, + ED6C8DAB2CE6A4BB00D7F7F3 /* Sentry-Dynamic */, + 126509842CD925B40011BA36 /* BrazeKit */, + 126509862CD925B40011BA36 /* BrazeUI */, + 12C25BEC2D27F0A40048BADA /* SnowplowTracker */, ); productName = Client; productReference = F84B21BE1A090F8100AAB793 /* Client.app */; @@ -14480,6 +15225,7 @@ dependencies = ( D09A0CF11FAA2C7E009A0273 /* PBXTargetDependency */, D09A0CF31FAA2C81009A0273 /* PBXTargetDependency */, + 2C1298AA2BF5EE23005AE4E4 /* PBXTargetDependency */, ); name = ShareTo; packageProductDependencies = ( @@ -14499,7 +15245,7 @@ attributes = { DefaultBuildSystemTypeForWorkspace = Latest; LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 1240; + LastSwiftUpdateCheck = 1620; LastUpgradeCheck = 1020; ORGANIZATIONNAME = Mozilla; TargetAttributes = { @@ -14521,6 +15267,17 @@ CreatedOnToolsVersion = 6.2; LastSwiftMigration = 1000; }; + 2C6C90812C614A16007D9B43 = { + CreatedOnToolsVersion = 15.4; + TestTargetID = F84B21BD1A090F8100AAB793; + }; + 2CFE99652D45329200B25CE0 = { + CreatedOnToolsVersion = 16.2; + }; + 2CFE996E2D45329300B25CE0 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = F84B21BD1A090F8100AAB793; + }; 2FA435FA1ABB83B4008031D1 = { CreatedOnToolsVersion = 6.2; DevelopmentTeam = 43AQ936H96; @@ -14547,15 +15304,6 @@ ProvisioningStyle = Manual; TestTargetID = F84B21BD1A090F8100AAB793; }; - 397848DA1ED86605004C0C0B = { - CreatedOnToolsVersion = 8.3.2; - LastSwiftMigration = 1000; - SystemCapabilities = { - com.apple.Push = { - enabled = 0; - }; - }; - }; 3B43E3CF1D95C48D00BBA9DB = { CreatedOnToolsVersion = 8.0; DevelopmentTeam = 43AQ936H96; @@ -14563,23 +15311,11 @@ ProvisioningStyle = Manual; TestTargetID = F84B21BD1A090F8100AAB793; }; - 3BFE4B061D342FB800DDF53F = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0820; - TestTargetID = F84B21BD1A090F8100AAB793; - }; 7BEB64401C7345600092C02E = { DevelopmentTeam = 43AQ936H96; ProvisioningStyle = Manual; TestTargetID = F84B21BD1A090F8100AAB793; }; - D39FA15E1A83E0EC00EE869C = { - CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = 43AQ936H96; - LastSwiftMigration = 1020; - ProvisioningStyle = Manual; - TestTargetID = F84B21BD1A090F8100AAB793; - }; E69DB07C1E97DEA9008A67E6 = { CreatedOnToolsVersion = 8.3; DevelopmentTeam = 43AQ936H96; @@ -14594,9 +15330,6 @@ ProvisioningStyle = Manual; TestTargetID = F84B21BD1A090F8100AAB793; }; - F8324A042649A188007E4BFA = { - CreatedOnToolsVersion = 12.4; - }; F84B21BD1A090F8100AAB793 = { CreatedOnToolsVersion = 6.1; LastSwiftMigration = 1000; @@ -14745,12 +15478,12 @@ "en-US", be, ug, + ab, ); mainGroup = F84B21B51A090F8100AAB793; packageReferences = ( 433F87CC2788EAB600693368 /* XCRemoteSwiftPackageReference "GCDWebServer" */, 433F87D12788EF5B00693368 /* XCRemoteSwiftPackageReference "KIF" */, - 433F87D62788F34500693368 /* XCRemoteSwiftPackageReference "rust-components-swift" */, 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */, 432BD0222790EBD000A0F3C3 /* XCRemoteSwiftPackageReference "ios_sdk" */, 4368F83B279669690013419B /* XCRemoteSwiftPackageReference "SnapKit" */, @@ -14760,16 +15493,21 @@ 8AB30EC62B6C038600BD9A9B /* XCRemoteSwiftPackageReference "lottie-ios" */, AB2AC6612BCFD0A200022AAB /* XCRemoteSwiftPackageReference "swift-certificates" */, 5A984D322C8A31A0007938C9 /* XCRemoteSwiftPackageReference "Kingfisher" */, + 2CCFB3D82C0FC4DC00BEDCA0 /* XCRemoteSwiftPackageReference "rust-components-swift" */, + 2CB172802C612D68008551E2 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */, + 126509832CD925B30011BA36 /* XCRemoteSwiftPackageReference "braze-swift-sdk" */, + 12C25BEB2D27EBFF0048BADA /* XCRemoteSwiftPackageReference "snowplow-ios-tracker" */, + 2CFE9FE82D453F9600B25CE0 /* XCRemoteSwiftPackageReference "SwiftSoup" */, + 2CC0C2A72DA802CD006FE9B7 /* XCRemoteSwiftPackageReference "ViewInspector" */, + 2C8723922EA930C50085902C /* XCRemoteSwiftPackageReference "Auth0" */, ); productRefGroup = F84B21BF1A090F8100AAB793 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( F84B21BD1A090F8100AAB793 /* Client */, - 397848DA1ED86605004C0C0B /* NotificationService */, F84B22481A0920C600AAB793 /* ShareTo */, 047F9B2624E1FE1C00CD7DF7 /* WidgetKitExtension */, - F8324A042649A188007E4BFA /* CredentialProvider */, 2FA435FA1ABB83B4008031D1 /* Account */, 2FCAE2191ABB51F800877008 /* Storage */, 2FA436041ABB83B4008031D1 /* AccountTests */, @@ -14779,12 +15517,13 @@ E6F9650B1B2F1CF20034B023 /* SharedTests */, E69DB07C1E97DEA9008A67E6 /* SyncTelemetryTests */, 282731671ABC9BE700AA1954 /* SyncTests */, - D39FA15E1A83E0EC00EE869C /* UITests */, 7BEB64401C7345600092C02E /* L10nSnapshotTests */, - 3BFE4B061D342FB800DDF53F /* XCUITests */, 43BE5774278BA4D900491291 /* RustMozillaAppServices */, 288A2D851AB8B3260023ABC3 /* Shared */, 2827315D1ABC9BE600AA1954 /* Sync */, + 2C6C90812C614A16007D9B43 /* EcosiaSnapshotTests */, + 2CFE99652D45329200B25CE0 /* Ecosia */, + 2CFE996E2D45329300B25CE0 /* EcosiaTests */, ); }; /* End PBXProject section */ @@ -14854,6 +15593,7 @@ 43B520E42B55493E00007187 /* TabToolbar.strings in Resources */, 43DC75A72A24BE73007C2BCE /* DisplayCard.strings in Resources */, D59643E825C9B8E000EAB8B9 /* Default Browser.strings in Resources */, + 430407CD2CDE52F40091115A /* LibraryPanel.strings in Resources */, D59643EC25C9B8E000EAB8B9 /* Localizable.strings in Resources */, 43A715432A2DF94F00DD5747 /* UpdateCard.strings in Resources */, D59643E525C9B8E000EAB8B9 /* AuthenticationManager.strings in Resources */, @@ -14889,52 +15629,34 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 2FCAE2221ABB51F800877008 /* Resources */ = { + 2C6C90802C614A16007D9B43 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E4791B9C1CC035FD00C6D77B /* testcert1.pem in Resources */, - 2891F2CB1F991185001B105E /* v33.db in Resources */, - E4791BAA1CC0360200C6D77B /* testcert2.pem in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 397848D91ED86605004C0C0B /* Resources */ = { + 2CFE99642D45329200B25CE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8A1F6C312BC5A6BE00DA6F86 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - D39FA15D1A83E0EC00EE869C /* Resources */ = { + 2CFE996D2D45329300B25CE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E6EAC5961B29CB3A00E1DE1E /* scrollablePage.html in Resources */, - D34E33031BA793C2006135F0 /* loginForm.html in Resources */, - 0B6FBAB21AC1F830007EC669 /* numberedPage.html in Resources */, - 0B5A93421B1EB572004F47A2 /* readablePage.html in Resources */, - E6B4C3D81C68F55C001F97E8 /* JSPrompt.html in Resources */, - D343DCFE1C446BDB00D7EEE8 /* findPage.html in Resources */, - 0BF8F8DA1AEFF1C900E90BC2 /* noTitle.html in Resources */, - E67422C51CFF2D39009E8373 /* youtube.ico in Resources */, - EBB6CA622140823900077CFA /* firefoxScheme.html in Resources */, - D3E171C21A841EAD00AB44CD /* KIFHelper.js in Resources */, - C8EB60C41F1FB12500F9B5B3 /* navigationDelegate.html in Resources */, - EBB6CAA1214197DB00077CFA /* blobURL.html in Resources */, - D31EC05F1CC57ED80096F4AB /* localhostLoad.html in Resources */, - 4F97573B1AFA6F37006ECC24 /* readerContent.html in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - F8324A032649A188007E4BFA /* Resources */ = { + 2FCAE2221ABB51F800877008 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8A1F6C302BC5A6B600DA6F86 /* PrivacyInfo.xcprivacy in Resources */, - C874FC652661367900EBE86E /* MainInterface.storyboard in Resources */, - C893075D265501EE00A1DB2F /* CredentialAssets.xcassets in Resources */, + E4791B9C1CC035FD00C6D77B /* testcert1.pem in Resources */, + 2891F2CB1F991185001B105E /* v33.db in Resources */, + E4791BAA1CC0360200C6D77B /* testcert2.pem in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -14942,58 +15664,66 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - BA8E197F2BF2FB1900590B5F /* AddressFormManager.js in Resources */, 8A1A935B2B757C7C0069C190 /* wave.json in Resources */, D4AFAB0E2AFA8F9A000BFEAA /* SyncIntegrationTests in Resources */, - 43F92B3829E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js in Resources */, 8C29376A2BF79EE000146613 /* AddressFormManager.css in Resources */, 8C29376B2BF79EE000146613 /* AddressFormManager.html in Resources */, - 8A3345662BA499B7008C52AB /* disconnect-block-content.json in Resources */, 39D056382665235700FBEE59 /* initial_experiments.json in Resources */, C8B0F5F8283B7D38007AE65D /* pocketsponsoredfeed.json in Resources */, - 8A33456A2BA499B7008C52AB /* disconnect-block-cryptomining.json in Resources */, D38A1EE01CB458EC0080C842 /* CertError.html in Resources */, 8AEE62CB2756BA34003207D1 /* DownloadHelper.js in Resources */, 8AEE62CA2756BA34003207D1 /* TrackingProtectionStats.js in Resources */, + 2C4BFFCA2D470E4100707D2F /* disconnect-block-cookies-analytics.json in Resources */, + 2C4BFFCB2D470E4100707D2F /* disconnect-block-cookies-advertising.json in Resources */, + 2C4BFFCC2D470E4100707D2F /* disconnect-block-cookies-social.json in Resources */, + 12141E6C2D6877EB0097788B /* EcosiaNetError.html in Resources */, + 2C4BFFCD2D470E4100707D2F /* disconnect-block-social.json in Resources */, + 2C4BFFCE2D470E4100707D2F /* disconnect-block-advertising.json in Resources */, + 2C4BFFCF2D470E4100707D2F /* disconnect-block-cookies-content.json in Resources */, + 2C4BFFD02D470E4100707D2F /* disconnect-block-analytics.json in Resources */, + 2C4BFFD12D470E4100707D2F /* disconnect-block-cryptomining.json in Resources */, + 2C4BFFD22D470E4100707D2F /* disconnect-block-fingerprinting.json in Resources */, + 2C4BFFD32D470E4100707D2F /* disconnect-block-content.json in Resources */, 0BA1E0301B051A07007675AF /* NetError.css in Resources */, F84B220B1A0910F600AAB793 /* Images.xcassets in Resources */, - 8A3345692BA499B7008C52AB /* disconnect-block-cookies-social.json in Resources */, - 8A3345682BA499B7008C52AB /* disconnect-block-social.json in Resources */, 4336FAD2264B169000A6B076 /* WebcompatAllFramesAtDocumentStart.js in Resources */, 23BEA767251A99ED00A014BF /* NewYorkMedium-Bold.otf in Resources */, E4CD9F541A71506400318571 /* Reader.html in Resources */, E1BDAC832B9F65780063E6BF /* reportSiteIssueOff.json in Resources */, E1AF3563286DE5F800960045 /* FullFunctionalTestPlan.xctestplan in Resources */, - 8A3345642BA499B7008C52AB /* disconnect-block-analytics.json in Resources */, E1AF3567286DE5F800960045 /* PerformanceTestPlan.xctestplan in Resources */, 8A1A935A2B757C7C0069C190 /* portrait.json in Resources */, 23BEA76A251A99ED00A014BF /* NewYorkMedium-RegularItalic.otf in Resources */, + 12141E6D2D6877F00097788B /* EcosiaNetError.css in Resources */, + 1214167B2D6375960097788B /* MainFrameAtDocumentStart.js in Resources */, + 1214167C2D6375960097788B /* MainFrameAtDocumentEnd.js in Resources */, 7B2142FE1E5E055000CDD3FC /* InfoPlist.strings in Resources */, - 8A3345652BA499B7008C52AB /* disconnect-block-cookies-advertising.json in Resources */, E1AF27442A17BCF700CE5991 /* engagementNotificationWithoutConditions.json in Resources */, E69922171B94E3EF007C480D /* Licenses.html in Resources */, 8A1F6C2F2BC5A62400DA6F86 /* PrivacyInfo.xcprivacy in Resources */, E1AF3560286DE5F800960045 /* Smoketest3.xctestplan in Resources */, + 2CFEA09D2D455BD500B25CE0 /* EcosiaLaunchScreen.xib in Resources */, + 2CFEA09E2D455BD500B25CE0 /* Ecosia.xcassets in Resources */, 8A1A93592B757C7C0069C190 /* landscape.json in Resources */, E4CD9F5B1A71506C00318571 /* Reader.css in Resources */, - D0FCF8061FE4772D004A7995 /* AllFramesAtDocumentEnd.js in Resources */, D37524871C6E8B5A00A5F6C2 /* topdomains.txt in Resources */, 8ABE9F1E2CB462CA0080E1DF /* RemoteSettingsFetchConfig.json in Resources */, 39F4C0FA2045D87400746155 /* FocusHelper.js in Resources */, E1AF3562286DE5F800960045 /* Smoketest2.xctestplan in Resources */, 8C29376C2BF79EE000146613 /* AddressFormManager.mjs in Resources */, 43175DB626B8774D00C41C31 /* Ads.js in Resources */, - D0FCF8081FE4772D004A7995 /* MainFrameAtDocumentStart.js in Resources */, D30684F11C84F12A002D8D82 /* SearchPlugins in Resources */, 8A5CDEED27E510F500CC60FF /* pocketglobalfeed.json in Resources */, 8A1A93582B757C7C0069C190 /* gradient.json in Resources */, 74821FFE1DB6D3AC00EEEA72 /* MailSchemes.plist in Resources */, D308EE561CBF0BF5006843F2 /* CertError.css in Resources */, E1AF3566286DE5F800960045 /* Smoketest1.xctestplan in Resources */, + 121416752D6373BA0097788B /* AllFramesAtDocumentEnd.js in Resources */, + 121416762D6373BA0097788B /* AllFramesAtDocumentStart.js in Resources */, + 121416772D6373BA0097788B /* AutofillAllFramesAtDocumentStart.js in Resources */, + 121416782D6373BA0097788B /* AddressFormManager.js in Resources */, E1AF3561286DE5F800960045 /* Smoketest4.xctestplan in Resources */, - 8A2B1A5D28216C4D0061216B /* Debug.xcconfig in Resources */, - 8A3345632BA499B7008C52AB /* disconnect-block-cookies-content.json in Resources */, - 8A3345612BA499B7008C52AB /* disconnect-block-fingerprinting.json in Resources */, + 123CEC4C2E7814A2009EB379 /* FoundersGroteskCondensed-Semibold.ttf in Resources */, 0BA1E00E1B03FB0B007675AF /* NetError.html in Resources */, 23BEA768251A99ED00A014BF /* NewYorkMedium-BoldItalic.otf in Resources */, E4A961381AC06FA50069AD6F /* ReaderViewLoading.html in Resources */, @@ -15001,18 +15731,11 @@ E1AF3564286DE5F800960045 /* SyncIntegrationTestPlan.xctestplan in Resources */, 8A7D1AC52BA3542600162F4B /* splashScreen.json in Resources */, 8AEE62C92756BA34003207D1 /* LoginsHelper.js in Resources */, - 8A3345672BA499B7008C52AB /* disconnect-block-cookies-analytics.json in Resources */, - 8A2B1A5F28216C4D0061216B /* Release.xcconfig in Resources */, 8CC033FA2BA476840033449E /* FormAutofillHelper.js in Resources */, D0E17FB6201F847600F1FCB5 /* FxASignIn.js in Resources */, - 8A2B1A5E28216C4D0061216B /* Common.xcconfig in Resources */, 39A35AED1C0662A3006B9E87 /* SpotlightHelper.js in Resources */, 8AF4E7712C45B98F00BAD91C /* RemotePasswordRules.json in Resources */, - D0FCF8071FE4772D004A7995 /* MainFrameAtDocumentEnd.js in Resources */, - E4D6BEB91A0930EC00F538BD /* LaunchScreen.xib in Resources */, 23BEA769251A99ED00A014BF /* NewYorkMedium-Regular.otf in Resources */, - 8A3345622BA499B7008C52AB /* disconnect-block-advertising.json in Resources */, - D03F8F23200EAC1F003C2224 /* AllFramesAtDocumentStart.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -15052,24 +15775,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 43338D07278F722700A23914 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "cd \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\"\nif [[ -d \"Frameworks\" ]]; then \n rm -fr Frameworks\nfi\n"; - }; 43338D08278F725C00A23914 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -15106,24 +15811,6 @@ shellPath = /bin/sh; shellScript = "cd \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\"\nif [[ -d \"Frameworks\" ]]; then \n rm -fr Frameworks\nfi\n"; }; - 43338D0B278F727600A23914 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "cd \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/\"\nif [[ -d \"Frameworks\" ]]; then \n rm -fr Frameworks\nfi\n"; - }; 4368F81027955AE80013419B /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -15162,50 +15849,6 @@ shellPath = /bin/sh; shellScript = "rm -rf .venv; bash $PWD/bin/sdk_generator.sh -g Glean -o $SRCROOT/Storage/Generated\n"; }; - 5FA2232B27F6FA00005B3D87 /* Glean SDK Generator Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/Client/metrics.yaml", - "$(SRCROOT)/Client/pings.yaml", - "$(SRCROOT)/Client/tags.yaml", - ); - name = "Glean SDK Generator Script"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(SRCROOT)/Client/Generated/Metrics/Metrics.swift", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "OUTPUT_DIR=\"${SRCROOT}/Client/Generated/Metrics/\"\n# remove old Metrics file if present\nrm \"${SRCROOT}/Client/Generated/Metrics.swift\"\nrm -rf .venv; bash $PWD/bin/sdk_generator.sh -g Glean -o $OUTPUT_DIR\n"; - }; - 5FA2232C27F6FA69005B3D87 /* Nimbus Feature Manifest Generator Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 12; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SOURCE_ROOT)/nimbus.fml.yaml", - ); - name = "Nimbus Feature Manifest Generator Script"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(SRCROOT)/Client/Generated/FxNimbus.swift", - "$(SRCROOT)/Client/Generated/FxNimbusMessaging.swift", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ \"$ACTION\" != \"indexbuild\" ]; then\n /usr/bin/env -i HOME=$HOME PROJECT=$PROJECT CONFIGURATION=$CONFIGURATION SOURCE_ROOT=$SOURCE_ROOT bash \"$SOURCE_ROOT/bin/nimbus-fml.sh\" --verbose\nfi\n\n"; - }; C874A4E327F62C5B006F54E5 /* Swiftlint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -15262,12 +15905,17 @@ 43118D07251A9CD100F24376 /* LegacySavedTab.swift in Sources */, E18BAB0128E4AEF300098AE2 /* ImageIdentifiers.swift in Sources */, 4392FB48252EC50400AD3D23 /* InternalSchemeHandler.swift in Sources */, + 2CC246602D520EF90098467A /* EcosiaLightTheme.swift in Sources */, + 2CC246632D520EF90098467A /* EcosiaDarkTheme.swift in Sources */, + 2CFEA0A42D455EB200B25CE0 /* EcosiaThemeManager.swift in Sources */, 435E34B3254A6A6000406D92 /* TimeConstants.swift in Sources */, 43E69ED7254D081F00B591C2 /* SimpleTab.swift in Sources */, 1DDAD13E24F0651C007623C8 /* TopSitesWidget.swift in Sources */, D59431EE25E9912900F0BA82 /* WidgetIntents.intentdefinition in Sources */, 047F9B4224E1FF4000CD7DF7 /* ImageButtonWithLabel.swift in Sources */, 047F9B2C24E1FE1C00CD7DF7 /* WidgetKit.swift in Sources */, + 2CFEA0A72D455F8D00B25CE0 /* (null) in Sources */, + 2CFEA0B12D45655200B25CE0 /* DispatchQueueHelper.swift in Sources */, 1DA3CE6724EEE86C00422BB2 /* AppInfo.swift in Sources */, DA9FD88824E213DD00168D1E /* QuickLink.swift in Sources */, 1DA3CE5D24EEE73100422BB2 /* OpenTabsWidget.swift in Sources */, @@ -15339,6 +15987,7 @@ E65075A21E37F7AB006961AC /* HexExtensions.swift in Sources */, 288A2DB61AB8B38D0023ABC3 /* Result.swift in Sources */, EB912B9B22722B6300DF585A /* ReadWriteLock.swift in Sources */, + 12348FFA2DB8D2D40017B0C2 /* PrefsKeys+Ecosia.swift in Sources */, E65075AD1E37F7AB006961AC /* UIColorExtensions.swift in Sources */, EB912B9C22722B6800DF585A /* LockProtected.swift in Sources */, E650759C1E37F7AB006961AC /* DeferredUtils.swift in Sources */, @@ -15353,6 +16002,48 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2C6C907E2C614A16007D9B43 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C2F31FA2D46941400977F55 /* MockWindowManager.swift in Sources */, + 2C69DA7D2C6244BE00D7F69F /* MockTabDataStore.swift in Sources */, + 2C69DA782C62259D00D7F69F /* MockProfile.swift in Sources */, + 2C2F31FC2D46942A00977F55 /* MockMicrosurveySurfaceManager.swift in Sources */, + 2C69DA792C6225AE00D7F69F /* DependencyHelperMock.swift in Sources */, + 2C69DA7C2C6225DB00D7F69F /* MockOverlayModeManager.swift in Sources */, + 2CC24C752D52478E0098467A /* MockTabManager.swift in Sources */, + 2C69DA7E2C6244C400D7F69F /* MockThemeManager.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2CFE99622D45329200B25CE0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 123475C32DA6730A0017B0C2 /* String+Extension.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2CFE996B2D45329300B25CE0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2CFEA0BB2D46429900B25CE0 /* ProfileTest.swift in Sources */, + 2CFEA0B22D4641F600B25CE0 /* MockProfile.swift in Sources */, + 2C2F31FB2D46941400977F55 /* MockWindowManager.swift in Sources */, + 2C2F31F72D4664CB00977F55 /* MockThemeManager.swift in Sources */, + 2C2F31F62D4663D800977F55 /* DependencyHelperMock.swift in Sources */, + 2C2F31FD2D46942A00977F55 /* MockMicrosurveySurfaceManager.swift in Sources */, + 2C2F31FE2D46961000977F55 /* MockablePinnedSites.swift in Sources */, + 2C2F31F52D46612F00977F55 /* TopSitesHelperTests.swift in Sources */, + 2CFEA0B32D46420E00B25CE0 /* MockTabManager.swift in Sources */, + 2CFEA0B42D46423600B25CE0 /* MockURLBarView.swift in Sources */, + 2CFEA0B52D46424B00B25CE0 /* MockOverlayModeManager.swift in Sources */, + 2C2F31F82D4664E300977F55 /* XCTestCaseExtensions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2FA435F61ABB83B4008031D1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -15392,7 +16083,6 @@ B2999FED2B044A5900F0FEC1 /* UnencryptedCreditCardFields.swift in Sources */, 15DE98FD27FCED4F00F1ECDB /* RustRemoteTabs.swift in Sources */, 285D3B901B4386520035FD22 /* SQLiteQueue.swift in Sources */, - 394CF6CF1BAA493C00906917 /* DefaultSuggestedSites.swift in Sources */, B2999FEF2B044B4E00F0FEC1 /* RustAutofillEncryptionKeys.swift in Sources */, 2FCAE2781ABB531100877008 /* Visit.swift in Sources */, D3BF8CBB1B7425570007AFE6 /* DiskImageStore.swift in Sources */, @@ -15400,6 +16090,7 @@ F8AAC1B429663619000BCDEC /* RustAutofill.swift in Sources */, 45D5EDCE292D83DB00311934 /* SQLitePinnedSites.swift in Sources */, E18BAB0028E4AEBB00098AE2 /* ImageIdentifiers.swift in Sources */, + 2C1298AF2BF602D3005AE4E4 /* DefaultSuggestedSites.swift in Sources */, 45D5EDCC292D839800311934 /* PinnedSites.swift in Sources */, 2FCAE2611ABB531100877008 /* FileAccessor.swift in Sources */, D37DE2831CA2047500A5EC69 /* CertStore.swift in Sources */, @@ -15444,25 +16135,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 397848D71ED86605004C0C0B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 397848DE1ED86605004C0C0B /* NotificationService.swift in Sources */, - F8B710A12ABE38980029726E /* RustErrors.swift in Sources */, - 45355B232A269E7100B1EA8E /* Autopush.swift in Sources */, - 45355B262A269EAC00B1EA8E /* PushConfiguration.swift in Sources */, - 396E38CC1EE0816C00CC180F /* Profile.swift in Sources */, - 1D5CBF4A2B17E3CB0001D033 /* NotificationPayloads.swift in Sources */, - 1D1933792AF2C8CF005089C9 /* AppEvent.swift in Sources */, - 396E38DD1EE081DA00CC180F /* SyncDisplayState.swift in Sources */, - 1D1933762AF2C8C9005089C9 /* EventQueue.swift in Sources */, - 6025B10E267B6C7F00F59F6B /* LoginRecordExtension.swift in Sources */, - 396E38F21EE0C8ED00CC180F /* FxAPushMessageHandler.swift in Sources */, - F8A0B08329AD64790091C75B /* RustSyncManager.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 3B43E3CC1D95C48D00BBA9DB /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -15473,74 +16145,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 3BFE4B031D342FB800DDF53F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 2CA16FDE1E5F089100332277 /* SearchTest.swift in Sources */, - 0BF0DB4A1E57B05E009172B0 /* LaunchArguments.swift in Sources */, - 8A9F0B5827C59E1800FE09AE /* ImageIdentifiers.swift in Sources */, - 3B546EC01D95ECAE00BDBE36 /* ActivityStreamTest.swift in Sources */, - 2CCB296720A99C9500121DD8 /* LoginsTests.swift in Sources */, - 3D9CA9841EF456A8002434DD /* NightModeTests.swift in Sources */, - 39012F281F8ED262002E3D31 /* ScreenGraphTest.swift in Sources */, - 0B305E1B1E3A98A900BE0767 /* BookmarksTests.swift in Sources */, - 5FF4AF5C2CC69CC900BABC62 /* TodayWidgetTests.swift in Sources */, - D81127D81F84023B0050841D /* PhotonActionSheetTests.swift in Sources */, - 3BFE4B501D34673D00DDF53F /* ThirdPartySearchTest.swift in Sources */, - EB3A38A02032673E004C6E67 /* DatabaseFixtureTest.swift in Sources */, - EB63CA51228F0539005FD0EF /* DesktopModeTests.swift in Sources */, - 2CF9D9AA20067FA10083DF2A /* BrowsingPDFTests.swift in Sources */, - B1158F2A2B5029F200AC9D70 /* URLValidationTests.swift in Sources */, - 4FB4AF7526E7E789005FDF91 /* HomeButtonTests.swift in Sources */, - 0BC9C9C41F26F54D000E8AB5 /* SiteLoadTest.swift in Sources */, - B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */, - 2C4B6BF320349EB800A009C2 /* OnboardingTests.swift in Sources */, - 2C4A07DC20246EAD0083E320 /* DragAndDropTests.swift in Sources */, - 2C2A5EF41E68469500F02659 /* PrivateBrowsingTest.swift in Sources */, - D4C35391283500A600F7DC7D /* PerformanceTests.swift in Sources */, - 2CF449A51E7BFE2C00FD7595 /* NavigationTest.swift in Sources */, - 2C2A91291FA2410D002E36BD /* HistoryTests.swift in Sources */, - 782B0A362AB41DFC0049EE1A /* FakespotTests.swift in Sources */, - 580B0C4221748CFE00448DF8 /* DataManagementTests.swift in Sources */, - 3BF4B8E91D38497A00493393 /* BaseTestCase.swift in Sources */, - 3D9CAA1C1EFCD655002434DD /* ClipBoardTests.swift in Sources */, - 2CB1A65A1FDEA8B60084E96D /* NewTabSettings.swift in Sources */, - 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */, - D4C4BDCE2253725E00986F04 /* LibraryTests.swift in Sources */, - 2CCF17532105E4FD00705AE5 /* DisplaySettingsTests.swift in Sources */, - B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */, - 2C97EC711E72C80E0092EC18 /* TopTabsTest.swift in Sources */, - 39EB469A1E26DDB4006346E8 /* FxScreenGraph.swift in Sources */, - 2CC1B3F01E9B861400814EEC /* DomainAutocompleteTests.swift in Sources */, - 78FE1E892B040E7000338465 /* FirefoxSuggestTest.swift in Sources */, - 4F2A06BE26F8E46E0017DA05 /* TabCounterTests.swift in Sources */, - E1AF27362A13BDFE00CE5991 /* EngagementNotificationTests.swift in Sources */, - 78F28FC02CB81FDF00DA862E /* InactiveTabsTest.swift in Sources */, - 2C473BD0209778900008C853 /* DownloadsTests.swift in Sources */, - 39C261CC2018DE21009D97BD /* FxScreenGraphTests.swift in Sources */, - 787EDD852943EE75002B93AE /* JumpBackInTests.swift in Sources */, - B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */, - B1F90EC12BB3F6B600A4D431 /* ZoomingTests.swift in Sources */, - 8AEAD9F92C3DB0CD001A2C5A /* MicrosurveyTests.swift in Sources */, - D4AFA84E2AFA5482000BFEAA /* ExperimentIntegrationTests.swift in Sources */, - 3DEFED081F55EBE300F8620C /* TrackingProtectionTests.swift in Sources */, - 2CEDADA220207EC400223A89 /* SyncFAUITests.swift in Sources */, - C84266772728462900382274 /* AccessibilityIdentifiers.swift in Sources */, - 0430A545203B372D00FDF76D /* IntegrationTests.swift in Sources */, - 2C31A8471E8D447F00DAC646 /* HomePageSettingsUITest.swift in Sources */, - B1664E9E2B163B7A005D4C71 /* CreditCardsTests.swift in Sources */, - 2CEA6F791E93E3A600D4100E /* SearchSettingsUITest.swift in Sources */, - 2C31A7A91E8BFB2200DAC646 /* ReadingListTests.swift in Sources */, - 2C8C07771E7800EA00DC1237 /* FindInPageTests.swift in Sources */, - 2CB56E3F1E926BFB00AF7586 /* ToolbarTest.swift in Sources */, - B10997432A97251D00CC8860 /* UrlBarTests.swift in Sources */, - E1BDAC812B9F5DE40063E6BF /* ReportSiteTests.swift in Sources */, - 2C3406C81E719F00000FD889 /* SettingsTests.swift in Sources */, - 2CF21D0920A4A163000D08B7 /* PocketTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 43BE577C278BA4D900491291 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -15554,40 +16158,17 @@ files = ( D4859A7727328569009A2390 /* AccessibilityIdentifiers.swift in Sources */, 7BEB64441C7345600092C02E /* L10nSuite2SnapshotTests.swift in Sources */, - 7BEB64451C7345600092C02E /* SnapshotHelper.swift in Sources */, - D88C01B722B29EC200936951 /* ScreenGraphTest.swift in Sources */, E40AFC541DD0E93300DA5651 /* L10nPermissionStringsSnapshotTests.swift in Sources */, E40AFC6C1DD128DA00DA5651 /* L10nSuite1SnapshotTests.swift in Sources */, D437C4FD25FF5A3E00316F2C /* L10nMktSuiteSnapshotTests.swift in Sources */, E40AFC651DD0F25500DA5651 /* L10nBaseSnapshotTests.swift in Sources */, + 12C11F632D2831E500E4DDBF /* ScreenGraphTest.swift in Sources */, 391B4FFF1F9767F50094F841 /* FxScreenGraph.swift in Sources */, 8A9F0B5727C59E1700FE09AE /* ImageIdentifiers.swift in Sources */, E402000A1E6493C800B45AFF /* LaunchArguments.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - D39FA15B1A83E0EC00EE869C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0B5A93221B1EB4C8004F47A2 /* ReadingListTest.swift in Sources */, - D38F03701C06387900175932 /* AuthenticationTests.swift in Sources */, - D39FA1811A83E84900EE869C /* Global.swift in Sources */, - 0B7C1E951F6097AD006A8869 /* TrackingProtectionTests.swift in Sources */, - 4F514FD41ACD8F2C0022D7EA /* HistoryTests.swift in Sources */, - C8EB60DC1F1FB9AD00F9B5B3 /* NavigationDelegateTests.swift in Sources */, - E6A92ADB1C52A8DA00743291 /* LoginInputTests.swift in Sources */, - D375A9201AE71675001B30D5 /* ViewMemoryLeakTests.swift in Sources */, - D313BE981B2F5096009EF241 /* DomainAutocompleteTests.swift in Sources */, - D3CFD3641CC5605B0064AB4A /* SecurityTests.swift in Sources */, - D3C3EB651B6FF44000388E9A /* SessionRestoreTests.swift in Sources */, - 7B24DC9C1B67B3590005766B /* ClearPrivateDataTests.swift in Sources */, - 9638332327E14ACC0011EEFC /* AccessibilityIdentifiers.swift in Sources */, - E633E37A1C2204BE001FFF6C /* LoginManagerTests.swift in Sources */, - 744B0FFE1B4F172E00100422 /* ToolbarTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; E69DB0791E97DEA9008A67E6 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -15602,7 +16183,9 @@ files = ( 6F994AFD2AF56234008B8112 /* NetworkUtilsTests.swift in Sources */, 28532BEB1C472015000072D9 /* UtilsTests.swift in Sources */, + 0B1C58D12CE5019A00F498F0 /* UserAgentTests.swift in Sources */, 28532BEA1C472008000072D9 /* DeferredTests.swift in Sources */, + 0ECB6B6D2CCFF718006A7C82 /* DateGroupedTableDataTests.swift in Sources */, 5A292129295CA8A900242235 /* ThemableTests.swift in Sources */, 39E65D271CA5B92000C63CE3 /* AsyncReducerTests.swift in Sources */, E4E7EB6D1C4AED5E0094275D /* SupportUtilsTests.swift in Sources */, @@ -15612,47 +16195,11 @@ 28532BE91C471FFB000072D9 /* ResultTests.swift in Sources */, E6F9653C1B2F1D5D0034B023 /* NSURLExtensionsTests.swift in Sources */, 5A29212A295CAA1700242235 /* XCTestCaseRootViewController.swift in Sources */, + 0BA152502CE78DAD0090B869 /* UserAgentBuilderTests.swift in Sources */, 3B4AA24B1D8B8C4C00A2E008 /* ArrayExtensionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - F8324A012649A188007E4BFA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8AB8574227D963290075C173 /* UIConstants.swift in Sources */, - 6025B10C267B6BEA00F59F6B /* LoginRecordExtension.swift in Sources */, - F8A0B08529AD647B0091C75B /* RustSyncManager.swift in Sources */, - C8CE38BB265E71FE0009B09E /* ItemListCell.swift in Sources */, - F8324A0A2649A188007E4BFA /* CredentialProviderViewController.swift in Sources */, - 60CE80C12667780D004026C7 /* CredentialListPresenter.swift in Sources */, - F80DF73F27034F6400E4C37D /* LegacyDynamicFontHelper.swift in Sources */, - 6025B111267B6EE100F59F6B /* CredentialWelcomeViewController.swift in Sources */, - 1D1933742AF2C8C8005089C9 /* EventQueue.swift in Sources */, - C87DF962267246730097E707 /* photon-colors.swift in Sources */, - 6025B10A267B6BB300F59F6B /* SelectPasswordCell.swift in Sources */, - 8AB8574127D9630E0075C173 /* LegacyTheme.swift in Sources */, - 1D1933772AF2C8CE005089C9 /* AppEvent.swift in Sources */, - F8324B122649B707007E4BFA /* Profile.swift in Sources */, - F8324B262649B76E007E4BFA /* SyncDisplayState.swift in Sources */, - C8CE38BD265E71FE0009B09E /* UIFontExtension.swift in Sources */, - F80DF74B270CB9CA00E4C37D /* AppAuthenticator.swift in Sources */, - F8B7109F2ABE38970029726E /* RustErrors.swift in Sources */, - F80DF7412703BC8E00E4C37D /* CredentialPasscodeRequirementViewController.swift in Sources */, - 60D71AEC26AAF45E00355588 /* UIColorExtension.swift in Sources */, - 5AC40329291AFBDB002BF91C /* DispatchQueueHelper.swift in Sources */, - C8CE389C265E71E00009B09E /* CredentialListViewController.swift in Sources */, - C8CE38B9265E71FE0009B09E /* EmptyPlaceholderCell.swift in Sources */, - 6025B109267B6BB300F59F6B /* NoSearchResultCell.swift in Sources */, - C8CE38BF265E71FE0009B09E /* UIImageExtension.swift in Sources */, - F85C7F14272104E3004BDBA4 /* Layout.swift in Sources */, - E1442FDC2947836E003680B0 /* UIView+Extension.swift in Sources */, - C874FC2826612BFF00EBE86E /* AlertControllerView.swift in Sources */, - C874FB3A2660E8B900EBE86E /* CredentialProviderPresenter.swift in Sources */, - E1442FC0294782B6003680B0 /* CGRect+Extension.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; F84B21BA1A090F8100AAB793 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -15661,7 +16208,6 @@ 8A4AC0EC28C929D700439F83 /* URLSessionProtocol.swift in Sources */, C2A72A672A76938C002ACCE2 /* DownloadsCoordinator.swift in Sources */, 5FC276552894AEFF00AF2721 /* LibraryPanelHelper.swift in Sources */, - 602A2B9827F6256200C3CB78 /* FxNimbus.swift in Sources */, E1CD81C5290C6D5800124B27 /* HelpView.swift in Sources */, 5A47CFF52860FB8900B2B7BF /* AppLaunchUtil.swift in Sources */, C8B0F5ED283B7C9F007AE65D /* LegacyPocketStandardCell.swift in Sources */, @@ -15674,8 +16220,10 @@ ED07C0ED2CCAE745006C0627 /* SearchEngineSelectionAction.swift in Sources */, 8AABBCFC2A0010900089941E /* GleanWrapper.swift in Sources */, 8C92DE912A7128CB0090BD28 /* ProductAnalysisResponse.swift in Sources */, + 12C11E282D281BD100E4DDBF /* EcosiaTopSiteItemCell.swift in Sources */, F85C7F0F271DD154004BDBA4 /* AppAuthenticator.swift in Sources */, D029A04920A62DB0001DB72F /* TemporaryDocument.swift in Sources */, + 2CB5C33F2E45F02D00B1048F /* SearchViewController+Ecosia.swift in Sources */, C8BA0E7627F20B8E00DD8214 /* HistoryDeletionUtility.swift in Sources */, 8A93F86D29D3A131004159D9 /* DefaultRouter.swift in Sources */, B236204B2B851FE1000B1DE7 /* AddressAutoFillBottomSheetView.swift in Sources */, @@ -15690,7 +16238,6 @@ CAA3B7E62497DCB60094E3C1 /* LoginDataSource.swift in Sources */, 8ADC2A1B2A33998100543DAA /* AppStoreReviewSetting.swift in Sources */, 8A5D1CA82A30D6D3005AD35C /* HomeSetting.swift in Sources */, - 1DFE57FF27BAE3150025DE58 /* HomepageSectionType.swift in Sources */, C2D1A10D2A66C70000205DCC /* BookmarksCoordinator.swift in Sources */, 8C46E1B72B2209F000F56521 /* FakespotAdsEvent.swift in Sources */, 396E38F11EE0C8EC00CC180F /* FxAPushMessageHandler.swift in Sources */, @@ -15699,6 +16246,7 @@ 8A7AE4442BAB510B0072DAEC /* LibraryPanelCoordinatorDelegate.swift in Sources */, 8C19532E2B85E7AE00761B20 /* SelfSizingHostingController.swift in Sources */, E4CD9F6D1A77DD2800318571 /* ReaderModeStyleViewController.swift in Sources */, + 2C00E15F2E299AE800811A76 /* InvisibleTabSession.swift in Sources */, 81F617C92C8755AB003799BF /* MainMenuAction.swift in Sources */, E13E9AB52AAB0FB5001A0E9D /* FakespotViewModel.swift in Sources */, 8A5D1CBD2A30DC4E005AD35C /* AccountStatusSetting.swift in Sources */, @@ -15754,6 +16302,7 @@ 8AAAB0592C1B7240008830B3 /* MockRustFirefoxSuggest.swift in Sources */, F018F84C2719AE8300B9A52D /* ThemedDefaultNavigationController.swift in Sources */, 0BDDB33F2CA6B1F000D501DF /* EditFolderViewModel.swift in Sources */, + 12C11EB02D28291900E4DDBF /* DispatchQueueHelper+BuildChannel.swift in Sources */, 602B3D6729B0E1DB0066DEF8 /* ConversionValueUtil.swift in Sources */, 8ADED7EE276A7750009C19E6 /* CumulativeDaysOfUseCounter.swift in Sources */, 7AC7E0502C160FF800051D4D /* ReaderPanelEmptyStateView.swift in Sources */, @@ -15773,18 +16322,21 @@ 8AE80BBC2891C20D00BC12EA /* JumpBackInList.swift in Sources */, 8A395552299AF83400B2AFBB /* UIControl+Extension.swift in Sources */, C2A72A692A769460002ACCE2 /* ReadingListCoordinator.swift in Sources */, + 12AEB1262D3A51670035D7D8 /* LegacySessionData.swift in Sources */, EDC3C2562CCAC9CB005A047F /* SearchEnginesManager.swift in Sources */, F85C7F122721048E004BDBA4 /* Layout.swift in Sources */, DF036E43274FD434002E834E /* HistoryHighlightsCell.swift in Sources */, B2FEA68D2B460D390058E616 /* AddressAutofillSettingsViewController.swift in Sources */, 21618A8A2A4389F700A5189E /* ActiveScreenState.swift in Sources */, 8CC617092C35463B001C7688 /* AddressModifiedStatus.swift in Sources */, - DA52E1DA25F5961F0092204C /* LegacyTabTrayViewController.swift in Sources */, 8AD40FD327BB068F00672675 /* MainMenuActionHelper.swift in Sources */, EB1C84BF212EFFBF001489DF /* BrowserViewController+ReaderMode.swift in Sources */, AB2AC6662BD15E6300022AAB /* CertificatesHandler.swift in Sources */, 81020C922BB5AFA2007B8481 /* OnboardingMultipleChoiceButtonView.swift in Sources */, 96D95016270238500079D39D /* Throttler.swift in Sources */, + 12C11E8D2D2826A600E4DDBF /* EcosiaDebugSettings.swift in Sources */, + 12C11E8E2D2826A600E4DDBF /* NTPCustomizationSettingsViewController.swift in Sources */, + 12C11E8F2D2826A600E4DDBF /* EcosiaSettings.swift in Sources */, 8A4593C92BF7BECA002758DE /* MicrosurveyTableHeaderView.swift in Sources */, 8A93080B27C01AD60052167D /* SingleActionViewModel.swift in Sources */, ABB184EB2CB807B300F0689D /* CertificatesModel.swift in Sources */, @@ -15801,6 +16353,7 @@ F8A0B08229AD61FA0091C75B /* RustSyncManager.swift in Sources */, 438FE8642988ABA600155B10 /* CreditCardTableViewController.swift in Sources */, 3BCE6D3C1CEB9E4D0080928C /* ThirdPartySearchAlerts.swift in Sources */, + 5AD3B6742CF625B000AFA1FE /* DefaultBrowserUtil.swift in Sources */, 8A93F86229D36F0F004159D9 /* NavigationController.swift in Sources */, E13F8C342928194800BDC8B4 /* PhotonActionSheetSiteHeaderView.swift in Sources */, C2D71B9B2A3850B4003DEC7A /* ThemedTableViewCellViewModel.swift in Sources */, @@ -15823,7 +16376,9 @@ 8A7D08E32CAAF7C30035999C /* HomepageViewController.swift in Sources */, D01017F5219CB6BD009CBB5A /* DownloadContentScript.swift in Sources */, 8A093D832A4B68940099ABA5 /* PrivacySettingsDelegate.swift in Sources */, - 39F819C61FD70F5D009E31E4 /* GlobalTabEventHandlers.swift in Sources */, + 2C87239A2EA931490085902C /* BrowserViewController+EcosiaErrorHandling.swift in Sources */, + 2C87239B2EA931490085902C /* TabManager+InvisibleTab.swift in Sources */, + 2C87239C2EA931490085902C /* Tab+InvisibleTab.swift in Sources */, C8DC90C32A066B4A0008832B /* MarkupToken.swift in Sources */, FA6B2AC21D41F02D00429414 /* String+Punycode.swift in Sources */, E174963C2992B6A60096900A /* HostingTableViewSectionHeader.swift in Sources */, @@ -15836,7 +16391,6 @@ 8AEAD9F52C3D7BA9001A2C5A /* FeatureFlagsDebugViewController.swift in Sources */, A55319BB2B5D5A850051559F /* SearchSettingsAction.swift in Sources */, 810CD9C12BB346D800E290C2 /* OnboardingCardViewController.swift in Sources */, - C8A012F126AB07D70096A7A7 /* JumpBackInViewModel.swift in Sources */, 8AAAB05F2C1B72C9008830B3 /* SearchHighlightItem.swift in Sources */, 8A3EF8172A2FD2B900796E3A /* AdvancedAccountSettings.swift in Sources */, EBB89509219398E500EB91A0 /* TabContentBlocker+ContentScript.swift in Sources */, @@ -15845,11 +16399,11 @@ D3BE7B461B054F8600641031 /* UITestAppDelegate.swift in Sources */, C8DC90C72A06759E0008832B /* MarkupAttributionUtility.swift in Sources */, 219A0FD52ACC8506009A6D1A /* InactiveTabsCell.swift in Sources */, + 12AEB1282D3A568D0035D7D8 /* SessionRestoreHandler.swift in Sources */, 8A1CBB992BE01839008BE4D4 /* MicrosurveyPromptState.swift in Sources */, 23D57E6E25ED6F2700883FAD /* SearchViewController.swift in Sources */, C82A94F2269F68ED00624AA7 /* LegacyFeatureFlagsManager.swift in Sources */, C8610DAA2A0EBF7100B79FF1 /* OnboardingCardDelegate.swift in Sources */, - 3BB50E111D6274CD004B33DF /* TopSiteItemCell.swift in Sources */, 819656192C80ECFE00E62323 /* MainMenuMiddleware.swift in Sources */, E127313D28B6AD99006F39D2 /* WallpaperSettingsViewModel.swift in Sources */, 8A4593C72BF7BECA002758DE /* MicrosurveyTableViewCell.swift in Sources */, @@ -15883,6 +16437,7 @@ E1442FD8294782D9003680B0 /* UIPasteboard+Extension.swift in Sources */, AB4FB4D52C89FAFD005EF0CC /* BlockedTrackersTableController.swift in Sources */, C8A4137428BE58C900D8EFEA /* WallpaperMetadataCodableProtocol.swift in Sources */, + 12C11E9A2D28281100E4DDBF /* WebsiteConnectionStatus.swift in Sources */, 8A93080927BFE88F0052167D /* PhotonActionSheetContainerCell.swift in Sources */, C8B41E0A29F0284B00FE218A /* NimbusOnboardingFeatureLayer.swift in Sources */, BD4B2DE229BB4CD9005FAA50 /* SnackButton.swift in Sources */, @@ -15896,7 +16451,6 @@ C8BD87622A0C257C00CD803A /* OnboardingCardInfoModelProtocol.swift in Sources */, DA27EEDB28BADF4700DD6F5D /* MenuBuilderHelper.swift in Sources */, E16AD22C2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift in Sources */, - B2999FF12B194A5800F0FEC1 /* CreditCardPayload.swift in Sources */, E1877A81286E0EFD00F5BDF2 /* WebViewNavigationHandler.swift in Sources */, 274A36CC239EB99400A21587 /* LibraryPanelContextMenu.swift in Sources */, 8C2937722BF79F0300146613 /* Address+Encodable.swift in Sources */, @@ -15908,10 +16462,10 @@ 8CE1E4382B8C76C80026530B /* LoginListViewModel.swift in Sources */, 0B8BF3722CA2DA4600E9812D /* EditBookmarkViewController.swift in Sources */, F18859502A3E454E0004AA7B /* EnhancedTrackingProtectionCoordinator.swift in Sources */, - 2386E4E624F8358E0072EF17 /* HomepageMessageCard.swift in Sources */, 21618A8C2A438A0900A5189E /* ActiveScreenAction.swift in Sources */, E15DE7C2293A7AED00B32667 /* PhotonActionSheetLineSeparator.swift in Sources */, 8CE1E43A2B8C76C80026530B /* LoginListView.swift in Sources */, + 12A536352D3E63A200924CB0 /* GlobalTabEventHandlers.swift in Sources */, 8A832A9429DC99BA0025D5DD /* LaunchScreenViewController.swift in Sources */, 21E77E4E2AA8BA5200FABA10 /* TabTrayViewController.swift in Sources */, 43BDBBFE2752FA8600254DE4 /* LegacyTabCell.swift in Sources */, @@ -15919,7 +16473,6 @@ C4E3983D1D21F1E7004E89BA /* TopTabCell.swift in Sources */, 814B60F52C90C0A0000997BE /* MainMenuConfigurationUtility.swift in Sources */, 210E0EBA298D9D6400BB4F33 /* OpenSearchEngine.swift in Sources */, - FA9293D41D6580E100AC8D33 /* QRCodeViewController.swift in Sources */, 8AAEBA082BF52708000C02B5 /* MicrosurveyCoordinator.swift in Sources */, 5A679E4B2B239FAE004F2B0D /* TabPeekViewController.swift in Sources */, E18CE8DC2BDA3FA600EE2BCD /* NavigationToolbarContainerModel.swift in Sources */, @@ -15928,7 +16481,6 @@ E4CD9F2D1A6DC91200318571 /* TabLocationView.swift in Sources */, 81CAE4DB2B1A2C220040C78A /* BrowserViewControllerState.swift in Sources */, C8BD87602A0C248000CD803A /* OnboardingButtonsModel.swift in Sources */, - 0BB5B2881AC0A2B90052877D /* SnackBar.swift in Sources */, E1442FCF294782D9003680B0 /* UIView+Screenshot.swift in Sources */, 8AA0A6652CAC6B7100AC7EB3 /* HomepageSectionLayoutProvider.swift in Sources */, 8AB8572C27D945FA0075C173 /* TopSitesDataAdaptor.swift in Sources */, @@ -15971,7 +16523,6 @@ E15DE7C4293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift in Sources */, 392ED7E61D0AEFEF009D9B62 /* HomePageAccessors.swift in Sources */, 8A0017C128A3FF6100FEFC8B /* MessageCardDataAdaptor.swift in Sources */, - 7BA8D1C71BA037F500C8AE9E /* DownloadHelper.swift in Sources */, 8A0D32842A61E1CC007D976D /* StatusBarOverlay.swift in Sources */, 8A5D1CA42A30D69A005AD35C /* SearchSetting.swift in Sources */, 74BBDF472A17979000D3BEFE /* OnboardingDefaultBrowserModelProtocol.swift in Sources */, @@ -15987,6 +16538,7 @@ 74E36D781B71323500D69DA1 /* SettingsContentViewController.swift in Sources */, EBB8950C21939E4100EB91A0 /* FirefoxTabContentBlocker.swift in Sources */, 21618A632A422A3900A5189E /* ThemeMiddleware.swift in Sources */, + 2C8723962EA9313D0085902C /* TabAutoCloseManager.swift in Sources */, 0E77DF0F2CB8220000B80BA1 /* GeneratedPasswordStorage.swift in Sources */, 219A0FDB2ACCCFFC009A6D1A /* InactiveTabsSectionManager.swift in Sources */, B2981F8A2B71AD7A00132C1B /* AutofillAccessoryViewButtonItem.swift in Sources */, @@ -15997,6 +16549,7 @@ AB52ED3B2A0E8873001067F5 /* UserConversionMetrics.swift in Sources */, 219935EC2B07110900E5966F /* TabTrayModel.swift in Sources */, 8A3EF7F72A2FCF6D00796E3A /* ExportLogDataSetting.swift in Sources */, + 123283EF2F210C23009EA5BC /* NTPFirstSearchViewModel.swift in Sources */, 43E69EC3254D081D00B591C2 /* SimpleTab.swift in Sources */, 0BDDB33D2CA6B1DA00D501DF /* EditFolderViewController.swift in Sources */, 1DF1167A2BDB0FDA00521158 /* WindowTabsSyncCoordinator.swift in Sources */, @@ -16026,7 +16579,6 @@ D0C95E0E200FD3B200E4E51C /* PrintHelper.swift in Sources */, C8CD80DA2A1E8C1D0097C3AE /* OnboardingTelemetryProtocol.swift in Sources */, 8A5D1CB02A30D740005AD35C /* SearchBarSetting.swift in Sources */, - EB9854FF2422686F0040F24B /* AppDelegate+PushNotifications.swift in Sources */, DFACBF85277B9B5B003D5F41 /* TopSitesRowCountSettingsController.swift in Sources */, 9614BF4428AD1C6700D3F7EA /* AccountSyncHandler.swift in Sources */, 282DA4731A68C1E700A406E2 /* OpenSearchParser.swift in Sources */, @@ -16036,12 +16588,13 @@ E63ED8E11BFD25580097D08E /* PasswordManagerListViewController.swift in Sources */, 8ADC2A162A33765E00543DAA /* UrlToOpenModel.swift in Sources */, D0625CA8208FC47A0081F3B2 /* BrowserViewController+DownloadQueueDelegate.swift in Sources */, + 2C00E1642E29A70500811A76 /* InvisibleTabManager.swift in Sources */, C22753402A3C9E1300B9C0D1 /* WebsiteDataManagementViewModel.swift in Sources */, 2F44FCC71A9E8CF500FD20CC /* SearchSettingsTableViewController.swift in Sources */, 0AFF7F682C78989000265214 /* CertificatesHeaderItem.swift in Sources */, 8AD3271527E3B45D00EAF033 /* SponsoredTile.swift in Sources */, - B2999FF32B194A8300F0FEC1 /* FillCreditCardForm.swift in Sources */, 5A64225129CB506500EEC3E5 /* LegacyTabManager.swift in Sources */, + 120ED1362F446A260056F398 /* NTPFirstSearchView.swift in Sources */, 8A93F86029D36EBD004159D9 /* Router.swift in Sources */, C8417D222657F0600010B877 /* LibraryViewModel.swift in Sources */, 219935E92B070F9000E5966F /* TabPanelAction.swift in Sources */, @@ -16056,6 +16609,7 @@ 1DD4B26E2CA4D09100B51945 /* TabErrorTelemetryHelper.swift in Sources */, 8A359EF32A1FD449004A5BB7 /* AdjustWrapper.swift in Sources */, 8AAEBA022BF5110A000C02B5 /* MicrosurveyAction.swift in Sources */, + 128D68602F2A3AEE00F4B3EE /* LocalizedSearchSuggestions.swift in Sources */, D34510881ACF415700EC27F0 /* SearchLoader.swift in Sources */, E17496402994302D0096900A /* PreferredFont.swift in Sources */, E1442FD4294782D9003680B0 /* URL+Mail.swift in Sources */, @@ -16080,8 +16634,10 @@ 8A5BC1EB2C4AA13F009A40A4 /* FeatureFlagsBoolSetting.swift in Sources */, 43AB6FA425DC53D30016B015 /* LegacyLabelButtonHeaderView.swift in Sources */, 43D16B7C29831CD0009F8279 /* CreditCardItemRow.swift in Sources */, - 39673BC12B6D82F400653F4A /* FxNimbusMessaging.swift in Sources */, 965C3C942933A860006499ED /* LaunchSessionProvider.swift in Sources */, + 2CFE9FF02D4557EF00B25CE0 /* FxNimbusMessaging.swift in Sources */, + 2CFE9FF12D4557EF00B25CE0 /* FxNimbus.swift in Sources */, + 2CFE9FF22D4557EF00B25CE0 /* Metrics.swift in Sources */, C8163851268A0899004C7160 /* AddCredentialViewController.swift in Sources */, 8A19ACB22A3290AE001C2147 /* ClearPrivateDataSetting.swift in Sources */, 8A9B87AF2C1B39EA0042B894 /* SearchListSection.swift in Sources */, @@ -16092,14 +16648,19 @@ 8A19ACAB2A32895E001C2147 /* BrowserNavigationHandler.swift in Sources */, 8AE80BB82891BE0700BC12EA /* JumpBackInDataAdaptor.swift in Sources */, 8A01891C275E9C2A00923EFE /* ClearHistorySheetProvider.swift in Sources */, + 2CE9E8072E43927000141C6D /* NTPHeaderViewModel.swift in Sources */, + 2CE9E8082E43927000141C6D /* NTPHeader.swift in Sources */, 8A4EA0DD2C0117F200E4E4F1 /* MicrosurveyModel.swift in Sources */, 8C44A9D22A6A99FE009A1AA7 /* ShoppingProduct.swift in Sources */, + 129F18FE2EC5F04200E870C0 /* Task+Sleep.swift in Sources */, 63F7A9AA2C7529ED005846F5 /* NativeErrorPageModel.swift in Sources */, 63F7A9AC2C752BB0005846F5 /* NativeErrorPageViewController.swift in Sources */, C88E7A572A0553360072E638 /* OnboardingButtonInfoModel.swift in Sources */, AB9A0C012C6CBC9100BFA22A /* TrackingProtectionDetailsViewController.swift in Sources */, 21B548952B1E5F1400DC1DF8 /* InactiveTabsManager.swift in Sources */, + 12AEB1292D3A9AE70035D7D8 /* CreditCardPayload.swift in Sources */, C8F457A81F1FD75A000CB895 /* BrowserViewController+WebViewDelegates.swift in Sources */, + 2CE48EDA2E29B5C400CE84CE /* EcosiaAuth.swift in Sources */, 964FA97728A2A55C0024BB3B /* ContextualHintPrefsKeysProvider.swift in Sources */, 8A1E3BDF28CBA81E003388C4 /* SponsoredContentFilterUtility.swift in Sources */, 8CBDE8E32AB09804001985BF /* ProductAnalyzeResponse.swift in Sources */, @@ -16127,6 +16688,7 @@ 2165B2CC28748CD7004C0786 /* LibraryPanelDescriptor.swift in Sources */, D31A0FC71A65D6D000DC8C7E /* SearchSuggestClient.swift in Sources */, 8A57519927AD80B800A84DBF /* ReaderModeStyleViewModel.swift in Sources */, + 5AD3B67E2CF665B400AFA1FE /* UIApplicationInterface.swift in Sources */, 435D7CC5246209AA0043ACB9 /* IntroViewController.swift in Sources */, C855728429AEA3C300AF32B0 /* SurveySurfaceViewModel.swift in Sources */, CA4ACE4924C8C91600F55894 /* BreachAlertsDetailView.swift in Sources */, @@ -16138,12 +16700,15 @@ E118B9292862674E00C84831 /* LegacyInactiveTabItemCellModel.swift in Sources */, C88E7A552A0553180072E638 /* OnboardingViewModel.swift in Sources */, 8C29627C2B1F473800571655 /* AdEventsResponse.swift in Sources */, + 12C11EB92D2829B200E4DDBF /* BookmarksExchange.swift in Sources */, 962021E128B8078400BDF3D9 /* ContextualHintCopyProvider.swift in Sources */, 8AE1E1D227B1ADC40024C45E /* TopBottomInterchangeable.swift in Sources */, 818302D82C988DFD009645EC /* MainMenuDetailState.swift in Sources */, 8C2937712BF79F0300146613 /* EditAddressViewController.swift in Sources */, 0BDDB3412CA6B23300D501DF /* EditFolderCell.swift in Sources */, 8A7653BD28A2C61D00924ABF /* PocketDataAdaptor.swift in Sources */, + 12AEB1232D37CC4A0035D7D8 /* LegacyTabTrayViewController.swift in Sources */, + 2C00E3942E29B0E700811A76 /* EcosiaAuthFlow.swift in Sources */, 8A093D7D2A4B3E4F0099ABA5 /* DebugSettingsDelegate.swift in Sources */, 96EA9454293655BF00123345 /* AppSession+Enums.swift in Sources */, C8B0F5F4283B7CCE007AE65D /* PocketProvider.swift in Sources */, @@ -16151,6 +16716,8 @@ C8E531C829E5EB6100E03FEF /* RouteBuilder.swift in Sources */, D3972BF41C22412B00035B87 /* TitleActivityItemProvider.swift in Sources */, D38A1BEE1A9FA2CA00F6A386 /* SiteTableViewController.swift in Sources */, + 2CC246682D520EF90098467A /* EcosiaLightTheme.swift in Sources */, + 2CC2466B2D520EF90098467A /* EcosiaDarkTheme.swift in Sources */, 7BA0601B1C0F4DE200DFADB6 /* LegacyTabPeekViewController.swift in Sources */, 212985E42A6F078800546684 /* ScreenState.swift in Sources */, D51EA5BA26406A0000334331 /* ExperimentsBranchesViewController.swift in Sources */, @@ -16182,6 +16749,7 @@ 96F8DA49280452CA00E53239 /* GleanPlumbContextProvider.swift in Sources */, 43DB9784292D6846002E0B9F /* ShareButton.swift in Sources */, BD6B361E2B3C2511005E5345 /* CircularProgressView.swift in Sources */, + 12C11E272D281BCC00E4DDBF /* EcosiaHomepageSectionType.swift in Sources */, 8AB8574627D97CB00075C173 /* HomepageContextMenuProtocol.swift in Sources */, 0E6C1E232C909E04001A43BB /* PasswordGeneratorState.swift in Sources */, 3BB50E201D627539004B33DF /* LegacyHomepageViewController.swift in Sources */, @@ -16195,6 +16763,7 @@ D821E90E2141B71C00452C55 /* SiriSettingsViewController.swift in Sources */, EBB89507219398E500EB91A0 /* ContentBlocker.swift in Sources */, 216A0D792A40E85A008077BA /* ThemeSettingsState.swift in Sources */, + 12A536342D3E637800924CB0 /* DownloadHelper.swift in Sources */, 5A3A2A0D287F742C00B79EAC /* BackgroundSyncUtility.swift in Sources */, 8AABB92D2C64F77000F1FE51 /* LoginProvider.swift in Sources */, 21AFCFEE2AE80B700027E9CE /* TabsCoordinator.swift in Sources */, @@ -16203,6 +16772,7 @@ 8A3EF80D2A2FD04D00796E3A /* ResetWallpaperOnboardingPage.swift in Sources */, E1FE132F29C0B3CB002A65FF /* NotificationSurfaceManager.swift in Sources */, D88FDA9F1F4E2B9200FD9709 /* PhotonActionSheetProtocol.swift in Sources */, + 5AD3B67C2CF65DE600AFA1FE /* LocaleInterface.swift in Sources */, 21618A932A4499FC00A5189E /* AppState.swift in Sources */, BD1C89CA2A1E3CE7000A4201 /* PocketFooterView.swift in Sources */, 81020C942BB5B026007B8481 /* OnboardingMultipleChoiceButtonViewModel.swift in Sources */, @@ -16275,17 +16845,20 @@ 21357F2D293FDB60004BF9FD /* LegacyRemoteTabsErrorDataSource.swift in Sources */, D34DC8531A16C40C00D49B7B /* Profile.swift in Sources */, DFACDFAA274D489B00A94EEC /* HistoryHighlightsViewModel.swift in Sources */, + 12AEB12A2D3A9AEB0035D7D8 /* FillCreditCardForm.swift in Sources */, C84655FF2887A06B00861B4A /* WallpaperFilePathProvider.swift in Sources */, 8A1E93EA2A3CDC6100DD540A /* BaseCoordinator.swift in Sources */, E1442FD6294782D9003680B0 /* UIView+Extension.swift in Sources */, A5519CF52B5D57560062BECB /* SearchSettingsState.swift in Sources */, AB936A692C05F2B100600F82 /* TrackingProtectionButton.swift in Sources */, 74F80D342A0A52D700013C3D /* PrivacyPolicyViewController.swift in Sources */, + 121C6E182EC1EF870060319A /* BrowserViewController+WelcomeTransition.swift in Sources */, 274A36CE239EB9EC00A21587 /* LibraryViewController+LibraryPanelDelegate.swift in Sources */, C869912D28917688007ACC5C /* WallpaperImageLoader.swift in Sources */, 96A5F73829928B3700234E5F /* GeneralizedImageFetcher.swift in Sources */, 8A454D362CB86993009436D9 /* PocketStoryState.swift in Sources */, 1D7B78992ADF328E0011E9F2 /* AppEvent.swift in Sources */, + 12AEB1222D37BE030035D7D8 /* SnackBar.swift in Sources */, 43F7952525795F69005AEE40 /* SearchTelemetry.swift in Sources */, E65075541E37F6FC006961AC /* LegacyDynamicFontHelper.swift in Sources */, 8ADAE4242A33A126007BF926 /* StudiesToggleSetting.swift in Sources */, @@ -16295,8 +16868,6 @@ 81122E212B221AC0003DD9F8 /* SearchScreenState.swift in Sources */, 8A4490952BF3C42B00E7E682 /* MicrosurveyConfirmationView.swift in Sources */, 1DA6F6512B48B42900BB5AD6 /* WindowEventCoordinator.swift in Sources */, - 0B11AF022CB412D100AD51D5 /* Metrics.swift in Sources */, - C4E3984C1D21F2FD004E89BA /* TabTrayButtonExtensions.swift in Sources */, ED07C0E62CCACD7E006C0627 /* Locale+possibilitiesForLanguageIdentifier.swift in Sources */, 437A857827E43FE100E42764 /* FxAWebViewTelemetry.swift in Sources */, E13E9AB42AAB0FB5001A0E9D /* FakespotCoordinator.swift in Sources */, @@ -16311,7 +16882,6 @@ 8A5D1CB92A30DBDB005AD35C /* ChinaSyncServiceSetting.swift in Sources */, 8AB893A92C73CBBD00DAEED7 /* CreditCardProvider.swift in Sources */, 1D8487B42AD0C6C100F7527C /* RemoteTabsPanelMiddleware.swift in Sources */, - 5AA1D8272BC09ECB00957516 /* TabTrayAnimationQueue.swift in Sources */, C849E46526B9C3DD00260F0B /* SlideoverPresentationController.swift in Sources */, 819656152C80C55300E62323 /* MainMenuState.swift in Sources */, E18F44072A951C330056160F /* FakespotHighlightGroup.swift in Sources */, @@ -16364,6 +16934,18 @@ 8A5D1CC12A30DCA4005AD35C /* SettingDisclosureUtility.swift in Sources */, E1442FD5294782D9003680B0 /* UIView+SnapKit.swift in Sources */, E1ADE23C2B0649F200FD17AA /* FakespotState.swift in Sources */, + 12C11EA12D28291300E4DDBF /* (null) in Sources */, + 12C11EA42D28291300E4DDBF /* URL+Ecosia.swift in Sources */, + 12C11EA52D28291300E4DDBF /* BrowserViewController+Ecosia.swift in Sources */, + 12C11EA62D28291300E4DDBF /* AppSettingsTableViewController+Ecosia.swift in Sources */, + 12C11EA72D28291300E4DDBF /* ErrorPageHandler+Ecosia.swift in Sources */, + 12C11EA82D28291300E4DDBF /* HomepageViewController+Ecosia.swift in Sources */, + 12C11EAA2D28291300E4DDBF /* UIView+maskedCorners.swift in Sources */, + 12C11EAB2D28291300E4DDBF /* NumberFormatter+Ecosia.swift in Sources */, + 12C11EAC2D28291300E4DDBF /* UIFont+Ecosia.swift in Sources */, + 12C11EAD2D28291300E4DDBF /* SnapKit+Ecosia.swift in Sources */, + 12C11EAE2D28291300E4DDBF /* SimpleToast+Ecosia.swift in Sources */, + 12C11EAF2D28291300E4DDBF /* UIButton+Ecosia.swift in Sources */, 2816F0001B33E05400522243 /* UIConstants.swift in Sources */, 21E78A7228F9A93100F8D687 /* UIDeviceInterface.swift in Sources */, EBB89508219398E500EB91A0 /* ContentBlocker+Safelist.swift in Sources */, @@ -16399,6 +16981,7 @@ 96A562A027D6D0E80045144A /* ContileProvider.swift in Sources */, 8A5D1CAC2A30D70B005AD35C /* OpenWithSetting.swift in Sources */, 8AFA263227B6E9AB00D0C33B /* ToolbarBadge.swift in Sources */, + 12AEB1242D37CC6F0035D7D8 /* TabTrayButtonExtensions.swift in Sources */, 43D16B7A29831C7F009F8279 /* CreditCardAutofillToggle.swift in Sources */, C23889E12A4F3E7200429673 /* ParentCoordinatorDelegate.swift in Sources */, 8AD40FCD27BADC5C00672675 /* TabLocationContainerView.swift in Sources */, @@ -16414,6 +16997,7 @@ C84655E42887394B00861B4A /* WallpaperMetadata.swift in Sources */, 8AB8572E27D94A1A0075C173 /* UXSizeClass.swift in Sources */, 965C3C8F29313A1B006499ED /* AppSessionManager.swift in Sources */, + 2C87239E2EA931DB0085902C /* AccountsProviderWrapper.swift in Sources */, ED07C0EF2CCAE856006C0627 /* SearchEngineSelectionMiddleware.swift in Sources */, 45D5EDA729269F7500311934 /* DataObserver.swift in Sources */, 81A3F6F02C2DAEE200BDD86B /* MainMenuCoordinator.swift in Sources */, @@ -16434,6 +17018,7 @@ D59431ED25E9912900F0BA82 /* WidgetIntents.intentdefinition in Sources */, 213778632980448C00D01309 /* DownloadFileFetcher.swift in Sources */, F85C7EDF2710B4DD004BDBA4 /* LoginOnboarding.swift in Sources */, + 120EBBA22F3F30FA0056F398 /* SpotlightToast.swift in Sources */, C8B0F5F5283B7CCE007AE65D /* PocketSponsoredStory.swift in Sources */, 8AF6D4E12A856B4500B0474B /* ContileNetworking.swift in Sources */, 8AB8571F27D931B40075C173 /* EmptyTopSiteCell.swift in Sources */, @@ -16441,14 +17026,15 @@ C8656D75270F834600E199EA /* FlaggableFeatureOptions.swift in Sources */, 1BE7A4922C636AEA00460798 /* URLSessionConfiguration+defaultMPTCP.swift in Sources */, C8CD80D82A1E31C20097C3AE /* NimbusOnboardingTestingConfigUtility.swift in Sources */, + 120EBBB62F3F35F60056F398 /* BrowserViewController+ProductTourSpotlight.swift in Sources */, 81E1914D2BB8578600543D78 /* OnboardingMultipleChoiceSelectionDelegate.swift in Sources */, 8AB8573727D951640075C173 /* HomeLogoHeaderViewModel.swift in Sources */, D3C744CD1A687D6C004CE85D /* URIFixup.swift in Sources */, 43D16B8029831DC5009F8279 /* CreditCardInputView.swift in Sources */, - 21112968289480630082C08B /* HomepageMessageCardViewModel.swift in Sources */, 8AB893A32C73AF5200DAEED7 /* MockCreditCardProvider.swift in Sources */, E1CD81BE290C5C7500124B27 /* DevicePickerTableViewHeaderCell.swift in Sources */, B2FEA68B2B460D1D0058E616 /* AddressAutofillSettingsView.swift in Sources */, + 123475CC2DA7D6620017B0C2 /* BeforeOrAfterView.swift in Sources */, E12BD0AC28AC37F00029AAF0 /* UIColor+Extension.swift in Sources */, E4A961181AC041C40069AD6F /* ReadabilityService.swift in Sources */, AB42CC752A1F5240003C9594 /* CreditCardBottomSheetHeaderView.swift in Sources */, @@ -16465,6 +17051,7 @@ B28BF6602B7A9E4F006357CA /* FillAddressAutofillForm.swift in Sources */, 8A4593CA2BF7BECA002758DE /* MicrosurveyTableView.swift in Sources */, 21A7C44E283539170071D996 /* IntroViewModel.swift in Sources */, + 123283ED2F210C10009EA5BC /* NTPFirstSearchCell.swift in Sources */, E1FF93E228A2E55700E6360E /* WallpaperSelectorViewController.swift in Sources */, D3A9949C1A3686BD008AD1AC /* BrowserViewController.swift in Sources */, 8A9B87AD2C1B39100042B894 /* SearchViewModel.swift in Sources */, @@ -16487,7 +17074,6 @@ 5A8017E029CE15D90047120D /* TabManagerImplementation.swift in Sources */, 8AABBD012A001ADF0089941E /* ApplicationHelper.swift in Sources */, E633E2DA1C21EAF8001FFF6C /* PasswordDetailViewController.swift in Sources */, - 8A9AC46B276D11280047F5B0 /* PocketViewModel.swift in Sources */, 8A7653BF28A2C92600924ABF /* PocketStandardCellViewModel.swift in Sources */, 0B8BF3752CA2EFC000E9812D /* EditBookmarkCell.swift in Sources */, 8A5D1CAE2A30D71A005AD35C /* ThemeSetting.swift in Sources */, @@ -16504,6 +17090,7 @@ EB98550124226EF70040F24B /* AppDelegate+SyncSentTabs.swift in Sources */, 744ED5611DBFEB8D00A2B5BE /* MailtoLinkHandler.swift in Sources */, 8A720C622A4CBB370003018A /* SupportSettingsDelegate.swift in Sources */, + BDD262562CE3AC8200DF2C62 /* PrivacyWindowHelper.swift in Sources */, DF1E6AAB2A976FE7000D4854 /* FakespotNoAnalysisCardView.swift in Sources */, DA4F826729AD221600189590 /* ZoomPageBar.swift in Sources */, 59A68E0B4ABBF55E14819668 /* LegacyBookmarksPanel.swift in Sources */, @@ -16511,6 +17098,53 @@ 810FF3582B1784E7009F062C /* PrivateModeAction.swift in Sources */, 21EA466A2B04130500AAAB2D /* TabsPanelState.swift in Sources */, D04D1B862097859B0074B35F /* DownloadToast.swift in Sources */, + 2CFEA0542D455BD500B25CE0 /* MarketsController.swift in Sources */, + 2CFEA0562D455BD500B25CE0 /* NewsController.swift in Sources */, + 2CFEA0572D455BD500B25CE0 /* NTPLogoCell.swift in Sources */, + 2CFEA0592D455BD500B25CE0 /* NTPImpactCell.swift in Sources */, + 2CFEA05A2D455BD500B25CE0 /* EcosiaPrimaryButton.swift in Sources */, + 2CFEA05C2D455BD500B25CE0 /* WhatsNewLocalDataProvider.swift in Sources */, + 2CFEA05D2D455BD500B25CE0 /* NTPTooltipDelegate.swift in Sources */, + 2CFEA05E2D455BD500B25CE0 /* EmptyReadingListView.swift in Sources */, + 2CFEA05F2D455BD500B25CE0 /* NTPImpactRowView.swift in Sources */, + 2CFEA0612D455BD500B25CE0 /* EmptyHeader.swift in Sources */, + 2CFEA0622D455BD500B25CE0 /* NTPTooltip.swift in Sources */, + 2CFEA0632D455BD500B25CE0 /* NTPLibraryShortcutView.swift in Sources */, + 2CFEA0642D455BD500B25CE0 /* MultiplyImpact.swift in Sources */, + 2CFEA0672D455BD500B25CE0 /* PageActionsShortcutsHeader.swift in Sources */, + 2CFEA0682D455BD500B25CE0 /* NTPImpactDividerFooter.swift in Sources */, + 2CFEA0692D455BD500B25CE0 /* EmptyBookmarksView.swift in Sources */, + 2CFEA06E2D455BD500B25CE0 /* CircleButton.swift in Sources */, + 2CFEA06F2D455BD500B25CE0 /* WhatsNewViewController.swift in Sources */, + 2CFEA0732D455BD500B25CE0 /* WhatsNewViewModel.swift in Sources */, + 2CFEA0752D455BD500B25CE0 /* NTPTooltip.Highlight.swift in Sources */, + 2CFEA0762D455BD500B25CE0 /* NTPNewsCell.swift in Sources */, + 2CFEA0772D455BD500B25CE0 /* NTPNewsCellViewModel.swift in Sources */, + 2CFEA0782D455BD500B25CE0 /* FilterController.swift in Sources */, + 2CFEA0792D455BD500B25CE0 /* PageActionMenuCell.swift in Sources */, + 2CFEA07A2D455BD500B25CE0 /* EcosiaLaunchScreenView.swift in Sources */, + 2CFEA07B2D455BD500B25CE0 /* WhatsNewDataProvider.swift in Sources */, + 2CFEA07C2D455BD500B25CE0 /* NTPImpactCellViewModel.swift in Sources */, + 2CFEA07D2D455BD500B25CE0 /* EmptyBookmarksViewDelegate.swift in Sources */, + 2CFEA07F2D455BD500B25CE0 /* DefaultBrowserViewController.swift in Sources */, + 2CFEA0812D455BD500B25CE0 /* WhatsNewItem.swift in Sources */, + 2CFEA0822D455BD500B25CE0 /* NTPLibaryCellViewModel.swift in Sources */, + 2CFEA0832D455BD500B25CE0 /* NTPConfigurableNudgeCardCell.swift in Sources */, + 2CFEA0842D455BD500B25CE0 /* LoadingScreen.swift in Sources */, + 2CFEA0852D455BD500B25CE0 /* NTPCustomizationCellViewModel.swift in Sources */, + 2CFEA0872D455BD500B25CE0 /* NTPCustomizationCell.swift in Sources */, + 2CFEA08A2D455BD500B25CE0 /* MultiplyImpactStep.swift in Sources */, + 2CFEA08B2D455BD500B25CE0 /* EcosiaFindInPageBar.swift in Sources */, + 2CFEA08C2D455BD500B25CE0 /* CustomizableNTPSettingConfig.swift in Sources */, + 2CFEA08D2D455BD500B25CE0 /* ProgressView.swift in Sources */, + 2CFEA08F2D455BD500B25CE0 /* NTPConfigurableNudgeCardCellViewModel.swift in Sources */, + 2CFEA0912D455BD500B25CE0 /* ClimateImpactInfo.swift in Sources */, + 2CFEA0942D455BD500B25CE0 /* NTPLayout.swift in Sources */, + 2CFEA0952D455BD500B25CE0 /* PageActionMenu.swift in Sources */, + 2CFEA0972D455BD500B25CE0 /* NTPLibraryCell.swift in Sources */, + 2CFEA0982D455BD500B25CE0 /* WhatsNewCell.swift in Sources */, + 2CFEA0992D455BD500B25CE0 /* EcosiaThemeManager.swift in Sources */, + 2CFEA09B2D455BD500B25CE0 /* EcosiaNavigation.swift in Sources */, C29B64EE2AD937D500F3244B /* QRCodeNavigationHandler.swift in Sources */, 8A19ACB42A3290D9001C2147 /* ContentBlockerSetting.swift in Sources */, 219935E72B05447C00E5966F /* TabDisplayModel.swift in Sources */, @@ -16531,13 +17165,13 @@ E17798A22BD804D300F6F0EB /* AddressToolbarContainerModel.swift in Sources */, 8AE1E1CB27B18F560024C45E /* SearchBarSettingsViewController.swift in Sources */, 8AD40FC527BADC1F00672675 /* TabToolbarHelper.swift in Sources */, + 120EBBA42F3F31140056F398 /* ProductTourSpotlightCoordinator.swift in Sources */, 9609F4CA26B57CE800F81493 /* Calendar+Extension.swift in Sources */, E663D5781BB341C4001EF30E /* ToggleButton.swift in Sources */, DFA51481275FFEE500266AA0 /* HistoryHighlightsManager.swift in Sources */, 81C3E6092C93261A00A19A5A /* MainMenuDetailsViewController.swift in Sources */, 8ADC2A182A33775F00543DAA /* FxASignInViewParameters.swift in Sources */, EBA3B2D22268F57E00728BDB /* BadgeWithBackdrop.swift in Sources */, - 8AB5958828413F6C0090F4AE /* BookmarksCell.swift in Sources */, 8A83B7482A264FB7002FF9AC /* LibraryCoordinator.swift in Sources */, E1CD81C2290C62A600124B27 /* HostingTableViewCell.swift in Sources */, 8AA020EF2B9A37E500771DE0 /* NimbusSplashScreenFeatureLayer.swift in Sources */, @@ -16552,8 +17186,8 @@ 0BDDB3462CA6E43A00D501DF /* BookmarksSaver.swift in Sources */, EBB89506219398E500EB91A0 /* TabContentBlocker.swift in Sources */, E1B9A2C22CAD91EF00F6A0E9 /* ToolbarTelemetry.swift in Sources */, + 12AEB1182D36A8C40035D7D8 /* QRCodeViewController.swift in Sources */, 21F2A2D22B0BC85200626AEC /* InactiveTabsModel.swift in Sources */, - 966206CD2698DE1E005C0A55 /* BookmarksViewModel.swift in Sources */, D3E8EF101B97BE69001900FB /* ClearPrivateDataTableViewController.swift in Sources */, C8F457AA1F1FDD9B000CB895 /* BrowserViewController+KeyCommands.swift in Sources */, 21357F2F294237D8004BF9FD /* RemoteTabsClientAndTabsDataSource.swift in Sources */, @@ -16569,6 +17203,7 @@ D04CD718215EBD85004FF5B0 /* SettingsLoadingView.swift in Sources */, 0B8BF3702CA2D60B00E9812D /* BookmarksViewController.swift in Sources */, 21420EF72ABA338D00B28550 /* TabTrayCoordinator.swift in Sources */, + 128D6E822F2A94CF00F4B3EE /* SearchSuggestionFlowLayout.swift in Sources */, 5A70EF1F295E3DFC00790249 /* UnitTestAppDelegate.swift in Sources */, 2128E27E2934F78600FB91BE /* CustomAppActivity.swift in Sources */, 966B0DC82926F60500A85A7E /* UIResponder+Extensions.swift in Sources */, @@ -16592,6 +17227,7 @@ 8AD40FCA27BADC4B00672675 /* ReaderModeButton.swift in Sources */, E1C437A32A96343A00D188CB /* FakespotFadeLabel.swift in Sources */, 8A3EF7FB2A2FCF9D00796E3A /* ForceCrashSetting.swift in Sources */, + 120059182F3C8BD800D65907 /* BrowserViewController+EcosiaNavigationHandling.swift in Sources */, 2178A6A4291455F7002EC290 /* ReaderModeFontSizeButton.swift in Sources */, BCFF93F02AABA55A005B5B71 /* BackgroundFirefoxSuggestIngestUtility.swift in Sources */, BCFF93F22AAF9688005B5B71 /* FirefoxSuggestSettings.swift in Sources */, @@ -16608,25 +17244,22 @@ 961577942A39008100391E8D /* SponsoredTileDataUtilityTests.swift in Sources */, 8A093D812A4B58330099ABA5 /* MockSettingsFlowDelegate.swift in Sources */, 431C0D1E25C9DC4D00395CE4 /* DefaultBrowserOnboardingTests.swift in Sources */, + 5AD3B6782CF650DF00AFA1FE /* DefaultBrowserUitTests.swift in Sources */, 213B67A827CE721E000542F5 /* StartAtHomeHelperTests.swift in Sources */, 8A13FA892AD82BC8007527AB /* AppSendTabDelegateTests.swift in Sources */, - 8A7653C228A2E57D00924ABF /* PocketDataAdaptorTests.swift in Sources */, C8CD80D42A1E268C0097C3AE /* MockGleanPlumbEvaluationUtility.swift in Sources */, - E1AEC176286E0CF500062E29 /* JumpBackInViewModelTests.swift in Sources */, 8A7AE4472BAC78230072DAEC /* MockLibraryNavigationHandler.swift in Sources */, 8A7A26E829D4C0FE00EA76F1 /* IntroScreenManagerTests.swift in Sources */, E1AEC17A286E0CF500062E29 /* WebViewNavigationHandlerTests.swift in Sources */, D3D488591ABB54CD00A93597 /* FileAccessorTests.swift in Sources */, 5AF6254528A57B6700A90253 /* MockHistoryHighlightsManager.swift in Sources */, EDC3D3552CB70A3F00C62DE3 /* OpenSearchEngineTests.swift in Sources */, - E571EE7E28756A130051D9AA /* PocketStoryProviderTests.swift in Sources */, 812581962C947CD4003DFA7C /* MainMenuDetailsViewControllerTests.swift in Sources */, 21420EF92ABC75A400B28550 /* TabTrayCoordinatorTests.swift in Sources */, 1DDE3DB52AC360EC0039363B /* TabCellTests.swift in Sources */, 8AFCE50729DE0CD500B1B253 /* LaunchCoordinatorTests.swift in Sources */, 961D6B832995AF84001B9CF1 /* GeneralizedImageFetcherTests.swift in Sources */, - 21ED80B32AF2E43A0065D4C7 /* TabDisplayViewTests.swift in Sources */, - E1AEC177286E0CF500062E29 /* HistoryHighlightsViewModelTests.swift in Sources */, + 21ED80B32AF2E43A0065D4C7 /* TabDisplayDiffableDataSourceTests.swift in Sources */, 2165B2C42860CB34004C0786 /* MockAdjustTelemetryData.swift in Sources */, C29B64872AD69D0200F3244B /* QRCodeCoordinatorTests.swift in Sources */, 0BA8964B1A250E6500C1010C /* ProfileTest.swift in Sources */, @@ -16640,9 +17273,6 @@ 81DAB2F12C88F02500F4BE98 /* MainMenuViewControllerTests.swift in Sources */, 219935F12B07DFA200E5966F /* TabDisplayPanelTests.swift in Sources */, E16941B42C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift in Sources */, - E1AEC179286E0CF500062E29 /* FirefoxHomeViewModelTests.swift in Sources */, - DF940A0C2A96352B00C1497D /* FakespotSettingsCardViewModelTests.swift in Sources */, - 8ABCFEA62B45CB4C00C2988A /* PrivateBrowsingTelemetryTests.swift in Sources */, 281B2BEA1ADF4D90002917DC /* MockProfile.swift in Sources */, 96AF8C1C29FC14F700EC2219 /* CreditCardInputFieldHelperTests.swift in Sources */, 21FA8FB02AE856590013B815 /* RemoteTabsCoordinatorTests.swift in Sources */, @@ -16652,7 +17282,6 @@ 5A3A7DDC2889EC5D0065F81A /* BookmarksHandlerMock.swift in Sources */, 2F44FA1B1A9D426A00FD20CC /* TestHashExtensions.swift in Sources */, 5AF6254928A58BB400A90253 /* MockHistoryHighlightsDelegate.swift in Sources */, - 8A04136B2825ABEA00D20B10 /* SponsoredTileTelemetryTests.swift in Sources */, 21D8EA932ABE04F7003FF16E /* TabTrayViewControllerTests.swift in Sources */, AB7D4C3129ACAED100626427 /* Tab+ChangeUserAgentTests.swift in Sources */, 8C8D8C822AA2229300490D32 /* FakespotViewModelTests.swift in Sources */, @@ -16681,14 +17310,11 @@ 1D4D79472BF2F4FD007C6796 /* Throttler.swift in Sources */, 6A3E5D8A283831D1001E706E /* DownloadQueueTests.swift in Sources */, 8AE80BB62891AEA100BC12EA /* MockDispatchGroup.swift in Sources */, - 8AA6ADB52742B567004EEE23 /* TelemetryWrapperTests.swift in Sources */, - 21534904288201E300FADB4D /* GleanPlumbMessageManagerTests.swift in Sources */, AB03032F2AB8561700DCD8EF /* FakespotOptInViewModelTests.swift in Sources */, 21BFEEF82A05A0370033048D /* TabMigrationUtilityTests.swift in Sources */, 8AA8389D2CA2FEFC003FA256 /* StoreTestUtility.swift in Sources */, DFD1046D2B23539600938418 /* ProductAdsCacheTests.swift in Sources */, E1390FB628B040E900C9EF3E /* WallpaperSelectorViewModelTests.swift in Sources */, - 39AF317429DAE37E00F8E6F7 /* NimbusMessagingMessageTests.swift in Sources */, E14C78962C105488002AD3C7 /* AddressToolbarContainerModelTests.swift in Sources */, C889D7D52858CD8800121E1D /* HistoryHighlightsTestEntryProvider.swift in Sources */, C2A72A6B2A77AC10002ACCE2 /* ReadingListCoordinatorTests.swift in Sources */, @@ -16696,7 +17322,6 @@ E19B38B128A3E69300D8C541 /* WallpaperCollectionAvailabilityTests.swift in Sources */, 81DAB2F32C88F14400F4BE98 /* MainMenuCoordinatorTests.swift in Sources */, 8A359EF62A1FE840004A5BB7 /* MockAdjustWrapper.swift in Sources */, - 8AC225662B6D403200CDA7FD /* HomepageTelemetryTests.swift in Sources */, 1D558A582BED7ECB001EF527 /* MockWindowManager.swift in Sources */, C8DC90D22A067C6D0008832B /* MarkupAttributionUtilityTests.swift in Sources */, BA1C68BC2B7ED153000D9397 /* MockWebKit.swift in Sources */, @@ -16706,7 +17331,6 @@ 5A70EF1D295E3C3500790249 /* TestSetup.swift in Sources */, E1442FDA294782F7003680B0 /* UIPasteboard+Extension.swift in Sources */, C2D80BEB2AAF395200CDF7A9 /* CredentialAutofillCoordinatorTests.swift in Sources */, - F98CB66E2A4123F1005F38E9 /* EnhancedTrackingProtectionMenuVMTests.swift in Sources */, DF8C6DD72A52EED1007FAAF2 /* ClientSyncManagerTests.swift in Sources */, C2D1A1112A67E73D00205DCC /* BookmarksCoordinatorTests.swift in Sources */, C2200A6A2B7D148C00DC062A /* ContentBlockerTests.swift in Sources */, @@ -16724,13 +17348,11 @@ 81DAB2F92C8901AC00F4BE98 /* MainMenuStateTests.swift in Sources */, 8ACA8F74291987AE00D3075D /* AccountSyncHandlerTests.swift in Sources */, 8AAEBA0B2BF53AF6000C02B5 /* MicrosurveyStateTests.swift in Sources */, - 8AA7347B28AEDB3100443D24 /* PocketViewModelTests.swift in Sources */, C2446B312A856D13000C527D /* MockLibraryCoordinatorDelegate.swift in Sources */, C869915728917809007ACC5C /* NetworkingMock.swift in Sources */, 8A4190D22A6B0848001E8401 /* StatusBarOverlayTests.swift in Sources */, C8EDDBF029DD83FC003A4C07 /* RouteTests.swift in Sources */, 0A7693612C7DD19600103A6D /* CertificatesViewModelTests.swift in Sources */, - 8A454D372CB86B86009436D9 /* PocketStateTests.swift in Sources */, 8AED868328CA3B3400351A50 /* BookmarkPanelViewModelTests.swift in Sources */, 434CD57829F6FC4500A0D04B /* MockAppAuthenticator.swift in Sources */, DACDE996225E537900C8F37F /* VersionSettingTests.swift in Sources */, @@ -16747,9 +17369,7 @@ DFA51484276103A000266AA0 /* HistoryHighlightsManagerTests.swift in Sources */, C2506C952A6A8D2600F2B76E /* HistoryCoordinatorTests.swift in Sources */, 8ADAFAC628AEBF6300FFEBE3 /* HomeLogoHeaderViewModelTests.swift in Sources */, - 8A75F1B828B558E20054E34D /* MessageCardDataAdaptorImplementationTests.swift in Sources */, E14BF33E2950B1230039758D /* MailProvidersTests.swift in Sources */, - C889D7CE2858C4B500121E1D /* ContextMenuHelperTests.swift in Sources */, C838FD612899A9BB0068F60B /* WallpaperURLProviderTests.swift in Sources */, 8A11C8112731CFD700AC7318 /* ReaderModeStyleTests.swift in Sources */, 8ABA9C8E28931288002C0077 /* JumpBackInDataAdaptorTests.swift in Sources */, @@ -16762,15 +17382,11 @@ E1463D0629830E4F0074E16E /* MockUserNotificationCenter.swift in Sources */, 439B78182A09721600CAAE37 /* FormAutofillHelperTests.swift in Sources */, 8ADED7F0276A7788009C19E6 /* CumulativeDaysOfUseCounterTests.swift in Sources */, - 8A7653C528A2E69100924ABF /* MockPocketAPI.swift in Sources */, 8A83B74A2A265044002FF9AC /* SettingsCoordinatorTests.swift in Sources */, - E1B9A2C42CADA78300F6A0E9 /* ToolbarTelemetryTests.swift in Sources */, 8A6E13982A71BA4E00A88FA8 /* TabWebViewTests.swift in Sources */, 8AF3B15E2AF99D2F009BB262 /* DownloadsPanelTests.swift in Sources */, - 8A32DD5028B419B300D57C60 /* HomepageMessageCardViewModelTests.swift in Sources */, 8A36AC2C2886F27F00CDC0AD /* MockTabManager.swift in Sources */, 5A3A7DD62889CF3D0065F81A /* BookmarksDataAdaptorTests.swift in Sources */, - 8AD08D1727E91AC800B8E907 /* TabsTelemetryTests.swift in Sources */, C8C3FE9D29F907B30038E3BA /* MockSearchHandlerRouteCoordinator.swift in Sources */, ED55DC8C2CC2D7DA00E3FE3A /* SearchEngineSelectionCoordinatorTests.swift in Sources */, C8C3FEA129F973C40038E3BA /* MockBrowserViewController.swift in Sources */, @@ -16778,7 +17394,6 @@ 8AF3B15A2AF99B86009BB262 /* HistoryPanelTests.swift in Sources */, 814A62462B587A3E00608195 /* DefaultThemeManagerTests.swift in Sources */, 967EDABD29D705300089208D /* CreditCardValidatorTests.swift in Sources */, - D815A3A824A53F3200AAB221 /* TabToolbarHelperTests.swift in Sources */, 967EDABF29D769A10089208D /* CreditCardInputFieldTests.swift in Sources */, C8445AD126443C7F00B83F53 /* LibraryPanelViewStateTests.swift in Sources */, 8A6E139C2A71BB5700A88FA8 /* LegacyTabCellTests.swift in Sources */, @@ -16798,7 +17413,6 @@ AB4FB4E12C90491F005EF0CC /* BlockedTrackersTableViewControllerTests.swift in Sources */, 39C137972655798A003DC662 /* NimbusIntegrationTests.swift in Sources */, 215B458427DA87FC00E5E800 /* TabMetadataManagerTests.swift in Sources */, - 215349062886007900FADB4D /* GleanPlumbMessageStoreTests.swift in Sources */, 2173326A29CCF901007F20C7 /* UIPanGestureRecognizerMock.swift in Sources */, 5A9A09D628B01FD500B6F51E /* MockURLBarView.swift in Sources */, 8A33222227DFE658008F809E /* NimbusMock.swift in Sources */, @@ -16812,7 +17426,6 @@ 630FE1352C7FB42500D9D6B2 /* NativeErrorPageViewControllerTests.swift in Sources */, 8AAEB9FE2BF50718000C02B5 /* MicrosurveyViewControllerTests.swift in Sources */, C8E531CC29E72A2F00E03FEF /* ShortcutRouteTests.swift in Sources */, - C83B7DD629BBB49D005565C2 /* SurveySurfaceManagerTests.swift in Sources */, E1E6F8CE29D4B7E700068D8D /* GleanPlumbContextProviderTests.swift in Sources */, C8DC90D02A067C5B0008832B /* MarkupParseUtilityTests.swift in Sources */, 0BA8964C1A250E6500C1010C /* TestBookmarks.swift in Sources */, @@ -16832,6 +17445,7 @@ 1D558A5B2BEE7D07001EF527 /* WindowSimpleTabsCoordinator.swift in Sources */, 8A6E8DEB2B275BA9000C4301 /* PrivateHomepageViewControllerTests.swift in Sources */, 8A93F86529D37331004159D9 /* DefaultRouterTests.swift in Sources */, + 5AD3B6802CF6674F00AFA1FE /* MockUIApplication.swift in Sources */, 8AF10D8A29D713F50086351D /* LaunchScreenViewModelTests.swift in Sources */, 2F13E79B1AC0C02700D75081 /* StringExtensionsTests.swift in Sources */, CA24B52224ABD7D40093848C /* PasswordManagerViewModelTests.swift in Sources */, @@ -16839,7 +17453,6 @@ 8A7A26E129D4785900EA76F1 /* MockRouter.swift in Sources */, 965C3C96293431FC006499ED /* MockLaunchSessionProvider.swift in Sources */, C869915628917803007ACC5C /* WallpaperJSONTestProvider.swift in Sources */, - 8A95FF672B1E97A800AC303D /* TelemetryContextualIdentifierTests.swift in Sources */, 965C3C9829343445006499ED /* MockAppSessionManager.swift in Sources */, 8AFCE50929DE136300B1B253 /* MockLaunchFinishedLoadingDelegate.swift in Sources */, 8AE1E1DB27B1C1320024C45E /* SearchBarSettingsViewModelTests.swift in Sources */, @@ -16847,13 +17460,10 @@ C8610DA82A0EBD4100B79FF1 /* OnboardingButtonActionTests.swift in Sources */, 4331D3EF2A059C1C00542BDD /* SyncContentSettingsViewControllerTests.swift in Sources */, 8A827E322C20C8AE008D5E3C /* MockMicrosurveySurfaceManager.swift in Sources */, - 39D0DA7629D767DE000760B8 /* NimbusMessagingTriggerTests.swift in Sources */, 21B41A1D298B187A008BC0A2 /* MockOverlayModeManager.swift in Sources */, - 8A827E302C20C7F5008D5E3C /* MicrosurveyMiddlewareTests.swift in Sources */, 21B548992B1E7FDF00DC1DF8 /* InactiveTabsManagerTests.swift in Sources */, 8AF6D4DF2A856A9000B0474B /* MockContileNetworking.swift in Sources */, 2FDB10931A9FBEC5006CF312 /* PrefsTests.swift in Sources */, - C89C91AD2A1FE9E900BE57B1 /* OnboardingTelemetryDelegationTests.swift in Sources */, 8A36BE2C29EDE16C00AC1C5C /* ContentContainerTests.swift in Sources */, ED4589402CC8220A006F2C0B /* MockSearchEngineSelectionCoordinator.swift in Sources */, 5A9A09D228AFD51900B6F51E /* MockHomepageDataModelDelegate.swift in Sources */, @@ -16864,10 +17474,9 @@ 8A11C8132731E54800AC7318 /* DictionaryExtensionsTests.swift in Sources */, 5A475E8F29DB89CE009C13FD /* MockTabDataStore.swift in Sources */, ED45893E2CC800D9006F2C0B /* SearchEngineSelectionViewControllerTests.swift in Sources */, + 5AD3B67A2CF653A600AFA1FE /* MockLocale.swift in Sources */, 8A7A93EE2810ADF2005E7E1B /* ContileProviderTests.swift in Sources */, 5A3A7DDA2889EC4D0065F81A /* ReadingListMock.swift in Sources */, - 8A33221F27DFE318008F809E /* TopSitesDataAdaptorTests.swift in Sources */, - 2165B2C02860BB41004C0786 /* AdjustTelemetryHelperTests.swift in Sources */, C869915428917803007ACC5C /* WallpaperTestDataProvider.swift in Sources */, 0AC659292BF493CE005C614A /* MockFxAWebViewModel.swift in Sources */, 8A5604F629DF09FA00035CA3 /* MockLaunchCoordinatorDelegate.swift in Sources */, @@ -16898,8 +17507,6 @@ C8699153289177FB007ACC5C /* WallpaperDataServiceTests.swift in Sources */, C23889E32A50319A00429673 /* ShareExtensionCoordinatorTests.swift in Sources */, 8AFE4C2127480D0C00B97C65 /* LegacyTabTrayViewControllerTests.swift in Sources */, - E1312FD129D237EE008DDA85 /* NotificationSurfaceManagerTests.swift in Sources */, - 21371FA228A6C4A200BC3F37 /* OnboardingTelemetryUtilityTests.swift in Sources */, 814B71FF2CBEDC3B001B134A /* MainMenuDetailsStateTests.swift in Sources */, 5AE371842A4DD6F50092A760 /* PasswordManagerListViewControllerSpy.swift in Sources */, 8A2825352760399B00395E66 /* KeyboardPressesHandlerTests.swift in Sources */, @@ -16922,16 +17529,13 @@ ABB507CF2A136FB2009CAA67 /* UserConversionMetricsTests.swift in Sources */, 21FA8FB22AE856EB0013B815 /* MockTabTrayCoordinatorDelegate.swift in Sources */, C8699152289177F5007ACC5C /* WallpaperNetworkingTests.swift in Sources */, - C818AD452A2100BA007F30BC /* OnboardingNotificationCardHelperTests.swift in Sources */, E1AEC178286E0CF500062E29 /* LegacyHomepageViewControllerTests.swift in Sources */, - 8A4EA0D42C01100200E4E4F1 /* MicrosurveySurfaceManagerTests.swift in Sources */, 8ADEC6832A40F208002D2ED8 /* AppSettingsTableViewControllerTests.swift in Sources */, 5A475E8E29DB89C7009C13FD /* TabManagerTests.swift in Sources */, C88012232A40E38D00F4D1D6 /* IntroViewControllerTests.swift in Sources */, E16E1C9628BFB2E600EE2EF5 /* WallpaperSettingsViewModelTests.swift in Sources */, E1463D042982D0240074E16E /* NotificationManagerTests.swift in Sources */, 8AA0A6682CAC747500AC7EB3 /* HomepageDiffableDataSourceTests.swift in Sources */, - 3B61CD591F2A750800D38DE1 /* PocketFeedTests.swift in Sources */, C8E1BC0A28085AA700C62964 /* NimbusFeatureFlagLayerTests.swift in Sources */, F80D53CF2A09A3350047ED14 /* RustSyncManagerTests.swift in Sources */, 1D3C90882ACE1AF400304C87 /* RemoteTabPanelTests.swift in Sources */, @@ -16939,11 +17543,9 @@ E19443F62AF9413300964EA5 /* FakespotUtilsTests.swift in Sources */, 8A93F86829D373B0004159D9 /* MockNavigationController.swift in Sources */, 8AE1E1D927B1BD380024C45E /* UIStackViewExtensionsTests.swift in Sources */, - 8A0727492B4898D20071BB9F /* WebviewTelemetryTests.swift in Sources */, 3B6F40181DC7849C00656CC6 /* TopSitesViewModelTests.swift in Sources */, 8A00BD882CAB401700680AF9 /* HomepageViewControllerTests.swift in Sources */, C869915528917803007ACC5C /* WallpaperMetadataTestProvider.swift in Sources */, - BC003F5E2B59F44600929ECB /* BrowserViewControllerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -16951,14 +17553,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2C7DC2402B1648BA00C049C8 /* LegacyTheme.swift in Sources */, + 124DAE8A2D3512FA0050104C /* DispatchQueueHelper.swift in Sources */, E1CD81C6290C6D5800124B27 /* HelpView.swift in Sources */, E1D6F2D828E325D100B2C8CC /* InstructionsView.swift in Sources */, BD2577982C9D85B5007B344C /* AnyHashable.swift in Sources */, - FA6B2AC41D41F02D00429414 /* String+Punycode.swift in Sources */, E1D6F2D928E32A5300B2C8CC /* ImageIdentifiers.swift in Sources */, F8A0B08429AD64790091C75B /* RustSyncManager.swift in Sources */, EB6E0C60207E6C3100FBFF7E /* SendToDevice.swift in Sources */, D3C744CF1A687D6C004CE85D /* URIFixup.swift in Sources */, + 2C7DC2462B16493C00C049C8 /* UIConstants.swift in Sources */, EB94075320850C9F00702E05 /* photon-colors.swift in Sources */, 210E0EB9298D9D4B00BB4F33 /* DefaultSearchEngineProvider.swift in Sources */, D38B2D8C1A8D98D90040E6B5 /* OpenSearchParser.swift in Sources */, @@ -16972,12 +17576,16 @@ EB9407492081353100702E05 /* UXConstants.swift in Sources */, F8B710A02ABE38980029726E /* RustErrors.swift in Sources */, E60D03271D511554002FE3F6 /* SyncDisplayState.swift in Sources */, + 2CFEA0A02D455DE500B25CE0 /* EcosiaThemeManager.swift in Sources */, 210E0EBB298D9D6600BB4F33 /* OpenSearchEngine.swift in Sources */, 2128E27C2930216F00FB91BE /* SendToDeviceHelper.swift in Sources */, + 2C2F31F92D46672300977F55 /* AccessibilityIdentifiers.swift in Sources */, E136D41A2B19D35D003D0302 /* EmbeddedNavController.swift in Sources */, + 124DAE822D350EFD0050104C /* String+Punycode.swift in Sources */, E1CD81C0290C5C9800124B27 /* DevicePickerTableViewCell.swift in Sources */, - E1CD81BA290C4ED900124B27 /* AccessibilityIdentifiers.swift in Sources */, E1CD81BF290C5C9500124B27 /* DevicePickerTableViewHeaderCell.swift in Sources */, + 2CC246642D520EF90098467A /* EcosiaLightTheme.swift in Sources */, + 2CC246672D520EF90098467A /* EcosiaDarkTheme.swift in Sources */, 1D1933782AF2C8CE005089C9 /* AppEvent.swift in Sources */, 1D1933752AF2C8C9005089C9 /* EventQueue.swift in Sources */, E41A7D4B1A1BE04500245963 /* InitialViewController.swift in Sources */, @@ -17007,6 +17615,41 @@ target = F84B21BD1A090F8100AAB793 /* Client */; targetProxy = 28ECD9BE1BA1F12B00D829DA /* PBXContainerItemProxy */; }; + 2C1298AA2BF5EE23005AE4E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2FCAE2191ABB51F800877008 /* Storage */; + targetProxy = 2C1298A92BF5EE23005AE4E4 /* PBXContainerItemProxy */; + }; + 2C6C90872C614A17007D9B43 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F84B21BD1A090F8100AAB793 /* Client */; + targetProxy = 2C6C90862C614A17007D9B43 /* PBXContainerItemProxy */; + }; + 2CFE99742D45329400B25CE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F84B21BD1A090F8100AAB793 /* Client */; + targetProxy = 2CFE99732D45329400B25CE0 /* PBXContainerItemProxy */; + }; + 2CFE997A2D45329500B25CE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2CFE99652D45329200B25CE0 /* Ecosia */; + targetProxy = 2CFE99792D45329500B25CE0 /* PBXContainerItemProxy */; + }; + 2CFE9FC52D4535FD00B25CE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2CFE99652D45329200B25CE0 /* Ecosia */; + targetProxy = 2CFE9FC42D4535FD00B25CE0 /* PBXContainerItemProxy */; + }; + 2CFE9FE62D453F0600B25CE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2CFE99652D45329200B25CE0 /* Ecosia */; + targetProxy = 2CFE9FE52D453F0600B25CE0 /* PBXContainerItemProxy */; + }; + 2CFEA0B92D46427600B25CE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 43BE5774278BA4D900491291 /* RustMozillaAppServices */; + targetProxy = 2CFEA0B82D46427600B25CE0 /* PBXContainerItemProxy */; + }; 2F11EE501ABCAE910083902D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 288A2D851AB8B3260023ABC3 /* Shared */; @@ -17048,35 +17691,16 @@ target = 288A2D851AB8B3260023ABC3 /* Shared */; targetProxy = 2FCAE23B1ABB520700877008 /* PBXContainerItemProxy */; }; - 397848E11ED86605004C0C0B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 397848DA1ED86605004C0C0B /* NotificationService */; - targetProxy = 397848E01ED86605004C0C0B /* PBXContainerItemProxy */; - }; 3B43E3D61D95C48D00BBA9DB /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F84B21BD1A090F8100AAB793 /* Client */; targetProxy = 3B43E3D51D95C48D00BBA9DB /* PBXContainerItemProxy */; }; - 3BFE4B0D1D342FB900DDF53F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F84B21BD1A090F8100AAB793 /* Client */; - targetProxy = 3BFE4B0C1D342FB900DDF53F /* PBXContainerItemProxy */; - }; 43118CDF251A9CAD00F24376 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 288A2D851AB8B3260023ABC3 /* Shared */; targetProxy = 43118CDE251A9CAD00F24376 /* PBXContainerItemProxy */; }; - 435A2FF7278F51CD00C94282 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F8324A042649A188007E4BFA /* CredentialProvider */; - targetProxy = 435A2FF6278F51CD00C94282 /* PBXContainerItemProxy */; - }; - 459F39E52A031D16008C3411 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - productRef = 459F39E42A031D16008C3411 /* Glean */; - }; 45C9D1C82A323263009CDD7A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 2FCAE2191ABB51F800877008 /* Storage */; @@ -17102,11 +17726,6 @@ target = 2827315D1ABC9BE600AA1954 /* Sync */; targetProxy = C82043702523DBEB00740B71 /* PBXContainerItemProxy */; }; - C82043D82523DDC000740B71 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2827315D1ABC9BE600AA1954 /* Sync */; - targetProxy = C82043D72523DDC000740B71 /* PBXContainerItemProxy */; - }; D09A0CF11FAA2C7E009A0273 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 288A2D851AB8B3260023ABC3 /* Shared */; @@ -17117,16 +17736,6 @@ target = 2827315D1ABC9BE600AA1954 /* Sync */; targetProxy = D09A0CF21FAA2C81009A0273 /* PBXContainerItemProxy */; }; - D0EA58171FA836D200D03ED1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 288A2D851AB8B3260023ABC3 /* Shared */; - targetProxy = D0EA58161FA836D200D03ED1 /* PBXContainerItemProxy */; - }; - D39FA1661A83E0EC00EE869C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = F84B21BD1A090F8100AAB793 /* Client */; - targetProxy = D39FA1651A83E0EC00EE869C /* PBXContainerItemProxy */; - }; D40B30A921F718CC003C02A7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F84B21BD1A090F8100AAB793 /* Client */; @@ -17147,16 +17756,6 @@ target = F84B21BD1A090F8100AAB793 /* Client */; targetProxy = E63CD1B21B31B66400A63AFF /* PBXContainerItemProxy */; }; - F8324AF92649B540007E4BFA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 288A2D851AB8B3260023ABC3 /* Shared */; - targetProxy = F8324AF82649B540007E4BFA /* PBXContainerItemProxy */; - }; - F8324AFD2649B540007E4BFA /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 2827315D1ABC9BE600AA1954 /* Sync */; - targetProxy = F8324AFC2649B540007E4BFA /* PBXContainerItemProxy */; - }; F84B21D51A090F8100AAB793 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F84B21BD1A090F8100AAB793 /* Client */; @@ -17268,6 +17867,7 @@ 43DC31A028B39F100034E6DF /* si */, 437C672A28D88484001A5C31 /* be */, 43EB5F902B2728070063D23E /* ug */, + 4318898D2CD8E1A200365FD8 /* ab */, ); name = Menu.strings; sourceTree = ""; @@ -17369,6 +17969,7 @@ D4BF6AB72893DDA9001B5130 /* en-US */, 4315C1E528EAF9C300BA61F5 /* be */, 43EB5F932B2728070063D23E /* ug */, + 431889902CD8E1A200365FD8 /* ab */, ); name = Shared.strings; sourceTree = ""; @@ -17458,6 +18059,7 @@ 431853AB2A0911440099B0E0 /* gl */, 4364EA812B1DE9FA003A1240 /* bs */, 43EB5F952B2728070063D23E /* ug */, + 431889922CD8E1A200365FD8 /* ab */, ); name = Today.strings; sourceTree = ""; @@ -17561,10 +18163,70 @@ D4BF6AA92893DDA7001B5130 /* en-US */, A85F9E4C289A4C9800DCBF19 /* be */, 4303F4622B0B794D00C3B8E1 /* ug */, + 431889822CD8E1A100365FD8 /* ab */, ); name = AuthenticationManager.strings; sourceTree = ""; }; + 430407CB2CDE52F40091115A /* LibraryPanel.strings */ = { + isa = PBXVariantGroup; + children = ( + 430407CC2CDE52F40091115A /* bs */, + 43EDDCCE2CDE532B001E5EDB /* cs */, + 4361B1E82CDE536100B79FD9 /* de */, + 437016EA2CDE53730068D6EC /* dsb */, + 432360BE2CDE5388007CA02B /* el */, + 43F74BFD2CDE53AF0066EB74 /* en-GB */, + 4306CE3F2CDE53C500B2F7BF /* en-US */, + 433B2BF72CDE54170088A11C /* es-AR */, + 43A2035F2CDE544200ABAD9B /* es-CL */, + 4302D2BB2CDE54D700420BC8 /* fi */, + 43144C9F2CDE54F10026437B /* fr */, + 4328526A2CDE5553008AD0EE /* he */, + 43564C432CDE557A0098FB48 /* hr */, + 43B6A1A42CDE558C000E1445 /* hsb */, + 43E71BC12CDE55A1002FF15A /* hu */, + 43010EBC2CDE55B800468396 /* hy-AM */, + 43FEA1492CDE55D10040E31F /* ia */, + 43BDE5722CDE55FF005ADA27 /* is */, + 438AE2222CDE5619009BF451 /* it */, + 43C8D8152CDE568E00573AD9 /* kk */, + 433DAEB72CDE577C00B4E660 /* nl */, + 43F976DF2CDE579200239DC4 /* nn */, + 437797DC2CDE57D00017226F /* pa-IN */, + 439563912CDE57F5008C1FC9 /* pt-BR */, + 43C63CC62CDE5808003FBE8A /* pt-PT */, + 431A04882CDE584500C552E8 /* ru */, + 4383E6E92CDE585A0044260D /* sat-Olck */, + 43ECF55E2CDE589B00D9A8C4 /* sk */, + 4309AE1F2CDE58AF0074AB97 /* sl */, + 431867B62CDE58C000BEDD7F /* sq */, + 437F677B2CDE58E4002D482A /* sv */, + 432B9A562CDE596400B3FF06 /* ug */, + 4319845A2CDE59D90051D065 /* zh-CN */, + 43287A7B2CDE59EC009DE122 /* zh-TW */, + 434E78E42CEB7D640095EE6A /* br */, + 43C6A82F2CEB7D9A00FDAC33 /* ca */, + 438F8BCA2CEB7DB600AC873C /* co */, + 43AE1B392CEB7DE500744617 /* cy */, + 436F91EA2CEB7DFF00587C77 /* da */, + 43BF9B3C2CEB7F0600ABB6FB /* es */, + 4381BA622CEB7F5100B754B1 /* eu */, + 438D01AA2CEB814F000DEDE9 /* ja */, + 433F5BFE2CEB81C10036A99F /* kab */, + 4384249B2CEB8240005DFC7A /* ko */, + 4347676F2CEB825A00D97A8B /* lo */, + 430386F72CEB830A006A9852 /* nb */, + 43D0EA982CEB83C50045E291 /* pl */, + 4325EDFB2CEB8412008DE78E /* rm */, + 436D303F2CEB854F00E485B3 /* th */, + 43D9CE312CEB857D00DAA539 /* tr */, + 43A80F902CEB85C200E21DFF /* uk */, + 43B2FC742CEB8607006CB485 /* vi */, + ); + name = LibraryPanel.strings; + sourceTree = ""; + }; 43079D1D2C85D3BB0047C28D /* Bookmarks.strings */ = { isa = PBXVariantGroup; children = ( @@ -17626,6 +18288,9 @@ 43D3E3EF2CAABFF100205186 /* es */, 43C93FAB2CBD35B20006D3F8 /* gd */, 43263DA82CBD3A12000C2C5F /* sat-Olck */, + 4318899A2CD8E1A300365FD8 /* ab */, + 43FFB0752CD8E94C00F3E51A /* si */, + 4302509D2CEB80E500936045 /* id */, ); name = Bookmarks.strings; sourceTree = ""; @@ -17689,6 +18354,9 @@ 43D3E3F02CAABFF100205186 /* es */, 43C93FB42CBD35B30006D3F8 /* gd */, 430BD28E2CCFAAF5006DD679 /* lo */, + 431889AF2CD8E1A400365FD8 /* ab */, + 4383E6EB2CDE585A0044260D /* sat-Olck */, + 430250A42CEB80E600936045 /* id */, ); name = MainMenu.strings; sourceTree = ""; @@ -17757,6 +18425,8 @@ 4364EA952B1DE9FB003A1240 /* bs */, 43EB5FA72B2728080063D23E /* ug */, 4300B0DE2C57B06E00F609C1 /* es-MX */, + 431889BC2CD8E1A500365FD8 /* ab */, + 438DB4D82CDE551D00A6F1BE /* gd */, ); name = SelectCreditCard.strings; sourceTree = ""; @@ -17824,6 +18494,8 @@ 439E22242C04A5620061A923 /* su */, 4300B0DF2C57B06E00F609C1 /* es-MX */, 43E0BE412C60EC4C004FED65 /* hr */, + 431889C22CD8E1A600365FD8 /* ab */, + 438DB4DC2CDE551D00A6F1BE /* gd */, ); name = TabLocation.strings; sourceTree = ""; @@ -17887,6 +18559,10 @@ 43CCB4D42C32C527001F2EBB /* ca */, 43EC79AA2C3C00BF00519D98 /* es-MX */, 43E0BE3C2C60EC4C004FED65 /* hr */, + 431889AD2CD8E1A400365FD8 /* ab */, + 438DB4D22CDE551C00A6F1BE /* gd */, + 4383E6E82CDE585A0044260D /* sat-Olck */, + 434E78E32CEB7D640095EE6A /* br */, ); name = KeyboardAccessory.strings; sourceTree = ""; @@ -17956,6 +18632,8 @@ 4364EA842B1DE9FA003A1240 /* bs */, 43EB5F982B2728070063D23E /* ug */, 43E0BE342C60EC4B004FED65 /* hr */, + 431889972CD8E1A300365FD8 /* ab */, + 4302509C2CEB80E500936045 /* id */, ); name = BiometricAuthentication.strings; sourceTree = ""; @@ -18026,6 +18704,7 @@ 439273302A6E95B50014E253 /* su */, 4364EA8B2B1DE9FA003A1240 /* bs */, 43EB5F9E2B2728080063D23E /* ug */, + 431889A62CD8E1A400365FD8 /* ab */, ); name = ErrorState.strings; sourceTree = ""; @@ -18091,6 +18770,9 @@ 4357B4942C168FF1003518CF /* su */, 43EC79AE2C3C00BF00519D98 /* es-MX */, 43E0BE402C60EC4C004FED65 /* hr */, + 431889BF2CD8E1A600365FD8 /* ab */, + 438DB4DA2CDE551D00A6F1BE /* gd */, + 4383E6F42CDE585B0044260D /* sat-Olck */, ); name = Shopping.strings; sourceTree = ""; @@ -18151,6 +18833,10 @@ 43AD76D02CA18636002EC62F /* kab */, 43CEB5092CA1877F008C4E6F /* pl */, 43D3E3F32CAABFF100205186 /* es */, + 431889C12CD8E1A600365FD8 /* ab */, + 438DB4DB2CDE551D00A6F1BE /* gd */, + 434E78E52CEB7D640095EE6A /* br */, + 439D70DD2CEB84610075AD08 /* sat-Olck */, ); name = SuggestedSites.strings; sourceTree = ""; @@ -18221,6 +18907,7 @@ 4364EA8F2B1DE9FB003A1240 /* bs */, 43EB5FA12B2728080063D23E /* ug */, 43E0BE3B2C60EC4C004FED65 /* hr */, + 431889AC2CD8E1A400365FD8 /* ab */, ); name = JumpBackIn.strings; sourceTree = ""; @@ -18291,6 +18978,7 @@ 4364EA9C2B1DE9FB003A1240 /* bs */, 43EB5FAE2B2728080063D23E /* ug */, 43C9CD4A2C6A26D9002F86B4 /* hr */, + 431889C62CD8E1A600365FD8 /* ab */, ); name = ToolbarLocation.strings; sourceTree = ""; @@ -18333,6 +19021,24 @@ 434720C72CCFAD8E007554E9 /* vi */, 436742352CCFADB00033AA35 /* zh-CN */, 4375B35D2CCFADC1000DB6C1 /* zh-TW */, + 431889AB2CD8E1A400365FD8 /* ab */, + 431A77A42CD8E2DD00CB7F5D /* bs */, + 431DFF4D2CD8E55100FF0946 /* hu */, + 431BB0A72CD8E855001238B5 /* pa-IN */, + 431568C22CD8EA0E009DF3B9 /* th */, + 4302D2BA2CDE54D700420BC8 /* fi */, + 438DB4D12CDE551C00A6F1BE /* gd */, + 43564C422CDE557A0098FB48 /* hr */, + 4383E6E72CDE585A0044260D /* sat-Olck */, + 431867B52CDE58C000BEDD7F /* sq */, + 434E78E22CEB7D640095EE6A /* br */, + 43C6A82E2CEB7D9A00FDAC33 /* ca */, + 438F8BC92CEB7DB600AC873C /* co */, + 436F91E92CEB7DFF00587C77 /* da */, + 438D01A92CEB814E000DEDE9 /* ja */, + 430386F62CEB830A006A9852 /* nb */, + 43D0EA972CEB83C50045E291 /* pl */, + 4325EDFA2CEB8412008DE78E /* rm */, ); name = FxANotification.strings; sourceTree = ""; @@ -18376,6 +19082,22 @@ 434720C82CCFAD8E007554E9 /* vi */, 436742362CCFADB00033AA35 /* zh-CN */, 4375B35E2CCFADC1000DB6C1 /* zh-TW */, + 431889BA2CD8E1A500365FD8 /* ab */, + 431A77A52CD8E2DD00CB7F5D /* bs */, + 431DFF4E2CD8E55100FF0946 /* hu */, + 431BB0A82CD8E855001238B5 /* pa-IN */, + 431568C32CD8EA0E009DF3B9 /* th */, + 438DB4D72CDE551D00A6F1BE /* gd */, + 43564C442CDE557A0098FB48 /* hr */, + 4383E6F22CDE585B0044260D /* sat-Olck */, + 431867B72CDE58C000BEDD7F /* sq */, + 43C6A8312CEB7D9A00FDAC33 /* ca */, + 438F8BCC2CEB7DB600AC873C /* co */, + 436F91EB2CEB7DFF00587C77 /* da */, + 438D01AB2CEB814F000DEDE9 /* ja */, + 430386F82CEB830B006A9852 /* nb */, + 43D0EA992CEB83C50045E291 /* pl */, + 4325EDFC2CEB8412008DE78E /* rm */, ); name = SearchEngineSelection.strings; sourceTree = ""; @@ -18443,6 +19165,9 @@ 43B890FF2C32C60B0049F033 /* es-MX */, 43E0BE392C60EC4B004FED65 /* hr */, 4316E75D2CB3FE15002B86EE /* lo */, + 431889A72CD8E1A400365FD8 /* ab */, + 438DB4D02CDE551C00A6F1BE /* gd */, + 4383E6E52CDE58590044260D /* sat-Olck */, ); name = FirefoxHomepage.strings; sourceTree = ""; @@ -18512,6 +19237,8 @@ 435114EE2C9848340060394E /* ar */, 438C36D12C984A41003AAC02 /* gl */, 43C93FAC2CBD35B20006D3F8 /* gd */, + 4318899B2CD8E1A300365FD8 /* ab */, + 4302509E2CEB80E500936045 /* id */, ); name = BottomSheet.strings; sourceTree = ""; @@ -18582,6 +19309,7 @@ 430132392ADD559F00B1FAD7 /* sq */, 4364EA882B1DE9FA003A1240 /* bs */, 43EB5F9C2B2728080063D23E /* ug */, + 431889A12CD8E1A300365FD8 /* ab */, ); name = "Edit Card.strings"; sourceTree = ""; @@ -18653,6 +19381,7 @@ 437B0B2D2A6E941500DD9F66 /* hr */, 4364EA832B1DE9FA003A1240 /* bs */, 43EB5F972B2728070063D23E /* ug */, + 431889962CD8E1A300365FD8 /* ab */, ); name = Alerts.strings; sourceTree = ""; @@ -18723,6 +19452,7 @@ 437B0B3C2A6E941600DD9F66 /* hr */, 4364EA9B2B1DE9FB003A1240 /* bs */, 43EB5FAD2B2728080063D23E /* ug */, + 431889C32CD8E1A600365FD8 /* ab */, ); name = TabsTray.strings; sourceTree = ""; @@ -18784,6 +19514,9 @@ 438C36D62C984A41003AAC02 /* gl */, 4312225C2C984B6F0031D9E4 /* ko */, 43D3E3F12CAABFF100205186 /* es */, + 431889B12CD8E1A500365FD8 /* ab */, + 438DB4D52CDE551C00A6F1BE /* gd */, + 4383E6ED2CDE585A0044260D /* sat-Olck */, ); name = NativeErrorPage.strings; sourceTree = ""; @@ -18851,6 +19584,8 @@ 435114EC2C9848330060394E /* ar */, 438C36CE2C984A40003AAC02 /* gl */, 43C93FA92CBD35B20006D3F8 /* gd */, + 431889932CD8E1A200365FD8 /* ab */, + 430250992CEB80E500936045 /* id */, ); name = ActivityStream.strings; sourceTree = ""; @@ -18918,6 +19653,8 @@ 435114ED2C9848340060394E /* ar */, 438C36CF2C984A41003AAC02 /* gl */, 43C93FAA2CBD35B20006D3F8 /* gd */, + 431889942CD8E1A300365FD8 /* ab */, + 4302509A2CEB80E500936045 /* id */, ); name = AddressToolbar.strings; sourceTree = ""; @@ -18985,6 +19722,7 @@ 438C36D32C984A41003AAC02 /* gl */, 43C93FB22CBD35B30006D3F8 /* gd */, 43263DA92CBD3A12000C2C5F /* sat-Olck */, + 431889A52CD8E1A400365FD8 /* ab */, ); name = EnhancedTrackingProtection.strings; sourceTree = ""; @@ -19048,6 +19786,8 @@ 4374D5482C8F14DB00747602 /* uk */, 438C36D72C984A41003AAC02 /* gl */, 43C93FB62CBD35B30006D3F8 /* gd */, + 431889B62CD8E1A500365FD8 /* ab */, + 4383E6F02CDE585B0044260D /* sat-Olck */, ); name = QRCode.strings; sourceTree = ""; @@ -19118,6 +19858,7 @@ 4392732F2A6E95B50014E253 /* su */, 4364EA8A2B1DE9FA003A1240 /* bs */, 43EB5F9D2B2728080063D23E /* ug */, + 431889A42CD8E1A400365FD8 /* ab */, ); name = EngagementNotification.strings; sourceTree = ""; @@ -19189,6 +19930,7 @@ 439273332A6E95B50014E253 /* su */, 4364EA912B1DE9FB003A1240 /* bs */, 43EB5FA32B2728080063D23E /* ug */, + 431889B32CD8E1A500365FD8 /* ab */, ); name = Onboarding.strings; sourceTree = ""; @@ -19259,6 +20001,7 @@ 43EB5FA52B2728080063D23E /* ug */, 437E34352C3C03870025BFA9 /* ro */, 43C9CD462C6A26D9002F86B4 /* hr */, + 431889B82CD8E1A500365FD8 /* ab */, ); name = ResearchSurface.strings; sourceTree = ""; @@ -19328,6 +20071,7 @@ 43EB5FA22B2728080063D23E /* ug */, 439E22232C04A5620061A923 /* su */, 43E0BE3F2C60EC4C004FED65 /* hr */, + 431889B22CD8E1A500365FD8 /* ab */, ); name = Notification.strings; sourceTree = ""; @@ -19397,6 +20141,7 @@ 4364EA9F2B1DE9FB003A1240 /* bs */, 43EB5FB12B2728090063D23E /* ug */, 43E0BE432C60EC4C004FED65 /* hr */, + 431889C92CD8E1A600365FD8 /* ab */, ); name = ZoomPageBar.strings; sourceTree = ""; @@ -19467,6 +20212,8 @@ 4364EA8E2B1DE9FB003A1240 /* bs */, 43EB5FA02B2728080063D23E /* ug */, 43B891012C32C60B0049F033 /* es-MX */, + 431889AA2CD8E1A400365FD8 /* ab */, + 430250A32CEB80E600936045 /* id */, ); name = Footer.strings; sourceTree = ""; @@ -19536,6 +20283,7 @@ 4364EA922B1DE9FB003A1240 /* bs */, 43EB5FA42B2728080063D23E /* ug */, 43EC79AC2C3C00BF00519D98 /* es-MX */, + 431889B72CD8E1A500365FD8 /* ab */, ); name = RememberCard.strings; sourceTree = ""; @@ -19605,6 +20353,7 @@ 4364EA9D2B1DE9FB003A1240 /* bs */, 43EB5FAF2B2728080063D23E /* ug */, 4300B0E12C57B06E00F609C1 /* es-MX */, + 431889C72CD8E1A600365FD8 /* ab */, ); name = UpdateCard.strings; sourceTree = ""; @@ -19668,6 +20417,9 @@ 43CCB4D72C32C527001F2EBB /* ca */, 43EC79AB2C3C00BF00519D98 /* es-MX */, 43C9CD442C6A26D9002F86B4 /* hr */, + 431889B42CD8E1A500365FD8 /* ab */, + 438DB4D62CDE551C00A6F1BE /* gd */, + 4383E6EE2CDE585A0044260D /* sat-Olck */, ); name = PasswordAutofill.strings; sourceTree = ""; @@ -19736,6 +20488,7 @@ 43C9CD422C6A26D8002F86B4 /* hr */, 4313C3C72C85D6EB00813E30 /* lo */, 43C93FAD2CBD35B20006D3F8 /* gd */, + 4318899C2CD8E1A300365FD8 /* ab */, ); name = ContextualHints.strings; sourceTree = ""; @@ -19804,6 +20557,7 @@ 4361DF2B2C453DB200CE1295 /* sat-Olck */, 43E0BE362C60EC4B004FED65 /* hr */, 43C93FAE2CBD35B20006D3F8 /* gd */, + 4318899D2CD8E1A300365FD8 /* ab */, ); name = CredentialProvider.strings; sourceTree = ""; @@ -19871,6 +20625,7 @@ 43079D202C85D3BB0047C28D /* br */, 4313C3C82C85D6EB00813E30 /* lo */, 43C93FAF2CBD35B30006D3F8 /* gd */, + 4318899E2CD8E1A300365FD8 /* ab */, ); name = Credentials.strings; sourceTree = ""; @@ -19938,6 +20693,9 @@ 43B891002C32C60B0049F033 /* es-MX */, 43E0BE3A2C60EC4C004FED65 /* hr */, 43C93FB32CBD35B30006D3F8 /* gd */, + 431889A82CD8E1A400365FD8 /* ab */, + 4383E6E62CDE585A0044260D /* sat-Olck */, + 430250A12CEB80E600936045 /* id */, ); name = FirefoxLogins.strings; sourceTree = ""; @@ -20004,6 +20762,9 @@ 43CCB4D52C32C527001F2EBB /* ca */, 43B891022C32C60B0049F033 /* es-MX */, 43E0BE3D2C60EC4C004FED65 /* hr */, + 431889AE2CD8E1A400365FD8 /* ab */, + 438DB4D32CDE551C00A6F1BE /* gd */, + 4383E6EA2CDE585A0044260D /* sat-Olck */, ); name = LoginsHelper.strings; sourceTree = ""; @@ -20068,6 +20829,9 @@ 43C9CD492C6A26D9002F86B4 /* hr */, 43079D252C85D3BB0047C28D /* br */, 435D18192C85D4DC005F72B1 /* es-MX */, + 431889C42CD8E1A600365FD8 /* ab */, + 438DB4DD2CDE551D00A6F1BE /* gd */, + 439D70DE2CEB84610075AD08 /* sat-Olck */, ); name = TabToolbar.strings; sourceTree = ""; @@ -20130,6 +20894,8 @@ 43C09A7E2C7C9EB400BCD71D /* uk */, 438C36D82C984A41003AAC02 /* gl */, 43C93FB72CBD35B30006D3F8 /* gd */, + 431889B92CD8E1A500365FD8 /* ab */, + 4383E6F12CDE585B0044260D /* sat-Olck */, ); name = ScanQRCode.strings; sourceTree = ""; @@ -20194,6 +20960,10 @@ 4300B0DC2C57B06E00F609C1 /* es-MX */, 43E0BE3E2C60EC4C004FED65 /* hr */, 438C36D52C984A41003AAC02 /* gl */, + 431889B02CD8E1A500365FD8 /* ab */, + 438DB4D42CDE551C00A6F1BE /* gd */, + 4383E6EC2CDE585A0044260D /* sat-Olck */, + 430250A52CEB80E600936045 /* id */, ); name = Microsurvey.strings; sourceTree = ""; @@ -20265,6 +21035,8 @@ 4364EA8D2B1DE9FB003A1240 /* bs */, 43EB5F9F2B2728080063D23E /* ug */, 43E59DCD2B9F20A100A44FD3 /* ro */, + 431889A92CD8E1A400365FD8 /* ab */, + 430250A22CEB80E600936045 /* id */, ); name = FirefoxSync.strings; sourceTree = ""; @@ -20334,6 +21106,7 @@ 4301323F2ADD559F00B1FAD7 /* sq */, 4364EA9E2B1DE9FB003A1240 /* bs */, 43EB5FB02B2728090063D23E /* ug */, + 431889C82CD8E1A600365FD8 /* ab */, ); name = Upgrade.strings; sourceTree = ""; @@ -20401,6 +21174,8 @@ 435114F02C9848340060394E /* ar */, 438C36D22C984A41003AAC02 /* gl */, 43C93FB12CBD35B30006D3F8 /* gd */, + 431889A22CD8E1A400365FD8 /* ab */, + 4302509F2CEB80E600936045 /* id */, ); name = EditAddress.strings; sourceTree = ""; @@ -20467,6 +21242,9 @@ 4357B4932C168FF1003518CF /* su */, 43EC79AD2C3C00BF00519D98 /* es-MX */, 43C9CD482C6A26D9002F86B4 /* hr */, + 431889BE2CD8E1A600365FD8 /* ab */, + 438DB4D92CDE551D00A6F1BE /* gd */, + 4383E6F32CDE585B0044260D /* sat-Olck */, ); name = Share.strings; sourceTree = ""; @@ -20537,6 +21315,7 @@ 4364EA872B1DE9FA003A1240 /* bs */, 43EB5F9B2B2728080063D23E /* ug */, 43B890FE2C32C60B0049F033 /* es-MX */, + 431889A02CD8E1A300365FD8 /* ab */, ); name = DisplayCard.strings; sourceTree = ""; @@ -20607,6 +21386,7 @@ 437B0B382A6E941600DD9F66 /* hr */, 4364EA942B1DE9FB003A1240 /* bs */, 43EB5FA62B2728080063D23E /* ug */, + 431889BB2CD8E1A500365FD8 /* ab */, ); name = SearchHeaderTitle.strings; sourceTree = ""; @@ -20667,6 +21447,10 @@ 4345B7712CBD339D00A50A82 /* bs */, 43C93FB52CBD35B30006D3F8 /* gd */, 43339EC42CC672950025F32C /* rm */, + 431889B52CD8E1A500365FD8 /* ab */, + 4383E6EF2CDE585A0044260D /* sat-Olck */, + 43C6A8302CEB7D9A00FDAC33 /* ca */, + 438F8BCB2CEB7DB600AC873C /* co */, ); name = PasswordGenerator.strings; sourceTree = ""; @@ -20728,6 +21512,9 @@ 43C09A7F2C7C9EB400BCD71D /* uk */, 438C36DA2C984A41003AAC02 /* gl */, 43D3E3F42CAABFF100205186 /* es */, + 431889C52CD8E1A600365FD8 /* ab */, + 438DB4DE2CDE551D00A6F1BE /* gd */, + 439D70DF2CEB84610075AD08 /* sat-Olck */, ); name = Toolbar.strings; sourceTree = ""; @@ -20798,6 +21585,7 @@ 43B890FD2C32C60B0049F033 /* es-MX */, 435114EF2C9848340060394E /* ar */, 43C93FB02CBD35B30006D3F8 /* gd */, + 4318899F2CD8E1A300365FD8 /* ab */, ); name = CustomizeFirefoxHome.strings; sourceTree = ""; @@ -20868,6 +21656,8 @@ 4392732B2A6E95B50014E253 /* su */, 4364EA822B1DE9FA003A1240 /* bs */, 43EB5F962B2728070063D23E /* ug */, + 431889952CD8E1A300365FD8 /* ab */, + 4302509B2CEB80E500936045 /* id */, ); name = Alert.strings; sourceTree = ""; @@ -20939,6 +21729,8 @@ 4364EA892B1DE9FA003A1240 /* bs */, 432FF9F82B1DED6E003E473B /* ug */, 43E59DCC2B9F20A100A44FD3 /* ro */, + 431889A32CD8E1A400365FD8 /* ab */, + 430250A02CEB80E600936045 /* id */, ); name = EditCard.strings; sourceTree = ""; @@ -21010,6 +21802,7 @@ 4364EA962B1DE9FB003A1240 /* bs */, 43EB5FA82B2728080063D23E /* ug */, 43E59DCE2B9F20A200A44FD3 /* ro */, + 431889BD2CD8E1A600365FD8 /* ab */, ); name = Settings.strings; sourceTree = ""; @@ -21079,6 +21872,7 @@ 43D2F8B82A8A45570095D4EB /* su */, 4364EA992B1DE9FB003A1240 /* bs */, 43EB5FAB2B2728080063D23E /* ug */, + 431889C02CD8E1A600365FD8 /* ab */, ); name = SnackBar.strings; sourceTree = ""; @@ -21183,6 +21977,7 @@ D4BF6AAA2893DDA7001B5130 /* en-US */, A85F9E4D289A4C9800DCBF19 /* be */, 4303F4632B0B794D00C3B8E1 /* ug */, + 431889832CD8E1A100365FD8 /* ab */, ); name = ClearHistoryConfirm.strings; sourceTree = ""; @@ -21286,6 +22081,7 @@ D4BF6AA82893DDA7001B5130 /* en-US */, A85F9E4B289A4C9800DCBF19 /* be */, 4303F4612B0B794D00C3B8E1 /* ug */, + 431889812CD8E1A100365FD8 /* ab */, ); name = 3DTouchActions.strings; sourceTree = ""; @@ -21389,6 +22185,7 @@ D4BF6AAB2893DDA7001B5130 /* en-US */, A85F9E4E289A4C9800DCBF19 /* be */, 4303F4642B0B794D00C3B8E1 /* ug */, + 431889842CD8E1A100365FD8 /* ab */, ); name = ClearPrivateData.strings; sourceTree = ""; @@ -21493,6 +22290,7 @@ 437C3A1E28B39A4C0034468A /* af */, 432FACF628B39FD5008C76C1 /* uz */, 4303F4692B0B794D00C3B8E1 /* ug */, + 431889892CD8E1A200365FD8 /* ab */, ); name = HistoryPanel.strings; sourceTree = ""; @@ -21597,6 +22395,7 @@ D4BF6AB22893DDA8001B5130 /* en-US */, A85F9E55289A4C9800DCBF19 /* be */, 4303F46B2B0B794E00C3B8E1 /* ug */, + 4318898B2CD8E1A200365FD8 /* ab */, ); name = Localizable.strings; sourceTree = ""; @@ -21701,6 +22500,7 @@ A85F9E4A289A4C9800DCBF19 /* be */, 430170C928B903CA007A163A /* en-US */, 4303F4602B0B794D00C3B8E1 /* ug */, + 431889802CD8E1A100365FD8 /* ab */, ); name = InfoPlist.strings; sourceTree = ""; @@ -21803,6 +22603,7 @@ D4BF6AB52893DDA9001B5130 /* en-US */, 437C672B28D88484001A5C31 /* be */, 43EB5F912B2728070063D23E /* ug */, + 4318898E2CD8E1A200365FD8 /* ab */, ); name = PrivateBrowsing.strings; sourceTree = ""; @@ -21906,6 +22707,7 @@ D4BF6AAE2893DDA8001B5130 /* en-US */, A85F9E51289A4C9800DCBF19 /* be */, 4303F4672B0B794D00C3B8E1 /* ug */, + 431889872CD8E1A200365FD8 /* ab */, ); name = ErrorPages.strings; sourceTree = ""; @@ -22008,6 +22810,7 @@ D4BF6AB82893DDAA001B5130 /* en-US */, 437C672D28D88484001A5C31 /* be */, 43EB5F942B2728070063D23E /* ug */, + 431889912CD8E1A200365FD8 /* ab */, ); name = Storage.strings; sourceTree = ""; @@ -22101,6 +22904,7 @@ 4303F4662B0B794D00C3B8E1 /* ug */, 4364EA802B1DE9FA003A1240 /* bs */, 437227122BA8575600147F81 /* bg */, + 431889862CD8E1A100365FD8 /* ab */, ); name = "Default Browser.strings"; sourceTree = ""; @@ -22203,6 +23007,7 @@ D4BF6AB12893DDA8001B5130 /* en-US */, A85F9E54289A4C9800DCBF19 /* be */, 4303F46A2B0B794D00C3B8E1 /* ug */, + 4318898A2CD8E1A200365FD8 /* ab */, ); name = Intro.strings; sourceTree = ""; @@ -22307,6 +23112,7 @@ 43C6A09628B39E8600DFC656 /* or */, 43DC319F28B39F100034E6DF /* si */, 4303F46C2B0B794E00C3B8E1 /* ug */, + 4318898C2CD8E1A200365FD8 /* ab */, ); name = LoginManager.strings; sourceTree = ""; @@ -22409,6 +23215,7 @@ 43DC31A128B39F100034E6DF /* si */, 437C672C28D88484001A5C31 /* be */, 43EB5F922B2728070063D23E /* ug */, + 4318898F2CD8E1A200365FD8 /* ab */, ); name = Search.strings; sourceTree = ""; @@ -22512,6 +23319,7 @@ D4BF6AAF2893DDA8001B5130 /* en-US */, A85F9E52289A4C9800DCBF19 /* be */, 4303F4682B0B794D00C3B8E1 /* ug */, + 431889882CD8E1A200365FD8 /* ab */, ); name = FindInPage.strings; sourceTree = ""; @@ -22615,6 +23423,7 @@ 432FACF728B39FD5008C76C1 /* uz */, 430170CA28B903CA007A163A /* en-US */, 4303F46D2B0B794E00C3B8E1 /* ug */, + 431889982CD8E1A300365FD8 /* ab */, ); name = BookmarkPanel.strings; sourceTree = ""; @@ -22694,6 +23503,7 @@ 4397617A2A0BCB860062C60C /* gl */, 4364EAA02B1DE9FB003A1240 /* bs */, 43EB5FB22B2728090063D23E /* ug */, + 431889CB2CD8E1A700365FD8 /* ab */, ); name = Localizable.strings; sourceTree = ""; @@ -22776,6 +23586,7 @@ 431853AC2A0911440099B0E0 /* gl */, 4364EA852B1DE9FA003A1240 /* bs */, 43EB5F992B2728070063D23E /* ug */, + 431889992CD8E1A300365FD8 /* ab */, ); name = BookmarkPanelDeleteConfirm.strings; sourceTree = ""; @@ -22880,6 +23691,7 @@ 8A05813928B56DB900FD8D46 /* bo */, 8A05813B28B56DD700FD8D46 /* uz */, 4303F46E2B0B794E00C3B8E1 /* ug */, + 431889CA2CD8E1A600365FD8 /* ab */, ); name = WidgetIntents.intentdefinition; sourceTree = ""; @@ -22984,6 +23796,7 @@ D4BF6AAC2893DDA7001B5130 /* en-US */, A85F9E4F289A4C9800DCBF19 /* be */, 4303F4652B0B794D00C3B8E1 /* ug */, + 431889852CD8E1A100365FD8 /* ab */, ); name = ClearPrivateDataConfirm.strings; sourceTree = ""; @@ -22991,40 +23804,82 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 282731771ABC9BE800AA1954 /* Fennec */ = { + 12C11FF02D2C2D5B00E4DDBF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.ecosiaapp.EcosiaSnapshotTests; + PRODUCT_NAME = EcosiaSnapshotTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Debug; + }; + 12C11FF12D2C2D5B00E4DDBF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.ecosiaapp.EcosiaSnapshotTests; + PRODUCT_NAME = EcosiaSnapshotTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Release; + }; + 12C11FF22D2C2D5B00E4DDBF /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.ecosiaapp.EcosiaSnapshotTests; + PRODUCT_NAME = EcosiaSnapshotTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Development_TestFlight; + }; + 282731771ABC9BE800AA1954 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; DEFINES_MODULE = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = Sync/Info.plist; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; }; - name = Fennec; + name = Debug; }; - 2827317A1ABC9BE800AA1954 /* Fennec */ = { + 2827317A1ABC9BE800AA1954 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; + name = Debug; }; - 288A2DA01AB8B3260023ABC3 /* Fennec */ = { + 288A2DA01AB8B3260023ABC3 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43AQ936H96; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; LOCALIZED_STRING_MACRO_NAMES = ( @@ -23033,12 +23888,57 @@ CFCopyLocalizedString, ); PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = Fennec; + name = Debug; + }; + 2C02BE252B0E319400FE80C2 /* BetaDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Beta; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = ""; + EXCLUDED_SOURCE_FILE_NAMES = "Client/Nimbus/TestData/*"; + INFOPLIST_FILE = Client/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = ( + "-ObjC", + "-ld_classic", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; + PRODUCT_MODULE_NAME = Client; + PRODUCT_NAME = Client; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; + VALIDATE_WORKSPACE = YES; + }; + name = BetaDebug; + }; + 2C02BE272B0E319400FE80C2 /* BetaDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Fennec.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + INFOPLIST_FILE = Extensions/NotificationService/Info.plist; + OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_RELEASE -DMOZ_TARGET_NOTIFICATIONSERVICE"; + PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = NO; + }; + name = BetaDebug; }; - 2FA436151ABB83B4008031D1 /* Fennec */ = { + 2C02BE2A2B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; @@ -23059,19 +23959,9 @@ ); SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; }; - name = Fennec; + name = BetaDebug; }; - 2FA436191ABB83B4008031D1 /* Fennec */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - }; - name = Fennec; - }; - 2FCAE2341ABB51F900877008 /* Fennec */ = { + 2C02BE2B2B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { DEFINES_MODULE = YES; @@ -23080,232 +23970,125 @@ SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; VALIDATE_WORKSPACE = YES; }; - name = Fennec; + name = BetaDebug; }; - 2FCAE2381ABB51F900877008 /* Fennec */ = { + 2C02BE2C2B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; + INFOPLIST_FILE = Tests/AccountTests/Info.plist; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; - }; - 3958DAB01ED98DCB0054AA27 /* Fennec_Enterprise */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/FennecEnterprise.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; - INFOPLIST_FILE = Extensions/NotificationService/Info.plist; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Xlinker", - "-no_application_extension", - "-fprofile-instr-generate", - ); - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_NOTIFICATIONSERVICE"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Fennec Enterprise NotificationService"; - SKIP_INSTALL = NO; - }; - name = Fennec_Enterprise; - }; - 397848E31ED86605004C0C0B /* Fennec */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Fennec.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = Extensions/NotificationService/Info.plist; - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_NOTIFICATIONSERVICE"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = NO; - }; - name = Fennec; - }; - 397848E51ED86605004C0C0B /* Firefox */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Firefox.entitlements"; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = Extensions/NotificationService/Info.plist; - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_RELEASE -DMOZ_TARGET_NOTIFICATIONSERVICE"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.Firefox.NotificationServic"; - SKIP_INSTALL = NO; - }; - name = Firefox; + name = BetaDebug; }; - 397848E61ED86605004C0C0B /* FirefoxBeta */ = { + 2C02BE2D2B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/FirefoxBeta.entitlements"; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = Extensions/NotificationService/Info.plist; - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_BETA -DMOZ_TARGET_NOTIFICATIONSERVICE"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)2"; - PRODUCT_NAME = NotificationService; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.FirefoxBeta.NotificationSe"; - SKIP_INSTALL = NO; + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = Tests/ClientTests/Info.plist; + PRODUCT_NAME = ClientTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = FirefoxBeta; + name = BetaDebug; }; - 3B43E3D71D95C48E00BBA9DB /* Fennec */ = { + 2C02BE2E2B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; ENABLE_TESTABILITY = YES; - INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + INFOPLIST_FILE = Tests/StoragePerfTests/Info.plist; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; + name = BetaDebug; }; - 3B43E3D81D95C48E00BBA9DB /* Firefox */ = { + 2C02BE2F2B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + INFOPLIST_FILE = Tests/StorageTests/Info.plist; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = BetaDebug; }; - 3B43E3D91D95C48E00BBA9DB /* FirefoxBeta */ = { + 2C02BE302B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + INFOPLIST_FILE = Tests/SharedTests/Info.plist; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = FirefoxBeta; + name = BetaDebug; }; - 3BFE4B0E1D342FB900DDF53F /* Fennec */ = { + 2C02BE312B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = "firefox-ios-tests/Tests/XCUITests/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; - PROVISIONING_PROFILE_SPECIFIER = "org.mozilla.ios.XCUITests 2021-08-12"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/firefox-ios-tests/Tests/XCUITests/XCUITests-Bridging-Header.h"; - TEST_TARGET_NAME = Client; + INFOPLIST_FILE = Tests/SyncTelemetryTests/Info.plist; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; + name = BetaDebug; }; - 3BFE4B0F1D342FB900DDF53F /* Firefox */ = { + 2C02BE322B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = "firefox-ios-tests/Tests/XCUITests/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.XCUITests"; - TEST_TARGET_NAME = Client; + INFOPLIST_FILE = Tests/SyncTests/Info.plist; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Tests/SyncTests/SyncTests-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = BetaDebug; }; - 3BFE4B101D342FB900DDF53F /* FirefoxBeta */ = { + 2C02BE342B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = "firefox-ios-tests/Tests/XCUITests/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.XCUITests"; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + PRODUCT_NAME = L10nSnapshotTests; TEST_TARGET_NAME = Client; + USES_XCTRUNNER = YES; }; - name = FirefoxBeta; - }; - 4354D3E124EEEEC5001184F6 /* Fennec */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Extensions/Entitlements/Fennec.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = WidgetKit/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 0.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).WidgetKit"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = NO; - }; - name = Fennec; + name = BetaDebug; }; - 4354D3E224EEEEC5001184F6 /* Fennec_Enterprise */ = { + 2C02BE362B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = Extensions/Entitlements/FennecEnterprise.entitlements; + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; - INFOPLIST_FILE = WidgetKit/Info.plist; - MARKETING_VERSION = 0.0.1; - OTHER_LDFLAGS = ( + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = CredentialProvider/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "-Xlinker", - "-no_application_extension", - "-fprofile-instr-generate", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", ); - PINFOPLIST_FILE = WidgetKit/Info.plist; - PRODUCT_BUNDLE_IDENTIFIER = "org.mozilla.ios.FennecEnterprise.Widget-Kit"; + PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Fennec Enterprise Widget-Kit"; - SKIP_INSTALL = NO; - }; - name = Fennec_Enterprise; - }; - 4354D3E324EEEEC5001184F6 /* Firefox */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Extensions/Entitlements/Firefox.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = WidgetKit/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 0.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).WidgetKit"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.Firefox.WidgetKit"; - SKIP_INSTALL = NO; - }; - name = Firefox; - }; - 4354D3E424EEEEC5001184F6 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = Extensions/Entitlements/FirefoxBeta.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = WidgetKit/Info.plist; - MARKETING_VERSION = 0.0.1; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).WidgetKit"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.FirefoxBeta.WidgetKit"; - SKIP_INSTALL = NO; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = FirefoxBeta; + name = BetaDebug; }; - 43BE5786278BA4D900491291 /* Fennec */ = { + 2C02BE372B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; @@ -23314,130 +24097,69 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - EAGER_LINKING = NO; + DEFINES_MODULE = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; - INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = CredentialProvider/Info.plist; LOCALIZED_STRING_MACRO_NAMES = ( MZLocalizedString, NSLocalizedString, CFCopyLocalizedString, ); - PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = Fennec; + name = BetaDebug; }; - 43BE5787278BA4D900491291 /* Fennec_Enterprise */ = { + 2C02BE382B0E319400FE80C2 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; - CODE_SIGN_STYLE = Manual; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; - EAGER_LINKING = NO; + COPY_PHASE_STRIP = NO; + DEFINES_MODULE = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; - INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; - LOCALIZED_STRING_MACRO_NAMES = ( - MZLocalizedString, - NSLocalizedString, - CFCopyLocalizedString, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + INFOPLIST_FILE = Sync/Info.plist; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - 43BE5788278BA4D900491291 /* Firefox */ = { + 2C02BE392B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2CB288D02B07C6F300A8FCB3 /* EcosiaBeta.xcconfig */; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Manual; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - EAGER_LINKING = NO; - ENABLE_TESTABILITY = YES; - GCC_WARN_INHIBIT_ALL_WARNINGS = YES; - INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; - LOCALIZED_STRING_MACRO_NAMES = ( - MZLocalizedString, - NSLocalizedString, - CFCopyLocalizedString, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; - }; - name = Firefox; - }; - 43BE5789278BA4D900491291 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Manual; - DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - EAGER_LINKING = NO; - GCC_WARN_INHIBIT_ALL_WARNINGS = YES; - INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; - LOCALIZED_STRING_MACRO_NAMES = ( - MZLocalizedString, - NSLocalizedString, - CFCopyLocalizedString, - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; - }; - name = FirefoxBeta; - }; - 8A19936D2C9B5A63001F4D57 /* Fennec_Testing */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E60961861B62B8A700DD640F /* Fennec.xcconfig */; - buildSettings = { - DEBUG_INFORMATION_FORMAT = dwarf; - EAGER_LINKING = YES; - FUSE_BUILD_SCRIPT_PHASES = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + INFOPLIST_FILE = Client/Info.plist; OTHER_LDFLAGS = ( "$(inherited)", "-Xlinker", "-no_application_extension", ); - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = TESTING; - SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; VALIDATE_WORKSPACE = YES; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A19936E2C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE3A2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8AEDFE832C9DAB9C00821359 /* FennecTesting.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Developer; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 43AQ936H96; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Beta; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + EXCLUDED_SOURCE_FILE_NAMES = "Client/Nimbus/TestData/*"; + INFOPLIST_FILE = Client/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -23447,147 +24169,54 @@ "-ObjC", "-ld_classic", ); - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC"; PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; PRODUCT_MODULE_NAME = Client; PRODUCT_NAME = Client; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox"; SKIP_INSTALL = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = TESTING; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; VALIDATE_WORKSPACE = YES; }; - name = Fennec_Testing; - }; - 8A19936F2C9B5A63001F4D57 /* Fennec_Testing */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Fennec.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = Extensions/NotificationService/Info.plist; - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_NOTIFICATIONSERVICE"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = NO; - }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993702C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE3C2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E152D2812B200E4DDBF /* EcosiaBeta.ShareTo.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Fennec.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 43AQ936H96; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; INFOPLIST_FILE = Extensions/ShareTo/Info.plist; - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_SHARETO"; + OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_BETA -DMOZ_TARGET_SHARETO"; PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox.ShareTo"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993712C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE3D2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E162D2812B200E4DDBF /* EcosiaBeta.WidgetKit.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = Extensions/Entitlements/Fennec.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 43AQ936H96; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; INFOPLIST_FILE = WidgetKit/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 0.0.1; PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).WidgetKit"; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox.WidgetKit"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; }; - name = Fennec_Testing; - }; - 8A1993722C9B5A63001F4D57 /* Fennec_Testing */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_ENTITLEMENTS = CredentialProvider/CredentialProviderFennec.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 43AQ936H96; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CredentialProvider/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 0.0.1; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_CREDENTIAL_PROVIDER"; - PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.Fennec.CredentialProvider; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993732C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE3F2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; DEFINES_MODULE = YES; + ENABLE_TESTABILITY = YES; HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, @@ -23604,61 +24233,61 @@ ); SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993742C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE402B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { DEFINES_MODULE = YES; INFOPLIST_FILE = Storage/Info.plist; - SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; - VALIDATE_WORKSPACE = YES; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993752C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE412B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993762C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE422B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; - LOCALIZATION_EXPORT_SUPPORTED = NO; PRODUCT_NAME = ClientTests; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993772C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE432B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; - ENABLE_TESTABILITY = YES; INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993782C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE442B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993792C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE452B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -23666,18 +24295,18 @@ SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A19937A2C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE462B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTelemetryTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A19937B2C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE472B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -23685,20 +24314,9 @@ SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Testing; - }; - 8A19937C2C9B5A63001F4D57 /* Fennec_Testing */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/UITests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/firefox-ios/firefox-ios-tests/Tests/UITests/UITests-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - VALIDATE_WORKSPACE = YES; - }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A19937D2C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE492B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -23707,35 +24325,17 @@ TEST_TARGET_NAME = Client; USES_XCTRUNNER = YES; }; - name = Fennec_Testing; - }; - 8A19937E2C9B5A63001F4D57 /* Fennec_Testing */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - INFOPLIST_FILE = "firefox-ios-tests/Tests/XCUITests/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; - PROVISIONING_PROFILE_SPECIFIER = "org.mozilla.ios.XCUITests 2021-08-12"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/firefox-ios-tests/Tests/XCUITests/XCUITests-Bridging-Header.h"; - TEST_TARGET_NAME = Client; - }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A19937F2C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE4B2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - COPY_PHASE_STRIP = NO; DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = 43AQ936H96; - EAGER_LINKING = NO; + DEVELOPMENT_TEAM = ""; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; LOCALIZED_STRING_MACRO_NAMES = ( @@ -23745,22 +24345,19 @@ ); PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993802C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE4C2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - COPY_PHASE_STRIP = NO; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43AQ936H96; + DEVELOPMENT_TEAM = ""; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; LOCALIZED_STRING_MACRO_NAMES = ( @@ -23769,70 +24366,59 @@ CFCopyLocalizedString, ); PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = Fennec_Testing; + name = Development_Firebase; }; - 8A1993812C9B5A63001F4D57 /* Fennec_Testing */ = { + 2C02BE4D2B0E31B100FE80C2 /* Development_Firebase */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; - COPY_PHASE_STRIP = NO; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Manual; DEFINES_MODULE = YES; + ENABLE_TESTABILITY = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = Sync/Info.plist; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; }; - name = Fennec_Testing; - }; - D39FA1681A83E0EC00EE869C /* Fennec */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/UITests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/firefox-ios/firefox-ios-tests/Tests/UITests/UITests-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - VALIDATE_WORKSPACE = YES; - }; - name = Fennec; - }; - D39FA1691A83E0EC00EE869C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - INFOPLIST_FILE = "firefox-ios-tests/Tests/UITests/Info.plist"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - VALIDATE_WORKSPACE = YES; - }; - name = Release; + name = Development_Firebase; }; - E448FC9D1AEE7A6000869B6C /* Firefox */ = { + 2C2F32022D46A5CC00977F55 /* Testing */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E60961891B62B8C800DD640F /* Firefox.xcconfig */; + baseConfigurationReference = 2C2F32012D46A53000977F55 /* EcosiaTesting.xcconfig */; buildSettings = { - EAGER_LINKING = YES; - FUSE_BUILD_SCRIPT_PHASES = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + DEVELOPMENT_TEAM = 33YMRSYD2L; + INFOPLIST_FILE = Client/Info.plist; OTHER_LDFLAGS = ( "$(inherited)", "-Xlinker", "-no_application_extension", ); - SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; VALIDATE_WORKSPACE = YES; }; - name = Firefox; + name = Testing; }; - E448FC9E1AEE7A6000869B6C /* Firefox */ = { + 2C2F32032D46A5CC00977F55 /* Testing */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Developer; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; - EXCLUDED_SOURCE_FILE_NAMES = "Client/Nimbus/TestData/*"; + EXCLUDED_ARCHS = ""; + EXCLUDED_SOURCE_FILE_NAMES = ( + CredentialProvider.appex, + "Client/Nimbus/TestData/*", + ); INFOPLIST_FILE = Client/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -23846,72 +24432,1781 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; PRODUCT_MODULE_NAME = Client; PRODUCT_NAME = Client; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.Firefox"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.firefox"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; VALIDATE_WORKSPACE = YES; }; - name = Firefox; - }; - E448FC9F1AEE7A6000869B6C /* Firefox */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; - LOCALIZATION_EXPORT_SUPPORTED = NO; - PRODUCT_NAME = ClientTests; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - }; - name = Firefox; + name = Testing; }; - E448FCA01AEE7A6000869B6C /* Firefox */ = { + 2C2F32042D46A5CC00977F55 /* Testing */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2C2F32172D46AA2C00977F55 /* EcosiaTesting.ShareTo.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Firefox.entitlements"; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 33YMRSYD2L; INFOPLIST_FILE = Extensions/ShareTo/Info.plist; - OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_RELEASE -DMOZ_TARGET_SHARETO"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.Firefox.ShareTo"; + OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_SHARETO"; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.firefox.ShareTo"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; }; - name = Firefox; + name = Testing; }; - E448FCA21AEE7A6000869B6C /* Firefox */ = { + 2C2F32052D46A5CC00977F55 /* Testing */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 2C2F32182D46AA2C00977F55 /* EcosiaTesting.WidgetKit.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - VALIDATE_WORKSPACE = YES; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 33YMRSYD2L; + INFOPLIST_FILE = WidgetKit/Info.plist; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Xlinker", + "-no_application_extension", + "-fprofile-instr-generate", + ); + PINFOPLIST_FILE = WidgetKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.firefox.WidgetKit"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; }; - name = Firefox; + name = Testing; }; - E448FCA31AEE7A6000869B6C /* Firefox */ = { + 2C2F32062D46A5CC00977F55 /* Testing */ = { isa = XCBuildConfiguration; buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Manual; + ALWAYS_SEARCH_USER_PATHS = YES; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43AQ936H96; - ENABLE_TESTABILITY = YES; - GCC_WARN_INHIBIT_ALL_WARNINGS = YES; - INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)\"", + "\"$(SDKROOT)/usr/include/libxml2\"", + "\"$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)/include/**\"", + "ThirdParty/ecec/include/**", + FxA/FxA/include, + ); + INFOPLIST_FILE = Account/Info.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/FxA/FxA/lib", + ); + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; + }; + name = Testing; + }; + 2C2F32072D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEFINES_MODULE = YES; + INFOPLIST_FILE = Storage/Info.plist; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; + }; + name = Testing; + }; + 2C2F32082D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F32092D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; + PRODUCT_NAME = ClientTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F320A2D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F320B2D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F320C2D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/SharedTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F320D2D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTelemetryTests/Info.plist"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F320E2D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Testing; + }; + 2C2F320F2D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + PRODUCT_NAME = L10nSnapshotTests; + TEST_TARGET_NAME = Client; + USES_XCTRUNNER = YES; + }; + name = Testing; + }; + 2C2F32102D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = NO; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; + LOCALIZED_STRING_MACRO_NAMES = ( + MZLocalizedString, + NSLocalizedString, + CFCopyLocalizedString, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + }; + name = Testing; + }; + 2C2F32112D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; + LOCALIZED_STRING_MACRO_NAMES = ( + MZLocalizedString, + NSLocalizedString, + CFCopyLocalizedString, + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + }; + name = Testing; + }; + 2C2F32122D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = Sync/Info.plist; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; + }; + name = Testing; + }; + 2C2F32132D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.ecosiaapp.EcosiaSnapshotTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + VALIDATE_PRODUCT = YES; + }; + name = Testing; + }; + 2C2F32142D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mozilla. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.Ecosia; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Testing; + }; + 2C2F32152D46A5CC00977F55 /* Testing */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.EcosiaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Testing; + }; + 2C6C908A2C614A17007D9B43 /* BetaDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.ecosiaapp.EcosiaSnapshotTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + VALIDATE_PRODUCT = YES; + }; + name = BetaDebug; + }; + 2C6C908D2C614A17007D9B43 /* Development_Firebase */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.ecosiaapp.EcosiaSnapshotTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Development_Firebase; + }; + 2CFE997F2D45329500B25CE0 /* BetaDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mozilla. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.Ecosia; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = BetaDebug; + }; + 2CFE99802D45329500B25CE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/Ecosia.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mozilla. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.Ecosia; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2CFE99812D45329500B25CE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/Ecosia.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mozilla. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.Ecosia; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 2CFE99822D45329500B25CE0 /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mozilla. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.Ecosia; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Development_TestFlight; + }; + 2CFE99832D45329500B25CE0 /* Development_Firebase */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2025 Mozilla. All rights reserved."; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.Ecosia; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Development_Firebase; + }; + 2CFE99852D45329500B25CE0 /* BetaDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.EcosiaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = BetaDebug; + }; + 2CFE99862D45329500B25CE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.EcosiaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Debug; + }; + 2CFE99872D45329500B25CE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.EcosiaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 2CFE99882D45329500B25CE0 /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.EcosiaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Development_TestFlight; + }; + 2CFE99892D45329500B25CE0 /* Development_Firebase */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 4; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.ecosia.framework.EcosiaTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Client"; + }; + name = Development_Firebase; + }; + 2FA436151ABB83B4008031D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; + DEFINES_MODULE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)\"", + "\"$(SDKROOT)/usr/include/libxml2\"", + "\"$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)/include/**\"", + "ThirdParty/ecec/include/**", + FxA/FxA/include, + ); + INFOPLIST_FILE = Account/Info.plist; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/FxA/FxA/lib", + ); + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; + }; + name = Debug; + }; + 2FA436191ABB83B4008031D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Debug; + }; + 2FCAE2341ABB51F900877008 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEFINES_MODULE = YES; + INFOPLIST_FILE = Storage/Info.plist; + SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; + VALIDATE_WORKSPACE = YES; + }; + name = Debug; + }; + 2FCAE2381ABB51F900877008 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Debug; + }; + 3B43E3D71D95C48E00BBA9DB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + ENABLE_TESTABILITY = YES; + INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Debug; + }; + 3B43E3D81D95C48E00BBA9DB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Release; + }; + 3B43E3D91D95C48E00BBA9DB /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Development_TestFlight; + }; + 4354D3E124EEEEC5001184F6 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E1A2D2812B200E4DDBF /* EcosiaDebug.WidgetKit.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 33YMRSYD2L; + INFOPLIST_FILE = WidgetKit/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.WidgetKit"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + }; + name = Debug; + }; + 4354D3E224EEEEC5001184F6 /* BetaDebug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E182D2812B200E4DDBF /* EcosiaBetaDebug.WidgetKit.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 33YMRSYD2L; + INFOPLIST_FILE = WidgetKit/Info.plist; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Xlinker", + "-no_application_extension", + "-fprofile-instr-generate", + ); + PINFOPLIST_FILE = WidgetKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.firefox.WidgetKit"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + }; + name = BetaDebug; + }; + 4354D3E324EEEEC5001184F6 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C11F672D2C299D00E4DDBF /* Ecosia.WidgetKit.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + INFOPLIST_FILE = WidgetKit/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.ecosia.ecosiaapp.WidgetKit"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + }; + name = Release; + }; + 4354D3E424EEEEC5001184F6 /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E162D2812B200E4DDBF /* EcosiaBeta.WidgetKit.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + INFOPLIST_FILE = WidgetKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox.WidgetKit"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + }; + name = Development_TestFlight; + }; + 43BE5786278BA4D900491291 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEFINES_MODULE = NO; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; + LOCALIZED_STRING_MACRO_NAMES = ( + MZLocalizedString, + NSLocalizedString, + CFCopyLocalizedString, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + }; + name = Debug; + }; + 43BE5787278BA4D900491291 /* BetaDebug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = NO; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; + LOCALIZED_STRING_MACRO_NAMES = ( + MZLocalizedString, + NSLocalizedString, + CFCopyLocalizedString, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + }; + name = BetaDebug; + }; + 43BE5788278BA4D900491291 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = NO; + DEVELOPMENT_TEAM = ""; + ENABLE_TESTABILITY = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; + LOCALIZED_STRING_MACRO_NAMES = ( + MZLocalizedString, + NSLocalizedString, + CFCopyLocalizedString, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + }; + name = Release; + }; + 43BE5789278BA4D900491291 /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = NO; + DEVELOPMENT_TEAM = ""; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "RustMozillaAppServices-Info.plist"; + LOCALIZED_STRING_MACRO_NAMES = ( + MZLocalizedString, + NSLocalizedString, + CFCopyLocalizedString, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + }; + name = Development_TestFlight; + }; + E448FC9D1AEE7A6000869B6C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2CB288D32B07C6F400A8FCB3 /* Ecosia.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/Ecosia.entitlements; + DEBUG_INFORMATION_FORMAT = dwarf; + EAGER_LINKING = "$(inherited)"; + FUSE_BUILD_SCRIPT_PHASES = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + INFOPLIST_FILE = Client/Info.plist; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Xlinker", + "-no_application_extension", + ); + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + VALIDATE_WORKSPACE = YES; + }; + name = Release; + }; + E448FC9E1AEE7A6000869B6C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + EXCLUDED_SOURCE_FILE_NAMES = "Client/Nimbus/TestData/*"; + INFOPLIST_FILE = Client/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; + OTHER_LDFLAGS = ( + "-ObjC", + "-ld_classic", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; + PRODUCT_MODULE_NAME = Client; + PRODUCT_NAME = Client; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.ecosia.ecosiaapp"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; + VALIDATE_WORKSPACE = YES; + }; + name = Release; + }; + E448FC9F1AEE7A6000869B6C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; + PRODUCT_NAME = ClientTests; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Release; + }; + E448FCA01AEE7A6000869B6C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 12C11F662D2C298600E4DDBF /* Ecosia.ShareTo.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + INFOPLIST_FILE = Extensions/ShareTo/Info.plist; + OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_RELEASE -DMOZ_TARGET_SHARETO"; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.ecosia.ecosiaapp.ShareTo"; + SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; + }; + name = Release; + }; + E448FCA31AEE7A6000869B6C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + ENABLE_TESTABILITY = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; LOCALIZED_STRING_MACRO_NAMES = ( MZLocalizedString, NSLocalizedString, CFCopyLocalizedString, ); PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = Firefox; + name = Release; }; - E448FCA41AEE7A6000869B6C /* Firefox */ = { + E448FCA41AEE7A6000869B6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { DEFINES_MODULE = YES; @@ -23919,20 +26214,21 @@ INFOPLIST_FILE = Storage/Info.plist; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; }; - name = Firefox; + name = Release; }; - E448FCA51AEE7A6000869B6C /* Firefox */ = { + E448FCA51AEE7A6000869B6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = Release; }; - E448FCA61AEE7A6000869B6C /* Firefox */ = { + E448FCA61AEE7A6000869B6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; @@ -23953,33 +26249,35 @@ ); SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; }; - name = Firefox; + name = Release; }; - E448FCA71AEE7A6000869B6C /* Firefox */ = { + E448FCA71AEE7A6000869B6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = Release; }; - E448FCA81AEE7A6000869B6C /* Firefox */ = { + E448FCA81AEE7A6000869B6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; ENABLE_TESTABILITY = YES; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = Sync/Info.plist; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; }; - name = Firefox; + name = Release; }; - E448FCA91AEE7A6000869B6C /* Firefox */ = { + E448FCA91AEE7A6000869B6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -23987,9 +26285,9 @@ SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = Release; }; - E601384C1C89EAE600DF9756 /* Fennec */ = { + E601384C1C89EAE600DF9756 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -23998,9 +26296,9 @@ TEST_TARGET_NAME = Client; USES_XCTRUNNER = YES; }; - name = Fennec; + name = Debug; }; - E601384D1C89EAE600DF9756 /* Firefox */ = { + E601384D1C89EAE600DF9756 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -24009,9 +26307,9 @@ TEST_TARGET_NAME = Client; USES_XCTRUNNER = YES; }; - name = Firefox; + name = Release; }; - E601384E1C89EAE600DF9756 /* FirefoxBeta */ = { + E601384E1C89EAE600DF9756 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -24020,71 +26318,70 @@ TEST_TARGET_NAME = Client; USES_XCTRUNNER = YES; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E69DB0931E97DEAA008A67E6 /* Fennec */ = { + E69DB0931E97DEAA008A67E6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTelemetryTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; + name = Debug; }; - E69DB0941E97DEAA008A67E6 /* Fennec_Enterprise */ = { + E69DB0941E97DEAA008A67E6 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTelemetryTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E69DB0951E97DEAA008A67E6 /* Firefox */ = { + E69DB0951E97DEAA008A67E6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTelemetryTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = Release; }; - E69DB0961E97DEAA008A67E6 /* FirefoxBeta */ = { + E69DB0961E97DEAA008A67E6 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTelemetryTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6DCC2051DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2051DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E6DCC1ED1DCBB6AA00CEC4B7 /* Fennec.enterprise.xcconfig */; + baseConfigurationReference = 2CB288D12B07C6F300A8FCB3 /* EcosiaBetaDebug.xcconfig */; buildSettings = { - EAGER_LINKING = YES; - FUSE_BUILD_SCRIPT_PHASES = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + DEVELOPMENT_TEAM = 33YMRSYD2L; + INFOPLIST_FILE = Client/Info.plist; OTHER_LDFLAGS = ( "$(inherited)", "-Xlinker", "-no_application_extension", ); - SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; VALIDATE_WORKSPACE = YES; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2061DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2061DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Developer; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; EXCLUDED_ARCHS = ""; EXCLUDED_SOURCE_FILE_NAMES = ( CredentialProvider.appex, @@ -24100,47 +26397,43 @@ "-ObjC", "-ld_classic", ); - PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.FennecEnterprise; + PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; PRODUCT_MODULE_NAME = Client; PRODUCT_NAME = Client; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Fennec Enterprise Development"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.firefox"; SKIP_INSTALL = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; VALIDATE_WORKSPACE = YES; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2081DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2081DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E172D2812B200E4DDBF /* EcosiaBetaDebug.ShareTo.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/FennecEnterprise.entitlements"; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 33YMRSYD2L; INFOPLIST_FILE = Extensions/ShareTo/Info.plist; OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_SHARETO"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Fennec Enterprise ShareTo"; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.firefox.ShareTo"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC20B1DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC20B1DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43AQ936H96; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; LOCALIZED_STRING_MACRO_NAMES = ( @@ -24149,21 +26442,20 @@ CFCopyLocalizedString, ); PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC20C1DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC20C1DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { DEFINES_MODULE = YES; INFOPLIST_FILE = Storage/Info.plist; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC20D1DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC20D1DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; @@ -24184,125 +26476,100 @@ ); SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC20E1DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC20E1DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; + CODE_SIGN_STYLE = Manual; DEFINES_MODULE = YES; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = Sync/Info.plist; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2101DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2101DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; - LOCALIZATION_EXPORT_SUPPORTED = NO; PRODUCT_NAME = ClientTests; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; - }; - E6DCC2111DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/UITests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/firefox-ios/firefox-ios-tests/Tests/UITests/UITests-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - VALIDATE_WORKSPACE = YES; - }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2131DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2131DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2141DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2141DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2151DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2151DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2171DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2171DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/SharedTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC2181DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC2181DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; IPHONEOS_DEPLOYMENT_TARGET = 16.4; PRODUCT_NAME = L10nSnapshotTests; TEST_TARGET_NAME = Client; - USES_XCTRUNNER = YES; - }; - name = Fennec_Enterprise; - }; - E6DCC21A1DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; - INFOPLIST_FILE = "firefox-ios-tests/Tests/XCUITests/Info.plist"; - PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.XCUITests; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Fennec Enterprise XCUITests"; - SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/firefox-ios-tests/Tests/XCUITests/XCUITests-Bridging-Header.h"; - TEST_TARGET_NAME = Client; + USES_XCTRUNNER = YES; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6DCC21B1DCBB6F100CEC4B7 /* Fennec_Enterprise */ = { + E6DCC21B1DCBB6F100CEC4B7 /* BetaDebug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/StoragePerfTests/Info.plist"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec_Enterprise; + name = BetaDebug; }; - E6F965151B2F1CF20034B023 /* Fennec */ = { + E6F965151B2F1CF20034B023 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -24310,9 +26577,9 @@ SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; + name = Debug; }; - E6F965171B2F1CF20034B023 /* Firefox */ = { + E6F965171B2F1CF20034B023 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; @@ -24320,33 +26587,32 @@ SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Firefox; + name = Release; }; - E6FCC4291C40562400DF6113 /* FirefoxBeta */ = { + E6FCC4291C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E6FCC43C1C40565200DF6113 /* FirefoxBeta.xcconfig */; + baseConfigurationReference = 2CB288D02B07C6F300A8FCB3 /* EcosiaBeta.xcconfig */; buildSettings = { - EAGER_LINKING = YES; - FUSE_BUILD_SCRIPT_PHASES = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements; + DEVELOPMENT_TEAM = 33YMRSYD2L; + INFOPLIST_FILE = Client/Info.plist; OTHER_LDFLAGS = ( "$(inherited)", "-Xlinker", "-no_application_extension", ); - SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; VALIDATE_WORKSPACE = YES; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC42A1C40562400DF6113 /* FirefoxBeta */ = { + E6FCC42A1C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Beta; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; EXCLUDED_SOURCE_FILE_NAMES = "Client/Nimbus/TestData/*"; INFOPLIST_FILE = Client/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -24361,57 +26627,50 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; PRODUCT_MODULE_NAME = Client; PRODUCT_NAME = Client; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.FirefoxBeta"; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; VALIDATE_WORKSPACE = YES; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC42B1C40562400DF6113 /* FirefoxBeta */ = { + E6FCC42B1C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; - LOCALIZATION_EXPORT_SUPPORTED = NO; PRODUCT_NAME = ClientTests; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC42D1C40562400DF6113 /* FirefoxBeta */ = { + E6FCC42D1C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E152D2812B200E4DDBF /* EcosiaBeta.ShareTo.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/FirefoxBeta.entitlements"; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = 43AQ936H96; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 33YMRSYD2L; INFOPLIST_FILE = Extensions/ShareTo/Info.plist; OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_BETA -DMOZ_TARGET_SHARETO"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.FirefoxBeta.ShareTo"; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.ecosia.ecosiaapp.firefox.ShareTo"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; }; - name = FirefoxBeta; - }; - E6FCC42F1C40562400DF6113 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - VALIDATE_WORKSPACE = YES; - }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC4311C40562400DF6113 /* FirefoxBeta */ = { + E6FCC4311C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Manual; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43AQ936H96; + DEVELOPMENT_TEAM = ""; GCC_WARN_INHIBIT_ALL_WARNINGS = YES; INFOPLIST_FILE = "Shared/Supporting Files/Info.plist"; LOCALIZED_STRING_MACRO_NAMES = ( @@ -24420,32 +26679,32 @@ CFCopyLocalizedString, ); PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC4321C40562400DF6113 /* FirefoxBeta */ = { + E6FCC4321C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { DEFINES_MODULE = YES; INFOPLIST_FILE = Storage/Info.plist; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC4331C40562400DF6113 /* FirefoxBeta */ = { + E6FCC4331C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; INFOPLIST_FILE = "firefox-ios-tests/Tests/StorageTests/Info.plist"; SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Storage/Storage-Bridging-Header.h"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - E6FCC4341C40562400DF6113 /* FirefoxBeta */ = { + E6FCC4341C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; @@ -24454,395 +26713,99 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "\"$(SRCROOT)\"", - "\"$(SDKROOT)/usr/include/libxml2\"", - "\"$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)/include/**\"", - "ThirdParty/ecec/include/**", - FxA/FxA/include, - ); - INFOPLIST_FILE = Account/Info.plist; - LIBRARY_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/FxA/FxA/lib", - ); - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; - }; - name = FirefoxBeta; - }; - E6FCC4351C40562400DF6113 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - ENABLE_STRICT_OBJC_MSGSEND = YES; - INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - }; - name = FirefoxBeta; - }; - E6FCC4361C40562400DF6113 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - APPLICATION_EXTENSION_API_ONLY = YES; - CODE_SIGN_ENTITLEMENTS = ""; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Manual; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43AQ936H96; - ENABLE_TESTABILITY = YES; - GCC_WARN_INHIBIT_ALL_WARNINGS = YES; - INFOPLIST_FILE = Sync/Info.plist; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; - }; - name = FirefoxBeta; - }; - E6FCC4371C40562400DF6113 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - }; - name = FirefoxBeta; - }; - E6FCC43A1C40562400DF6113 /* FirefoxBeta */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = ""; - INFOPLIST_FILE = "firefox-ios-tests/Tests/SharedTests/Info.plist"; - SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; - }; - name = FirefoxBeta; - }; - F8324A132649A189007E4BFA /* Fennec */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_ENTITLEMENTS = CredentialProvider/CredentialProviderFennec.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 43AQ936H96; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CredentialProvider/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 0.0.1; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_CREDENTIAL_PROVIDER"; - PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.Fennec.CredentialProvider; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Fennec; - }; - F8324A142649A189007E4BFA /* Fennec_Enterprise */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_ENTITLEMENTS = CredentialProvider/CredentialProvider.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CODE_SIGN_STYLE = Manual; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 9G8J6YA743; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CredentialProvider/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 0.0.1; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_CREDENTIAL_PROVIDER"; - PRODUCT_BUNDLE_IDENTIFIER = "org.mozilla.ios.FennecEnterprise.Credential-Provider"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Fennec Enterprise Credential"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Fennec_Enterprise; - }; - F8324A152649A189007E4BFA /* Firefox */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_ENTITLEMENTS = CredentialProvider/CredentialProviderFirefox.entitlements; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = 43AQ936H96; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CredentialProvider/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( + "\"$(SRCROOT)\"", + "\"$(SDKROOT)/usr/include/libxml2\"", + "\"$(BUILD_DIR)/Release$(EFFECTIVE_PLATFORM_NAME)/include/**\"", + "ThirdParty/ecec/include/**", + FxA/FxA/include, + ); + INFOPLIST_FILE = Account/Info.plist; + LIBRARY_SEARCH_PATHS = ( "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", + "$(PROJECT_DIR)/FxA/FxA/lib", ); - MARKETING_VERSION = 0.0.1; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_RELEASE -DMOZ_TARGET_CREDENTIAL_PROVIDER"; - PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.Firefox.CredentialProvider; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.Firefox.CredentialProvider"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; }; - name = Firefox; + name = Development_TestFlight; }; - F8324A162649A189007E4BFA /* FirefoxBeta */ = { + E6FCC4351C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_ENTITLEMENTS = CredentialProvider/CredentialProviderFirefoxBeta.entitlements; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_IDENTITY = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = "firefox-ios-tests/Tests/AccountTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Account/Account-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Development_TestFlight; + }; + E6FCC4361C40562400DF6113 /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = 43AQ936H96; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - INFOPLIST_FILE = CredentialProvider/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 0.0.1; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_BETA -DMOZ_TARGET_CREDENTIAL_PROVIDER"; - PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.FirefoxBeta.CredentialProvider; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "bitrise org.mozilla.ios.FirefoxBeta.CredentialProv"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; + DEFINES_MODULE = YES; + ENABLE_TESTABILITY = YES; + GCC_WARN_INHIBIT_ALL_WARNINGS = YES; + INFOPLIST_FILE = Sync/Info.plist; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Sync/Sync-Bridging-Header.h"; }; - name = FirefoxBeta; + name = Development_TestFlight; }; - F84B21DB1A090F8100AAB793 /* Fennec */ = { + E6FCC4371C40562400DF6113 /* Development_TestFlight */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E60961861B62B8A700DD640F /* Fennec.xcconfig */; buildSettings = { - DEBUG_INFORMATION_FORMAT = dwarf; - EAGER_LINKING = YES; - FUSE_BUILD_SCRIPT_PHASES = YES; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/SyncTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/firefox-ios-tests/Tests/SyncTests/SyncTests-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Development_TestFlight; + }; + E6FCC43A1C40562400DF6113 /* Development_TestFlight */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + INFOPLIST_FILE = "firefox-ios-tests/Tests/SharedTests/Info.plist"; + SWIFT_OBJC_BRIDGING_HEADER = "$SRCROOT/Shared/Shared-Bridging-Header.h"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; + }; + name = Development_TestFlight; + }; + F84B21DB1A090F8100AAB793 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2CB288CF2B07C6F300A8FCB3 /* EcosiaDebug.xcconfig */; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/Ecosia.entitlements; + DEVELOPMENT_TEAM = 33YMRSYD2L; + INFOPLIST_FILE = Client/Info.plist; OTHER_LDFLAGS = ( "$(inherited)", "-Xlinker", "-no_application_extension", ); - SWIFT_TREAT_WARNINGS_AS_ERRORS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; VALIDATE_WORKSPACE = YES; }; - name = Fennec; + name = Debug; }; - F84B21DE1A090F8100AAB793 /* Fennec */ = { + F84B21DE1A090F8100AAB793 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon_Developer; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = 43AQ936H96; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -24855,39 +26818,42 @@ PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID)"; PRODUCT_MODULE_NAME = Client; PRODUCT_NAME = Client; - PROVISIONING_PROFILE_SPECIFIER = ""; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp"; SKIP_INSTALL = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Client/Client-Bridging-Header.h"; VALIDATE_WORKSPACE = YES; }; - name = Fennec; + name = Debug; }; - F84B21E11A090F8100AAB793 /* Fennec */ = { + F84B21E11A090F8100AAB793 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; INFOPLIST_FILE = "firefox-ios-tests/Tests/ClientTests/Info.plist"; LOCALIZATION_EXPORT_SUPPORTED = NO; PRODUCT_NAME = ClientTests; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Client.app/Client"; }; - name = Fennec; + name = Debug; }; - F84B22561A0920C600AAB793 /* Fennec */ = { + F84B22561A0920C600AAB793 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 12C11E192D2812B200E4DDBF /* EcosiaDebug.ShareTo.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "$(inherit)Extensions/Entitlements/Fennec.entitlements"; + CODE_SIGN_ENTITLEMENTS = "$(inherited)"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 43AQ936H96; + CODE_SIGN_STYLE = Manual; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; INFOPLIST_FILE = Extensions/ShareTo/Info.plist; OTHER_SWIFT_FLAGS = "-DMOZ_CHANNEL_FENNEC -DMOZ_TARGET_SHARETO"; - PRODUCT_BUNDLE_IDENTIFIER = "$(MOZ_BUNDLE_ID).$(PRODUCT_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; + PRODUCT_BUNDLE_IDENTIFIER = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = "match Development com.ecosia.ecosiaapp.ShareTo"; SKIP_INSTALL = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; }; - name = Fennec; + name = Debug; }; /* End XCBuildConfiguration section */ @@ -24895,259 +26861,337 @@ 047F9B3824E1FE1F00CD7DF7 /* Build configuration list for PBXNativeTarget "WidgetKitExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( - 4354D3E124EEEEC5001184F6 /* Fennec */, - 8A1993712C9B5A63001F4D57 /* Fennec_Testing */, - 4354D3E224EEEEC5001184F6 /* Fennec_Enterprise */, - 4354D3E324EEEEC5001184F6 /* Firefox */, - 4354D3E424EEEEC5001184F6 /* FirefoxBeta */, + 4354D3E224EEEEC5001184F6 /* BetaDebug */, + 2C2F32052D46A5CC00977F55 /* Testing */, + 4354D3E124EEEEC5001184F6 /* Debug */, + 4354D3E324EEEEC5001184F6 /* Release */, + 4354D3E424EEEEC5001184F6 /* Development_TestFlight */, + 2C02BE3D2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; 282731971ABC9BE800AA1954 /* Build configuration list for PBXNativeTarget "Sync" */ = { isa = XCConfigurationList; buildConfigurations = ( - 282731771ABC9BE800AA1954 /* Fennec */, - 8A1993812C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC20E1DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA81AEE7A6000869B6C /* Firefox */, - E6FCC4361C40562400DF6113 /* FirefoxBeta */, + E6DCC20E1DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32122D46A5CC00977F55 /* Testing */, + 282731771ABC9BE800AA1954 /* Debug */, + 2C02BE382B0E319400FE80C2 /* BetaDebug */, + E448FCA81AEE7A6000869B6C /* Release */, + E6FCC4361C40562400DF6113 /* Development_TestFlight */, + 2C02BE4D2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; 282731981ABC9BE800AA1954 /* Build configuration list for PBXNativeTarget "SyncTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2827317A1ABC9BE800AA1954 /* Fennec */, - 8A19937B2C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2151DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA91AEE7A6000869B6C /* Firefox */, - E6FCC4371C40562400DF6113 /* FirefoxBeta */, + E6DCC2151DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F320E2D46A5CC00977F55 /* Testing */, + 2827317A1ABC9BE800AA1954 /* Debug */, + 2C02BE322B0E319400FE80C2 /* BetaDebug */, + E448FCA91AEE7A6000869B6C /* Release */, + E6FCC4371C40562400DF6113 /* Development_TestFlight */, + 2C02BE472B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; 288A2D9F1AB8B3260023ABC3 /* Build configuration list for PBXNativeTarget "Shared" */ = { isa = XCConfigurationList; buildConfigurations = ( - 288A2DA01AB8B3260023ABC3 /* Fennec */, - 8A1993802C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC20B1DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA31AEE7A6000869B6C /* Firefox */, - E6FCC4311C40562400DF6113 /* FirefoxBeta */, + E6DCC20B1DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32112D46A5CC00977F55 /* Testing */, + 288A2DA01AB8B3260023ABC3 /* Debug */, + 2C02BE372B0E319400FE80C2 /* BetaDebug */, + E448FCA31AEE7A6000869B6C /* Release */, + E6FCC4311C40562400DF6113 /* Development_TestFlight */, + 2C02BE4C2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 2FA436141ABB83B4008031D1 /* Build configuration list for PBXNativeTarget "Account" */ = { + 2C6C90882C614A17007D9B43 /* Build configuration list for PBXNativeTarget "EcosiaSnapshotTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2FA436151ABB83B4008031D1 /* Fennec */, - 8A1993732C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC20D1DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA61AEE7A6000869B6C /* Firefox */, - E6FCC4341C40562400DF6113 /* FirefoxBeta */, + 2C6C908A2C614A17007D9B43 /* BetaDebug */, + 2C2F32132D46A5CC00977F55 /* Testing */, + 2C6C908D2C614A17007D9B43 /* Development_Firebase */, + 12C11FF02D2C2D5B00E4DDBF /* Debug */, + 12C11FF12D2C2D5B00E4DDBF /* Release */, + 12C11FF22D2C2D5B00E4DDBF /* Development_TestFlight */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 2FA436181ABB83B4008031D1 /* Build configuration list for PBXNativeTarget "AccountTests" */ = { + 2CFE997E2D45329500B25CE0 /* Build configuration list for PBXNativeTarget "Ecosia" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2FA436191ABB83B4008031D1 /* Fennec */, - 8A1993752C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2141DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA71AEE7A6000869B6C /* Firefox */, - E6FCC4351C40562400DF6113 /* FirefoxBeta */, + 2CFE997F2D45329500B25CE0 /* BetaDebug */, + 2C2F32142D46A5CC00977F55 /* Testing */, + 2CFE99802D45329500B25CE0 /* Debug */, + 2CFE99812D45329500B25CE0 /* Release */, + 2CFE99822D45329500B25CE0 /* Development_TestFlight */, + 2CFE99832D45329500B25CE0 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 2FCAE2331ABB51F900877008 /* Build configuration list for PBXNativeTarget "Storage" */ = { + 2CFE99842D45329500B25CE0 /* Build configuration list for PBXNativeTarget "EcosiaTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2FCAE2341ABB51F900877008 /* Fennec */, - 8A1993742C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC20C1DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA41AEE7A6000869B6C /* Firefox */, - E6FCC4321C40562400DF6113 /* FirefoxBeta */, + 2CFE99852D45329500B25CE0 /* BetaDebug */, + 2C2F32152D46A5CC00977F55 /* Testing */, + 2CFE99862D45329500B25CE0 /* Debug */, + 2CFE99872D45329500B25CE0 /* Release */, + 2CFE99882D45329500B25CE0 /* Development_TestFlight */, + 2CFE99892D45329500B25CE0 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 2FCAE2371ABB51F900877008 /* Build configuration list for PBXNativeTarget "StorageTests" */ = { + 2FA436141ABB83B4008031D1 /* Build configuration list for PBXNativeTarget "Account" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2FCAE2381ABB51F900877008 /* Fennec */, - 8A1993782C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2131DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA51AEE7A6000869B6C /* Firefox */, - E6FCC4331C40562400DF6113 /* FirefoxBeta */, + E6DCC20D1DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32062D46A5CC00977F55 /* Testing */, + 2FA436151ABB83B4008031D1 /* Debug */, + 2C02BE2A2B0E319400FE80C2 /* BetaDebug */, + E448FCA61AEE7A6000869B6C /* Release */, + E6FCC4341C40562400DF6113 /* Development_TestFlight */, + 2C02BE3F2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 397848F51ED86605004C0C0B /* Build configuration list for PBXNativeTarget "NotificationService" */ = { + 2FA436181ABB83B4008031D1 /* Build configuration list for PBXNativeTarget "AccountTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 397848E31ED86605004C0C0B /* Fennec */, - 8A19936F2C9B5A63001F4D57 /* Fennec_Testing */, - 397848E51ED86605004C0C0B /* Firefox */, - 397848E61ED86605004C0C0B /* FirefoxBeta */, - 3958DAB01ED98DCB0054AA27 /* Fennec_Enterprise */, + E6DCC2141DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32082D46A5CC00977F55 /* Testing */, + 2FA436191ABB83B4008031D1 /* Debug */, + 2C02BE2C2B0E319400FE80C2 /* BetaDebug */, + E448FCA71AEE7A6000869B6C /* Release */, + E6FCC4351C40562400DF6113 /* Development_TestFlight */, + 2C02BE412B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 3B43E3E91D95C48E00BBA9DB /* Build configuration list for PBXNativeTarget "StoragePerfTests" */ = { + 2FCAE2331ABB51F900877008 /* Build configuration list for PBXNativeTarget "Storage" */ = { isa = XCConfigurationList; buildConfigurations = ( - 3B43E3D71D95C48E00BBA9DB /* Fennec */, - 8A1993772C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC21B1DCBB6F100CEC4B7 /* Fennec_Enterprise */, - 3B43E3D81D95C48E00BBA9DB /* Firefox */, - 3B43E3D91D95C48E00BBA9DB /* FirefoxBeta */, + E6DCC20C1DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32072D46A5CC00977F55 /* Testing */, + 2FCAE2341ABB51F900877008 /* Debug */, + 2C02BE2B2B0E319400FE80C2 /* BetaDebug */, + E448FCA41AEE7A6000869B6C /* Release */, + E6FCC4321C40562400DF6113 /* Development_TestFlight */, + 2C02BE402B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 3BFE4B201D342FB900DDF53F /* Build configuration list for PBXNativeTarget "XCUITests" */ = { + 2FCAE2371ABB51F900877008 /* Build configuration list for PBXNativeTarget "StorageTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 3BFE4B0E1D342FB900DDF53F /* Fennec */, - 8A19937E2C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC21A1DCBB6F100CEC4B7 /* Fennec_Enterprise */, - 3BFE4B0F1D342FB900DDF53F /* Firefox */, - 3BFE4B101D342FB900DDF53F /* FirefoxBeta */, + E6DCC2131DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F320B2D46A5CC00977F55 /* Testing */, + 2FCAE2381ABB51F900877008 /* Debug */, + 2C02BE2F2B0E319400FE80C2 /* BetaDebug */, + E448FCA51AEE7A6000869B6C /* Release */, + E6FCC4331C40562400DF6113 /* Development_TestFlight */, + 2C02BE442B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - 43BE5785278BA4D900491291 /* Build configuration list for PBXNativeTarget "RustMozillaAppServices" */ = { + 3B43E3E91D95C48E00BBA9DB /* Build configuration list for PBXNativeTarget "StoragePerfTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 43BE5786278BA4D900491291 /* Fennec */, - 8A19937F2C9B5A63001F4D57 /* Fennec_Testing */, - 43BE5787278BA4D900491291 /* Fennec_Enterprise */, - 43BE5788278BA4D900491291 /* Firefox */, - 43BE5789278BA4D900491291 /* FirefoxBeta */, + E6DCC21B1DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F320A2D46A5CC00977F55 /* Testing */, + 3B43E3D71D95C48E00BBA9DB /* Debug */, + 2C02BE2E2B0E319400FE80C2 /* BetaDebug */, + 3B43E3D81D95C48E00BBA9DB /* Release */, + 3B43E3D91D95C48E00BBA9DB /* Development_TestFlight */, + 2C02BE432B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; - D39FA1671A83E0EC00EE869C /* Build configuration list for PBXNativeTarget "UITests" */ = { + 43BE5785278BA4D900491291 /* Build configuration list for PBXNativeTarget "RustMozillaAppServices" */ = { isa = XCConfigurationList; buildConfigurations = ( - D39FA1681A83E0EC00EE869C /* Fennec */, - 8A19937C2C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2111DCBB6F100CEC4B7 /* Fennec_Enterprise */, - D39FA1691A83E0EC00EE869C /* Release */, - E448FCA21AEE7A6000869B6C /* Firefox */, - E6FCC42F1C40562400DF6113 /* FirefoxBeta */, + 43BE5787278BA4D900491291 /* BetaDebug */, + 2C2F32102D46A5CC00977F55 /* Testing */, + 43BE5786278BA4D900491291 /* Debug */, + 2C02BE362B0E319400FE80C2 /* BetaDebug */, + 43BE5788278BA4D900491291 /* Release */, + 43BE5789278BA4D900491291 /* Development_TestFlight */, + 2C02BE4B2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; E60138631C89EAE700DF9756 /* Build configuration list for PBXNativeTarget "L10nSnapshotTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - E601384C1C89EAE600DF9756 /* Fennec */, - 8A19937D2C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2181DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E601384D1C89EAE600DF9756 /* Firefox */, - E601384E1C89EAE600DF9756 /* FirefoxBeta */, + E6DCC2181DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F320F2D46A5CC00977F55 /* Testing */, + E601384C1C89EAE600DF9756 /* Debug */, + 2C02BE342B0E319400FE80C2 /* BetaDebug */, + E601384D1C89EAE600DF9756 /* Release */, + E601384E1C89EAE600DF9756 /* Development_TestFlight */, + 2C02BE492B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; E69DB0921E97DEAA008A67E6 /* Build configuration list for PBXNativeTarget "SyncTelemetryTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - E69DB0931E97DEAA008A67E6 /* Fennec */, - 8A19937A2C9B5A63001F4D57 /* Fennec_Testing */, - E69DB0941E97DEAA008A67E6 /* Fennec_Enterprise */, - E69DB0951E97DEAA008A67E6 /* Firefox */, - E69DB0961E97DEAA008A67E6 /* FirefoxBeta */, + E69DB0941E97DEAA008A67E6 /* BetaDebug */, + 2C2F320D2D46A5CC00977F55 /* Testing */, + E69DB0931E97DEAA008A67E6 /* Debug */, + 2C02BE312B0E319400FE80C2 /* BetaDebug */, + E69DB0951E97DEAA008A67E6 /* Release */, + E69DB0961E97DEAA008A67E6 /* Development_TestFlight */, + 2C02BE462B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; E6F965381B2F1CF20034B023 /* Build configuration list for PBXNativeTarget "SharedTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - E6F965151B2F1CF20034B023 /* Fennec */, - 8A1993792C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2171DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E6F965171B2F1CF20034B023 /* Firefox */, - E6FCC43A1C40562400DF6113 /* FirefoxBeta */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; - }; - F8324A172649A189007E4BFA /* Build configuration list for PBXNativeTarget "CredentialProvider" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F8324A132649A189007E4BFA /* Fennec */, - 8A1993722C9B5A63001F4D57 /* Fennec_Testing */, - F8324A142649A189007E4BFA /* Fennec_Enterprise */, - F8324A152649A189007E4BFA /* Firefox */, - F8324A162649A189007E4BFA /* FirefoxBeta */, + E6DCC2171DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F320C2D46A5CC00977F55 /* Testing */, + E6F965151B2F1CF20034B023 /* Debug */, + 2C02BE302B0E319400FE80C2 /* BetaDebug */, + E6F965171B2F1CF20034B023 /* Release */, + E6FCC43A1C40562400DF6113 /* Development_TestFlight */, + 2C02BE452B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; F84B21B91A090F8100AAB793 /* Build configuration list for PBXProject "Client" */ = { isa = XCConfigurationList; buildConfigurations = ( - F84B21DB1A090F8100AAB793 /* Fennec */, - 8A19936D2C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2051DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FC9D1AEE7A6000869B6C /* Firefox */, - E6FCC4291C40562400DF6113 /* FirefoxBeta */, + E6DCC2051DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32022D46A5CC00977F55 /* Testing */, + F84B21DB1A090F8100AAB793 /* Debug */, + E448FC9D1AEE7A6000869B6C /* Release */, + E6FCC4291C40562400DF6113 /* Development_TestFlight */, + 2C02BE392B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; F84B21DD1A090F8100AAB793 /* Build configuration list for PBXNativeTarget "Client" */ = { isa = XCConfigurationList; buildConfigurations = ( - F84B21DE1A090F8100AAB793 /* Fennec */, - 8A19936E2C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2061DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FC9E1AEE7A6000869B6C /* Firefox */, - E6FCC42A1C40562400DF6113 /* FirefoxBeta */, + E6DCC2061DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32032D46A5CC00977F55 /* Testing */, + F84B21DE1A090F8100AAB793 /* Debug */, + 2C02BE252B0E319400FE80C2 /* BetaDebug */, + E448FC9E1AEE7A6000869B6C /* Release */, + E6FCC42A1C40562400DF6113 /* Development_TestFlight */, + 2C02BE3A2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; F84B21E01A090F8100AAB793 /* Build configuration list for PBXNativeTarget "ClientTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - F84B21E11A090F8100AAB793 /* Fennec */, - 8A1993762C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2101DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FC9F1AEE7A6000869B6C /* Firefox */, - E6FCC42B1C40562400DF6113 /* FirefoxBeta */, + E6DCC2101DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32092D46A5CC00977F55 /* Testing */, + F84B21E11A090F8100AAB793 /* Debug */, + 2C02BE2D2B0E319400FE80C2 /* BetaDebug */, + E448FC9F1AEE7A6000869B6C /* Release */, + E6FCC42B1C40562400DF6113 /* Development_TestFlight */, + 2C02BE422B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; F84B22551A0920C600AAB793 /* Build configuration list for PBXNativeTarget "ShareTo" */ = { isa = XCConfigurationList; buildConfigurations = ( - F84B22561A0920C600AAB793 /* Fennec */, - 8A1993702C9B5A63001F4D57 /* Fennec_Testing */, - E6DCC2081DCBB6F100CEC4B7 /* Fennec_Enterprise */, - E448FCA01AEE7A6000869B6C /* Firefox */, - E6FCC42D1C40562400DF6113 /* FirefoxBeta */, + E6DCC2081DCBB6F100CEC4B7 /* BetaDebug */, + 2C2F32042D46A5CC00977F55 /* Testing */, + F84B22561A0920C600AAB793 /* Debug */, + 2C02BE272B0E319400FE80C2 /* BetaDebug */, + E448FCA01AEE7A6000869B6C /* Release */, + E6FCC42D1C40562400DF6113 /* Development_TestFlight */, + 2C02BE3C2B0E31B100FE80C2 /* Development_Firebase */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Fennec; + defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 126509832CD925B30011BA36 /* XCRemoteSwiftPackageReference "braze-swift-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/braze-inc/braze-swift-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.2.0; + }; + }; + 12C25BEB2D27EBFF0048BADA /* XCRemoteSwiftPackageReference "snowplow-ios-tracker" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/snowplow/snowplow-ios-tracker"; + requirement = { + kind = upToNextMinorVersion; + minimumVersion = 6.0.9; + }; + }; + 2C8723922EA930C50085902C /* XCRemoteSwiftPackageReference "Auth0" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/auth0/Auth0.swift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.15.1; + }; + }; + 2CB172802C612D68008551E2 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.17.3; + }; + }; + 2CC0C2A72DA802CD006FE9B7 /* XCRemoteSwiftPackageReference "ViewInspector" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/nalexn/ViewInspector.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.10.1; + }; + }; + 2CCFB3D82C0FC4DC00BEDCA0 /* XCRemoteSwiftPackageReference "rust-components-swift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ecosia/rust-components-swift/"; + requirement = { + branch = 133.0.0_Glean_removed; + kind = branch; + }; + }; + 2CFE9FE82D453F9600B25CE0 /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/scinfu/SwiftSoup"; + requirement = { + kind = exactVersion; + version = 2.5.3; + }; + }; 432BD0222790EBD000A0F3C3 /* XCRemoteSwiftPackageReference "ios_sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/adjust/ios_sdk.git"; @@ -25172,14 +27216,6 @@ version = 3.8.9; }; }; - 433F87D62788F34500693368 /* XCRemoteSwiftPackageReference "rust-components-swift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; - requirement = { - kind = exactVersion; - version = 133.0.20241027050311; - }; - }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mozilla/glean-swift"; @@ -25217,7 +27253,7 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git"; requirement = { kind = exactVersion; - version = 8.21.0; + version = 8.36.0; }; }; 5A984D322C8A31A0007938C9 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { @@ -25247,10 +27283,88 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 126509842CD925B40011BA36 /* BrazeKit */ = { + isa = XCSwiftPackageProductDependency; + package = 126509832CD925B30011BA36 /* XCRemoteSwiftPackageReference "braze-swift-sdk" */; + productName = BrazeKit; + }; + 126509862CD925B40011BA36 /* BrazeUI */ = { + isa = XCSwiftPackageProductDependency; + package = 126509832CD925B30011BA36 /* XCRemoteSwiftPackageReference "braze-swift-sdk" */; + productName = BrazeUI; + }; + 12A536362D3E89BF00924CB0 /* GCDWebServers */ = { + isa = XCSwiftPackageProductDependency; + package = 433F87CC2788EAB600693368 /* XCRemoteSwiftPackageReference "GCDWebServer" */; + productName = GCDWebServers; + }; + 12C25BEC2D27F0A40048BADA /* SnowplowTracker */ = { + isa = XCSwiftPackageProductDependency; + package = 12C25BEB2D27EBFF0048BADA /* XCRemoteSwiftPackageReference "snowplow-ios-tracker" */; + productName = SnowplowTracker; + }; 216A0D752A40E7AB008077BA /* Redux */ = { isa = XCSwiftPackageProductDependency; productName = Redux; }; + 2C2EA77E2DA3F2990085F5BC /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 8AB30EC62B6C038600BD9A9B /* XCRemoteSwiftPackageReference "lottie-ios" */; + productName = Lottie; + }; + 2C69DA7A2C6225C400D7F69F /* Common */ = { + isa = XCSwiftPackageProductDependency; + productName = Common; + }; + 2C6C908E2C614A6C007D9B43 /* SnapshotTesting */ = { + isa = XCSwiftPackageProductDependency; + package = 2CB172802C612D68008551E2 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; + productName = SnapshotTesting; + }; + 2C8723932EA930C50085902C /* Auth0 */ = { + isa = XCSwiftPackageProductDependency; + package = 2C8723922EA930C50085902C /* XCRemoteSwiftPackageReference "Auth0" */; + productName = Auth0; + }; + 2CABD7152C11C9CC00A0750F /* MozillaAppServices */ = { + isa = XCSwiftPackageProductDependency; + package = 2CCFB3D82C0FC4DC00BEDCA0 /* XCRemoteSwiftPackageReference "rust-components-swift" */; + productName = MozillaAppServices; + }; + 2CC0C2AC2DA804EA006FE9B7 /* ViewInspector */ = { + isa = XCSwiftPackageProductDependency; + package = 2CC0C2A72DA802CD006FE9B7 /* XCRemoteSwiftPackageReference "ViewInspector" */; + productName = ViewInspector; + }; + 2CFE9FBE2D45348200B25CE0 /* SnowplowTracker */ = { + isa = XCSwiftPackageProductDependency; + package = 12C25BEB2D27EBFF0048BADA /* XCRemoteSwiftPackageReference "snowplow-ios-tracker" */; + productName = SnowplowTracker; + }; + 2CFE9FCB2D45363500B25CE0 /* BrazeKit */ = { + isa = XCSwiftPackageProductDependency; + package = 126509832CD925B30011BA36 /* XCRemoteSwiftPackageReference "braze-swift-sdk" */; + productName = BrazeKit; + }; + 2CFE9FCD2D45363500B25CE0 /* BrazeUI */ = { + isa = XCSwiftPackageProductDependency; + package = 126509832CD925B30011BA36 /* XCRemoteSwiftPackageReference "braze-swift-sdk" */; + productName = BrazeUI; + }; + 2CFE9FCF2D45364100B25CE0 /* SnowplowTracker */ = { + isa = XCSwiftPackageProductDependency; + package = 12C25BEB2D27EBFF0048BADA /* XCRemoteSwiftPackageReference "snowplow-ios-tracker" */; + productName = SnowplowTracker; + }; + 2CFE9FD12D45364600B25CE0 /* Common */ = { + isa = XCSwiftPackageProductDependency; + productName = Common; + }; + 2CFE9FE92D45404800B25CE0 /* SwiftSoup */ = { + isa = XCSwiftPackageProductDependency; + package = 2CFE9FE82D453F9600B25CE0 /* XCRemoteSwiftPackageReference "SwiftSoup" */; + productName = SwiftSoup; + }; 432BD0232790EBD000A0F3C3 /* Adjust */ = { isa = XCSwiftPackageProductDependency; package = 432BD0222790EBD000A0F3C3 /* XCRemoteSwiftPackageReference "ios_sdk" */; @@ -25266,31 +27380,11 @@ package = 433F87CC2788EAB600693368 /* XCRemoteSwiftPackageReference "GCDWebServer" */; productName = GCDWebServers; }; - 433F87D42788F01500693368 /* KIF */ = { - isa = XCSwiftPackageProductDependency; - package = 433F87D12788EF5B00693368 /* XCRemoteSwiftPackageReference "KIF" */; - productName = KIF; - }; 435C85EF2788F4D00072B526 /* Glean */ = { isa = XCSwiftPackageProductDependency; package = 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */; productName = Glean; }; - 43A878FB27AB498E0071C372 /* MozillaAppServices */ = { - isa = XCSwiftPackageProductDependency; - package = 433F87D62788F34500693368 /* XCRemoteSwiftPackageReference "rust-components-swift" */; - productName = MozillaAppServices; - }; - 43C6A47E27A0679300C79856 /* MappaMundi */ = { - isa = XCSwiftPackageProductDependency; - package = 43C6A47D27A0679300C79856 /* XCRemoteSwiftPackageReference "MappaMundi" */; - productName = MappaMundi; - }; - 459F39E42A031D16008C3411 /* Glean */ = { - isa = XCSwiftPackageProductDependency; - package = 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */; - productName = Glean; - }; 5A06135929D6052E008F3D38 /* TabDataStore */ = { isa = XCSwiftPackageProductDependency; productName = TabDataStore; @@ -25299,15 +27393,6 @@ isa = XCSwiftPackageProductDependency; productName = Common; }; - 5A37861829A2C337006B3A34 /* Sentry */ = { - isa = XCSwiftPackageProductDependency; - package = 5A37861729A2C337006B3A34 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; - productName = Sentry; - }; - 5A37861C29A2C37C006B3A34 /* Common */ = { - isa = XCSwiftPackageProductDependency; - productName = Common; - }; 5A393F8C2C8A410900D2628D /* SiteImageView */ = { isa = XCSwiftPackageProductDependency; productName = SiteImageView; @@ -25336,10 +27421,6 @@ isa = XCSwiftPackageProductDependency; productName = Common; }; - 5A70EF15295DFD9000790249 /* Common */ = { - isa = XCSwiftPackageProductDependency; - productName = Common; - }; 5A871487292EA1440039A5BD /* Fuzi */ = { isa = XCSwiftPackageProductDependency; package = 43AFC0E027967BFA0039DDF4 /* XCRemoteSwiftPackageReference "Fuzi" */; @@ -25374,11 +27455,6 @@ package = 4368F83B279669690013419B /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - 5A8FD0F1293A7D9000333AA7 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = 4368F83B279669690013419B /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; 5A984D302C89FD88007938C9 /* SiteImageView */ = { isa = XCSwiftPackageProductDependency; productName = SiteImageView; @@ -25410,14 +27486,6 @@ isa = XCSwiftPackageProductDependency; productName = MenuKit; }; - 8A05B0042A69A0C40011B622 /* Common */ = { - isa = XCSwiftPackageProductDependency; - productName = Common; - }; - 8A05B0062A69A25C0011B622 /* Common */ = { - isa = XCSwiftPackageProductDependency; - productName = Common; - }; 8A8881592B20FFE0009635AE /* GCDWebServers */ = { isa = XCSwiftPackageProductDependency; package = 433F87CC2788EAB600693368 /* XCRemoteSwiftPackageReference "GCDWebServer" */; @@ -25465,6 +27533,11 @@ isa = XCSwiftPackageProductDependency; productName = ToolbarKit; }; + ED6C8DAB2CE6A4BB00D7F7F3 /* Sentry-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 5A37861729A2C337006B3A34 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; + productName = "Sentry-Dynamic"; + }; EDF5679F2C8B51DC00FDB09D /* SiteImageView */ = { isa = XCSwiftPackageProductDependency; productName = SiteImageView; @@ -25474,11 +27547,6 @@ package = 5A984D322C8A31A0007938C9 /* XCRemoteSwiftPackageReference "Kingfisher" */; productName = Kingfisher; }; - F8DEACC42A3D43DA00C3B19D /* Sentry */ = { - isa = XCSwiftPackageProductDependency; - package = 5A37861729A2C337006B3A34 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; - productName = Sentry; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = F84B21B61A090F8100AAB793 /* Project object */; diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index 530b833587b73..c53f3a4ab5b03 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -5,6 +5,6 @@ BuildSystemType Latest PreviewsEnabled - + diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index bbaac616f9594..35593322ced44 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -9,6 +9,24 @@ "version" : "3.0.0-beta-1" } }, + { + "identity" : "auth0.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/Auth0.swift", + "state" : { + "revision" : "259b5d5f303d49203a6cc8415b56100d8b7584ce", + "version" : "2.15.1" + } + }, + { + "identity" : "braze-swift-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/braze-inc/braze-swift-sdk", + "state" : { + "revision" : "6c7bcbd58e36b4776f235260398fa1817244160d", + "version" : "11.9.0" + } + }, { "identity" : "dip", "kind" : "remoteSourceControl", @@ -54,6 +72,15 @@ "version" : "4.37.0" } }, + { + "identity" : "jwtdecode.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/JWTDecode.swift.git", + "state" : { + "revision" : "36a5ce735a61c4bc119593f43ce2c027b4ca7392", + "version" : "3.3.0" + } + }, { "identity" : "kif", "kind" : "remoteSourceControl", @@ -93,10 +120,19 @@ { "identity" : "rust-components-swift", "kind" : "remoteSourceControl", - "location" : "https://github.com/mozilla/rust-components-swift.git", + "location" : "https://github.com/ecosia/rust-components-swift/", "state" : { - "revision" : "e891a641517fc8c76097e33a77b3e92818fc5656", - "version" : "133.0.20241027050311" + "branch" : "133.0.0_Glean_removed", + "revision" : "e23b9086b2ce905e1b3187248c965c123b780fff" + } + }, + { + "identity" : "sdwebimage", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SDWebImage/SDWebImage.git", + "state" : { + "revision" : "10d06f6a33bafae8c164fbfd1f03391f6d4692b3", + "version" : "5.20.0" } }, { @@ -104,8 +140,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "5575af93efb776414f243e93d6af9f6258dc539a", + "version" : "8.36.0" + } + }, + { + "identity" : "simplekeychain", + "kind" : "remoteSourceControl", + "location" : "https://github.com/auth0/SimpleKeychain.git", + "state" : { + "revision" : "776c4a6db74d5c6c143974be91c383680d468630", + "version" : "1.3.0" } }, { @@ -117,13 +162,22 @@ "version" : "5.7.0" } }, + { + "identity" : "snowplow-ios-tracker", + "kind" : "remoteSourceControl", + "location" : "https://github.com/snowplow/snowplow-ios-tracker", + "state" : { + "revision" : "c105d9f8b875f2442512c3c8b1c8bbe9c209a302", + "version" : "6.0.9" + } + }, { "identity" : "swift-asn1", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-asn1.git", "state" : { - "revision" : "df5d2fcd22e3f480e3ef85bf23e277a4a0ef524d", - "version" : "1.2.0" + "revision" : "ae33e5941bb88d88538d0a6b19ca0b01e6c76dcf", + "version" : "1.3.1" } }, { @@ -140,8 +194,35 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-crypto.git", "state" : { - "revision" : "9f95b4d033a4edd3814b48608db3f2ca90c7218b", - "version" : "3.7.0" + "revision" : "f2f3774fd116a305136b6866e5e7cb7dff39d8f2", + "version" : "3.10.1" + } + }, + { + "identity" : "swift-custom-dump", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-custom-dump", + "state" : { + "revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1", + "version" : "1.3.3" + } + }, + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b", + "version" : "1.18.7" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax", + "state" : { + "revision" : "0687f71944021d616d34d922343dcef086855920", + "version" : "600.0.1" } }, { @@ -153,6 +234,15 @@ "version" : "0.17.0" } }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup", + "state" : { + "revision" : "f707b8680cddb96dc1855632340a572ef37bbb98", + "version" : "2.5.3" + } + }, { "identity" : "swiftybeaver", "kind" : "remoteSourceControl", @@ -161,6 +251,24 @@ "revision" : "1080914828ef1c9ca9cd2bad50667b3d847dabff", "version" : "2.0.0" } + }, + { + "identity" : "viewinspector", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nalexn/ViewInspector.git", + "state" : { + "revision" : "788e7879d38a839c4e348ab0762dcc0364e646a2", + "version" : "0.10.1" + } + }, + { + "identity" : "xctest-dynamic-overlay", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state" : { + "revision" : "b444594f79844b0d6d76d70fbfb3f7f71728f938", + "version" : "1.5.1" + } } ], "version" : 2 diff --git a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Fennec.xcscheme b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Ecosia.xcscheme similarity index 92% rename from firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Fennec.xcscheme rename to firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Ecosia.xcscheme index 3dda949c67210..f89d8717f0278 100644 --- a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Fennec.xcscheme +++ b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Ecosia.xcscheme @@ -136,10 +136,24 @@ ReferencedContainer = "container:FxA/FxA.xcodeproj"> + + + + - - - - - - - - - - - - - - - - @@ -517,10 +507,32 @@ ReferencedContainer = "container:../BrowserKit"> + + + + + + + + + buildConfiguration = "Debug"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/EcosiaSnapshotTests.xcscheme b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/EcosiaSnapshotTests.xcscheme new file mode 100644 index 0000000000000..4efd68f4fb8d5 --- /dev/null +++ b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/EcosiaSnapshotTests.xcscheme @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Firefox.xcscheme b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Firefox.xcscheme deleted file mode 100644 index 215c3274b5ef5..0000000000000 --- a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Firefox.xcscheme +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/FirefoxBeta.xcscheme b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/FirefoxBeta.xcscheme deleted file mode 100644 index 769489c56912e..0000000000000 --- a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/FirefoxBeta.xcscheme +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Telemetry.xcscheme b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Telemetry.xcscheme index 69b58d399a40c..0b2f99ca6d562 100644 --- a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Telemetry.xcscheme +++ b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Telemetry.xcscheme @@ -20,6 +20,20 @@ ReferencedContainer = "container:Client.xcodeproj"> + + + + + + + + + + + + + + + + @@ -53,24 +76,25 @@ ReferencedContainer = "container:Client.xcodeproj"> + + + + - - - - - - - - diff --git a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Fennec_Enterprise.xcscheme b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/WidgetKitExtension.xcscheme similarity index 51% rename from firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Fennec_Enterprise.xcscheme rename to firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/WidgetKitExtension.xcscheme index 0a529cf31607d..ce0ff1bcd2ce0 100644 --- a/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/Fennec_Enterprise.xcscheme +++ b/firefox-ios/Client.xcodeproj/xcshareddata/xcschemes/WidgetKitExtension.xcscheme @@ -1,29 +1,27 @@ + LastUpgradeVersion = "1540" + wasCreatedForAppExtension = "YES" + version = "2.0"> - - - - - - - - - - + buildImplicitDependencies = "YES" + buildArchitectures = "Automatic"> + + + + - - - - + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> + skipped = "NO" + parallelizable = "YES"> + + + + + allowLocationSimulation = "YES" + launchAutomaticallySubstyle = "2"> + + + + + debugDocumentVersioning = "YES" + askForAppToLaunch = "Yes" + launchAutomaticallySubstyle = "2"> + buildConfiguration = "Debug"> diff --git a/firefox-ios/Client/AccessoryViewProvider.swift b/firefox-ios/Client/AccessoryViewProvider.swift index f0f23a6ed3650..550d3b681dc3f 100644 --- a/firefox-ios/Client/AccessoryViewProvider.swift +++ b/firefox-ios/Client/AccessoryViewProvider.swift @@ -237,8 +237,12 @@ class AccessoryViewProvider: UIView, Themeable, InjectedThemeUUIDIdentifiable { backgroundColor = theme.colors.layer5 [previousButton, nextButton, doneButton].forEach { + /* Ecosia: update button colors $0.tintColor = theme.colors.iconAccentBlue $0.customView?.tintColor = theme.colors.iconAccentBlue + */ + $0.tintColor = theme.colors.ecosia.buttonBackgroundPrimary + $0.customView?.tintColor = theme.colors.ecosia.buttonBackgroundPrimary } [creditCardAutofillView, addressAutofillView, loginAutofillView, passwordGeneratorView].forEach { diff --git a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift index e2ccfd7a4b2b0..fb1f6f2289337 100644 --- a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift +++ b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift @@ -80,6 +80,7 @@ public struct AccessibilityIdentifiers { struct NavigationHeaderView { static let backButton = "MainMenu.BackButton" + static let title = "MainMenu.Title" static let closeButton = "MainMenu.CloseMenuButton" } diff --git a/firefox-ios/Client/Application/AppDelegate+PushNotifications.swift b/firefox-ios/Client/Application/AppDelegate+PushNotifications.swift index 1012551c80e8e..f28029f059744 100644 --- a/firefox-ios/Client/Application/AppDelegate+PushNotifications.swift +++ b/firefox-ios/Client/Application/AppDelegate+PushNotifications.swift @@ -77,6 +77,7 @@ extension AppDelegate { } } +/* Ecosia: Deactivate Firefox Push Notification Bahviour extension AppDelegate: UNUserNotificationCenterDelegate { // Called when the user taps on a notification from the background. func userNotificationCenter(_ center: UNUserNotificationCenter, @@ -164,3 +165,4 @@ extension AppDelegate { category: .setup) } } +*/ diff --git a/firefox-ios/Client/Application/AppDelegate.swift b/firefox-ios/Client/Application/AppDelegate.swift index f0645d45d105e..d00d6c1b565fa 100644 --- a/firefox-ios/Client/Application/AppDelegate.swift +++ b/firefox-ios/Client/Application/AppDelegate.swift @@ -7,8 +7,11 @@ import Storage import CoreSpotlight import UIKit import Common +/* Ecosia: Remove Glean import Glean + */ import TabDataStore +import Ecosia import class MozillaAppServices.Viaduct @@ -29,7 +32,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { creditCardAutofillEnabled: creditCardAutofillStatus ) + /* Ecosia: Swap Theme Manager with Ecosia's lazy var themeManager: ThemeManager = DefaultThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + */ + lazy var themeManager: ThemeManager = EcosiaThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) lazy var ratingPromptManager = RatingPromptManager(profile: profile) lazy var appSessionManager: AppSessionProvider = AppSessionManager() lazy var notificationSurfaceManager = NotificationSurfaceManager() @@ -43,6 +49,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private var shutdownWebServer: DispatchSourceTimer? private var webServerUtil: WebServerUtil? private var appLaunchUtil: AppLaunchUtil? + // Ecosia: Searches counter + private let searchesCounter = SearchesCounter() private var backgroundWorkUtility: BackgroundFetchAndProcessingUtility? private var widgetManager: TopSitesWidgetManager? private var menuBuilderHelper: MenuBuilderHelper? @@ -76,12 +84,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { .preLaunchDependenciesComplete, .postLaunchDependenciesComplete, .accountManagerInitialized, - .browserIsReady + .browserIsReady, + // Ecosia: Add Feature Management dependency + .featureManagementInitialized ]) // Then setup dependency container as it's needed for everything else DependencyHelper().bootstrapDependencies() + // Ecosia: Configure accounts provider factory for error simulation support + // This must happen before EcosiaAuthUIStateProvider.shared is first accessed + EcosiaAuthUIStateProvider.accountsProviderFactory = { AccountsProviderWrapper() } + appLaunchUtil = AppLaunchUtil(profile: profile) appLaunchUtil?.setUpPreLaunchDependencies() @@ -108,8 +122,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { level: .info, category: .lifecycle) - pushNotificationSetup() + // Ecosia: Allow Braze to be initialized after feature management + BrazeService.prepareForDelayedInitialization() + + // Ecosia: pushNotificationSetup() appLaunchUtil?.setUpPostLaunchDependencies() + /* Ecosia: Do not intialize Background sync backgroundWorkUtility = BackgroundFetchAndProcessingUtility() backgroundWorkUtility?.registerUtility(BackgroundSyncUtility(profile: profile, application: application)) backgroundWorkUtility?.registerUtility(BackgroundNotificationSurfaceUtility()) @@ -118,6 +136,36 @@ class AppDelegate: UIResponder, UIApplicationDelegate { firefoxSuggest: firefoxSuggest )) } + */ + // Ecosia: Update EcosiaInstallType if needed. This should always happen before `FeatureManagement`. + EcosiaInstallType.evaluateCurrentEcosiaInstallType() + // Ecosia: Disable BG sync //backgroundSyncUtil = BackgroundSyncUtil(profile: profile, application: application) + + /* + Ecosia: Feature Management fetch + We perform the same configuration retrieval in + `applicationDidBecomeActive(:)` and sounds redundant; + However we need it here to make sure we retrieve the latest + flag state of the EngagementService. + Decouple the "loading" only from the filesystem of any + previously saved Model from the `Unleash.start(:)` will not + make any tangible difference in the process as we check if + any cached version of the Model is in place. + */ + Task { + await FeatureManagement.fetchConfiguration() + // Signal that feature management initialization is complete on main thread + AppEventQueue.signal(event: .featureManagementInitialized) + // Ecosia: Braze Service Initialization after feature flags are fetched for conditional initialization + BrazeService.shared.initialize() + // Ecosia: Lifecycle tracking. Needs to happen after Unleash start so that the flags are correctly added to the analytics context. + Analytics.shared.activity(.launch) + } + + // Ecosia: fetching statistics before they are used + Task.detached { + try? await Statistics.shared.fetchAndUpdate() + } let topSitesProvider = TopSitesProviderImplementation( placesFetcher: profile.places, @@ -129,6 +177,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { addObservers() + // Ecosia: Send the install event. It happens only once per App install. + Analytics.shared.install() + logger.log("didFinishLaunchingWithOptions end", level: .info, category: .lifecycle) @@ -167,6 +218,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { self?.ratingPromptManager.updateData() } + // Ecosia + Task { + await FeatureManagement.fetchConfiguration() + Analytics.shared.activity(.resume) + } + MMP.sendSession() + searchesCounter.subscribe(self) { searchCount in + MMP.handleSearchEvent(searchCount) + } + DispatchQueue.global().async { [weak self] in self?.profile.pollCommands(forcePoll: false) } @@ -313,3 +374,17 @@ extension AppDelegate { return configuration } } + +// Ecosia: Register the APN device token and handle background notifications +extension AppDelegate { + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + BrazeService.shared.registerDeviceToken(deviceToken) + } + + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + if BrazeService.shared.handleBackgroundNotification(userInfo: userInfo, completionHandler: completionHandler) { + return + } + completionHandler(.noData) + } +} diff --git a/firefox-ios/Client/Application/AppLaunchUtil.swift b/firefox-ios/Client/Application/AppLaunchUtil.swift index 0455c68e91be9..8bc6bde303f3f 100644 --- a/firefox-ios/Client/Application/AppLaunchUtil.swift +++ b/firefox-ios/Client/Application/AppLaunchUtil.swift @@ -7,7 +7,9 @@ import Foundation import Shared import Storage import Account +/* Ecosia: Remove Glean import Glean + */ class AppLaunchUtil { private var logger: Logger @@ -44,11 +46,12 @@ class AppLaunchUtil { setMenuItems() + /* Ecosia: Do not intialize Firefox-specific SkAdNetwork and legacy feature flag manager // Initialize conversion value by specifying fineValue and coarseValue. // Call update postback conversion value for install event. let conversionValue = ConversionValueUtil(fineValue: 0, coarseValue: .low, logger: logger) conversionValue.adNetworkAttributionUpdateConversionEvent() - + */ // Initialize the feature flag subsystem. // Among other things, it toggles on and off Nimbus, Contile, Adjust. // i.e. this must be run before initializing those systems. @@ -56,7 +59,8 @@ class AppLaunchUtil { // Start initializing the Nimbus SDK. This should be done after Glean // has been started. - initializeExperiments() + // Ecosia: Do not intialize Nimbus + // initializeExperiments() // We migrate history from browser db to places if it hasn't already DispatchQueue.global().async { @@ -78,10 +82,15 @@ class AppLaunchUtil { } } + /* Ecosia: Hide RustFirefoxAccounts + Keep `accountManagerInitialized` signaling right after this commented code👇 active to satifsy tasks dependencies RustFirefoxAccounts.startup(prefs: profile.prefs) { _ in self.logger.log("RustFirefoxAccounts started", level: .info, category: .sync) + AppEventQueue.signal(event: .accountManagerInitialized) } + */ + AppEventQueue.signal(event: .accountManagerInitialized) // Add swizzle on UIViewControllers to automatically log when there's a new view showing UIViewController.loggerSwizzle() @@ -100,6 +109,8 @@ class AppLaunchUtil { } func setUpPostLaunchDependencies() { + DefaultBrowserUtil().processUserDefaultState(isFirstRun: introScreenManager.shouldShowIntroScreen) + let persistedCurrentVersion = InstallType.persistedCurrentVersion() // upgrade install - Intro screen shown & persisted current version does not match if !introScreenManager.shouldShowIntroScreen && persistedCurrentVersion != AppInfo.appVersion { @@ -161,25 +172,32 @@ class AppLaunchUtil { logger.log("Migrating Application services history", level: .info, category: .sync) + + /* Ecosia: remove Glean dependency let id = GleanMetrics.PlacesHistoryMigration.duration.start() // We mark that the migration started // this will help us identify how often the migration starts, but never ends // additionally, we have a separate metric for error rates GleanMetrics.PlacesHistoryMigration.migrationEndedRate.addToNumerator(1) GleanMetrics.PlacesHistoryMigration.migrationErrorRate.addToNumerator(1) + */ browserProfile?.migrateHistoryToPlaces( callback: { result in self.logger.log("Successful Migration took \(result.totalDuration / 1000) seconds", level: .info, category: .sync) + /* Ecosia: remove Glean dependency // We record various success metrics here GleanMetrics.PlacesHistoryMigration.duration.stopAndAccumulate(id) GleanMetrics.PlacesHistoryMigration.numMigrated.set(Int64(result.numSucceeded)) + */ self.logger.log("Migrated \(result.numSucceeded) entries", level: .info, category: .sync) + /* Ecosia: remove Glean dependency GleanMetrics.PlacesHistoryMigration.numToMigrate.set(Int64(result.numTotal)) GleanMetrics.PlacesHistoryMigration.migrationEndedRate.addToDenominator(1) + */ UserDefaults.standard.setValue(true, forKey: PrefsKeys.PlacesHistoryMigrationSucceeded) NotificationCenter.default.post(name: .TopSitesUpdated, object: nil) }, @@ -188,10 +206,11 @@ class AppLaunchUtil { self.logger.log("Migration failed with \(errDescription)", level: .warning, category: .sync) - + /* Ecosia: remove Glean dependency GleanMetrics.PlacesHistoryMigration.duration.cancel(id) GleanMetrics.PlacesHistoryMigration.migrationEndedRate.addToDenominator(1) GleanMetrics.PlacesHistoryMigration.migrationErrorRate.addToDenominator(1) + */ }) } else { self.logger.log("History Migration skipped", diff --git a/firefox-ios/Client/Application/DefaultBrowserUtil.swift b/firefox-ios/Client/Application/DefaultBrowserUtil.swift new file mode 100644 index 0000000000000..26c9666b77a84 --- /dev/null +++ b/firefox-ios/Client/Application/DefaultBrowserUtil.swift @@ -0,0 +1,86 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Common + +struct DefaultBrowserUtil { + let userDefault: UserDefaultsInterface + let telemtryWrapper: TelemetryWrapperProtocol + let locale: LocaleInterface + let application: UIApplicationInterface + let dmaCountries = ["BE", "BG", "CZ", "DK", "DE", "EE", "IE", "EL", "ES", "FR", "HR", "IT", "CY", "LV", + "LT", "LU", "HU", "MT", "NL", "AT", "PL", "PT", "RO", "SI", "SK", "FI", "SE", "GR"] + private let logger: Logger + init(userDefault: UserDefaultsInterface = UserDefaults.standard, + telemetryWrapper: TelemetryWrapperProtocol = TelemetryWrapper.shared, + locale: LocaleInterface = Locale.current, + application: UIApplicationInterface = UIApplication.shared, + logger: Logger = DefaultLogger.shared) { + self.userDefault = userDefault + self.telemtryWrapper = telemetryWrapper + self.locale = locale + self.application = application + self.logger = logger + } + + func processUserDefaultState(isFirstRun: Bool) { + guard #available(iOS 18.2, *) else { return } + + guard !isRunningOnBlockListBetaOS() else { + logger.log("Cannot run the isDefault since the device running a Beta on the block list", + level: .info, + category: .setup) + return + } + + logger.log("Going to try UIApplicationInterface.isDefault", level: .info, category: .setup) + guard let isDefault = try? application.isDefault(.webBrowser) else { + logger.log("UIApplicationInterface.isDefault was not present", level: .info, category: .setup) + return + } + + logger.log("UIApplicationInterface.isDefault was successful", level: .info, category: .setup) + trackIfUserIsDefault(isDefault) + + if isFirstRun { + trackIfNewUserIsComingFromBrowserChoiceScreen(isDefault) + + if isDefault { + // If the user is set to default don't ask on the home page later + // This is temporary until we can refactor set to default flows now that we have the ability to check + userDefault.set(true, forKey: PrefsKeys.DidDismissDefaultBrowserMessage) + } + } + } + + private func isRunningOnBlockListBetaOS() -> Bool { + let systemVersion = ProcessInfo.processInfo.operatingSystemVersionString + let betaBlockLists: [String] = ["22C5109p", "22C5125e", "22C5131e", "22C5142a"] + + return betaBlockLists.contains { systemVersion.contains($0) } + } + + private func trackIfUserIsDefault(_ isDefault: Bool) { + userDefault.set(isDefault, forKey: PrefsKeys.AppleConfirmedUserIsDefaultBrowser) + + telemtryWrapper.recordEvent(category: .action, + method: .open, + object: .defaultBrowser, + extras: [TelemetryWrapper.EventExtraKey.isDefaultBrowser.rawValue: isDefault]) + } + + private func trackIfNewUserIsComingFromBrowserChoiceScreen(_ isDefault: Bool) { + guard let regionCode = locale.localeRegionCode else { return } + // User is in a DMA effective region + if dmaCountries.contains(regionCode) { + let key = TelemetryWrapper.EventExtraKey.didComeFromBrowserChoiceScreen.rawValue + telemtryWrapper.recordEvent(category: .action, + method: .open, + object: .choiceScreenAcquisition, + extras: [key: isDefault]) + } + } +} diff --git a/firefox-ios/Client/Application/ImageIdentifiers.swift b/firefox-ios/Client/Application/ImageIdentifiers.swift index 533d35ac554a8..e1eaf649c6a9a 100644 --- a/firefox-ios/Client/Application/ImageIdentifiers.swift +++ b/firefox-ios/Client/Application/ImageIdentifiers.swift @@ -10,10 +10,16 @@ import Foundation /// Sing the song if you must. public struct ImageIdentifiers { public static let badgeMask = "badge-mask" + /* Ecosia: Update Firefox brand logos public static let firefoxFavicon = "faviconFox" public static let foxConfirmation = "foxConfirmation" public static let homeHeaderLogoBall = "fxHomeHeaderLogoBall" public static let homeHeaderLogoText = "fxHomeHeaderLogoText" + */ + public static let firefoxFavicon = "ecosiaHomeHeaderLogoBall" + public static let foxConfirmation = "foxConfirmation" + public static let homeHeaderLogoBall = "ecosiaHomeHeaderLogoBall" + public static let homeHeaderLogoText = "ecosiaHomeHeaderLogoText" public static let homepagePocket = "homepage-pocket" public static let logo = "splash" public static let logoAmex = "logo_amex" diff --git a/firefox-ios/Client/Assets/About/Licenses.html b/firefox-ios/Client/Assets/About/Licenses.html index 748334eca0951..3d7817f3ab18c 100644 --- a/firefox-ios/Client/Assets/About/Licenses.html +++ b/firefox-ios/Client/Assets/About/Licenses.html @@ -61,8 +61,8 @@

Licenses

-

Firefox for iOS

- +

Ecosia for iOS

+
@@ -530,7 +530,123 @@

Exhibit B - “

-

+
+ + +
+ +
+

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/

+ +

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+ +

1. Definitions.

+ +

“License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

+ +

“Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

+ +

“Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

+ +

“You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License.

+ +

“Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

+ +

“Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

+ +

“Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

+ +

“Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

+ +

“Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.”

+ +

“Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

+ +

2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

+ +

3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

+ +

4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

+ +

(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and

+ +

(b) You must cause any modified files to carry prominent notices stating that You changed the files; and

+ +

(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and

+ +

(d) If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

+ +

You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

+ +

5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

+ +

6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

+ +

7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

+ +

8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

+ +

9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

+

END OF TERMS AND CONDITIONS

+ +

APPENDIX: How to apply the Apache License to your work.

+ +

To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets “{}” replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same “printed page” as the copyright notice for easier identification within third-party archives.

+ +

Copyright {yyyy} {name of copyright owner}

+ +

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 you are using FMDB in your project, I'd love to hear about it. Let Gus know + by sending an email to gus@flyingmeat.com.

+ +

And if you happen to come across either Gus Mueller or Rob Ryan in a bar, you + might consider purchasing a drink of their choosing if FMDB has been useful to + you.

+ +

Finally, and shortly, this is the MIT License.

+ +

Copyright (c) 2008-2014 Flying Meat Inc.

+ +

Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions:

+ +

The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software.

+ +

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE.

+
+
+
+

-

+ + + diff --git a/firefox-ios/Client/Assets/EcosiaNetError.css b/firefox-ios/Client/Assets/EcosiaNetError.css new file mode 100644 index 0000000000000..86970bc74f337 --- /dev/null +++ b/firefox-ios/Client/Assets/EcosiaNetError.css @@ -0,0 +1,93 @@ +/* +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0 +*/ + +/* Light mode */ +@media (prefers-color-scheme: light) { +:root { + --body-bg: #FFFFFF; + --body-color: #333333; + --error-title-text-color: #333333; + --error-short-desc-color: #6C6C6C; + --refresh-button-color: #333333; +} +} + +/* Dark mode */ +@media (prefers-color-scheme: dark) { +:root { + --body-bg: #1A1A1A; + --body-color: #FFFFFF; + --error-title-text-color: #FFFFFF; + --error-short-desc-color: #DEDED9; + --refresh-button-color: #FFFFFF; +} +} + +html, +body { + margin: 0; + padding: 0; + height: 100%; +} + +body { + background-color: var(--body-bg); + padding: 0 65px; + -webkit-text-size-adjust: none; + font-size: 17px; + font: -apple-system-body; + text-align: center; +} + +.error-title { + color: var(--error-title-text-color); + font-weight: 600; +} + +.error-description { + color: var(--error-short-desc-color); + font-weight: 400; +} + +img { + display: block; + margin-left: auto; + margin-right: auto; + width: 50%; +} + +button { + /* Force buttons to display: block here to try and enforce collapsing margins */ + display: block; + width: 100%; + border: none; + padding: 1rem; + font: -apple-system-body; + background-color: transparent; + font-weight: 300; + border-radius: 5px; + background-image: none; + margin: 10px 0 0; + position: -webkit-sticky; + bottom: 5px; + font-weight: 400; + font-size: 17px; + color: var(--refresh-button-color); +} + +.error-container { + -webkit-transform: translateY(127px); + padding-bottom: 10px; + min-height: calc(100% - 127px - 10px); + display: flex; + flex-direction: column; + align-items: center; +} + +.error-description-container { + /* Margins between the li and buttons below it won't be collapsed. Remove the bottom margin here. */ + margin: 20px 0 0; +} diff --git a/firefox-ios/Client/Assets/EcosiaNetError.html b/firefox-ios/Client/Assets/EcosiaNetError.html new file mode 100644 index 0000000000000..f9dd8163e5b2b --- /dev/null +++ b/firefox-ios/Client/Assets/EcosiaNetError.html @@ -0,0 +1,43 @@ + + + + + + %error_title% + + + + + + +
+
+ + +
+
+

%error_title%

+
+
+
+

%short_description%

+
+ +
%actions%
+
+
+ + + + diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024 1.png new file mode 100644 index 0000000000000..edb85320d03fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024 2.png new file mode 100644 index 0000000000000..edb85320d03fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000000000..4f868324aef19 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/1024.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114 1.png new file mode 100644 index 0000000000000..14d1fb5fea7c4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114 2.png new file mode 100644 index 0000000000000..14d1fb5fea7c4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114.png new file mode 100644 index 0000000000000..10b2b21e477ad Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/114.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 1.png new file mode 100644 index 0000000000000..612fea396fac1 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 2.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 3.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 3.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 3.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 4.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 4.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 4.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 5.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 5.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120 5.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120.png new file mode 100644 index 0000000000000..612fea396fac1 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/120.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128 1.png new file mode 100644 index 0000000000000..d67452488a7a8 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128 2.png new file mode 100644 index 0000000000000..d67452488a7a8 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 0000000000000..c01ea76322f00 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/128.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136 1.png new file mode 100644 index 0000000000000..706a284c1367c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136 2.png new file mode 100644 index 0000000000000..80ded7922cc10 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136.png new file mode 100644 index 0000000000000..706a284c1367c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/136.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152 1.png new file mode 100644 index 0000000000000..1f67a81d34b90 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152 2.png new file mode 100644 index 0000000000000..1f67a81d34b90 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000000000..d97cc3265a95b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/152.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167 1.png new file mode 100644 index 0000000000000..fa12b27cdf80b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167 2.png new file mode 100644 index 0000000000000..fa12b27cdf80b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000000000..786bc189e92f3 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/167.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180 1.png new file mode 100644 index 0000000000000..b838a947033d9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180 2.png new file mode 100644 index 0000000000000..b838a947033d9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000000000..1462d82a4dd52 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/180.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192 1.png new file mode 100644 index 0000000000000..650d32511a092 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192 2.png new file mode 100644 index 0000000000000..63134526535fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192.png new file mode 100644 index 0000000000000..650d32511a092 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/192.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40 1.png new file mode 100644 index 0000000000000..6100bcff9e062 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40 2.png new file mode 100644 index 0000000000000..6100bcff9e062 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000000000..a1d9261ab727a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/40.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58 1.png new file mode 100644 index 0000000000000..754f87a4a619a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58 2.png new file mode 100644 index 0000000000000..754f87a4a619a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000000000..cc7bbdcac713f Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/58.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60 1.png new file mode 100644 index 0000000000000..aebe27d632bb4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60 2.png new file mode 100644 index 0000000000000..aebe27d632bb4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000000000..4f9f353eccd7c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/60.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76 1.png new file mode 100644 index 0000000000000..5c73f80430cf9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76 2.png new file mode 100644 index 0000000000000..5c73f80430cf9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000000000..52223c6a7560e Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/76.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80 1.png new file mode 100644 index 0000000000000..dc714f3c306d5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80 2.png new file mode 100644 index 0000000000000..dc714f3c306d5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000000000..87de7b2cdd318 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/80.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87 1.png new file mode 100644 index 0000000000000..31f72fee6e99b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87 2.png new file mode 100644 index 0000000000000..31f72fee6e99b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000000000..3a5924eba238a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/87.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/Contents.json index 465cd15ddd6dc..f921c4f5d2342 100644 --- a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,152 +1,533 @@ { "images" : [ { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "ios-20@2x.png", - "scale" : "2x" + "filename" : "40 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" }, { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "ios-20@3x.png", - "scale" : "3x" + "filename" : "60 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29.png", - "scale" : "1x" + "filename" : "58 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29@2x.png", - "scale" : "2x" + "filename" : "87 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29@3x.png", - "scale" : "3x" + "filename" : "76 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "ios-40@2x.png", - "scale" : "2x" + "filename" : "114 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "ios-40@3x.png", - "scale" : "3x" + "filename" : "80 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" }, { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "1x" + "filename" : "120 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" }, { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "2x" + "filename" : "120 4.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "ios-60@2x.png", - "scale" : "2x" + "filename" : "180 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "ios-60@3x.png", - "scale" : "3x" + "filename" : "128 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "ios-20.png", - "scale" : "1x" + "filename" : "192.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "ios-20@2x-1.png", - "scale" : "2x" + "filename" : "136.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "ios-30.png", - "scale" : "1x" + "filename" : "152 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "ios-29@2x-1.png", - "scale" : "2x" + "filename" : "167 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "ios-40.png", - "scale" : "1x" + "filename" : "1024 1.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "ios-40@2x-1.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "40.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" }, { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "60.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" }, { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "58.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" }, { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "87.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" }, { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "76.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "ios-ipad-76.png", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "114.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "ios-ipad-76@2x.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "80.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" }, { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "ios-ipad-pro-83.5@2x.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "120.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" }, { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "icon-apple-app-store.png", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "120 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "180.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "128.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "192 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "136 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "152.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "167.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "40 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "60 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "58 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "87 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "76 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "114 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "80 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "120 3.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "120 5.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "180 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "128 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "192 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "136 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "152 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "167 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "1024 2.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/icon-apple-app-store.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/icon-apple-app-store.png deleted file mode 100644 index c2a1e1c515154..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/icon-apple-app-store.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20.png deleted file mode 100644 index 039df963525ea..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@2x-1.png deleted file mode 100644 index 4f51af9063a26..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@2x.png deleted file mode 100644 index 4f51af9063a26..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@3x.png deleted file mode 100644 index b164e93204d82..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-20@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29.png deleted file mode 100644 index 2d5879744e858..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@2x-1.png deleted file mode 100644 index 197b1dec90ebc..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@2x.png deleted file mode 100644 index 197b1dec90ebc..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@3x.png deleted file mode 100644 index d9676da1b7e3e..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-29@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-30.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-30.png deleted file mode 100644 index 2d5879744e858..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-30.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40.png deleted file mode 100644 index 2a98de5e87560..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@2x-1.png deleted file mode 100644 index 83f9c03bde126..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@2x.png deleted file mode 100644 index 83f9c03bde126..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@3x.png deleted file mode 100644 index 7bde3d5b4a760..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-40@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-60@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-60@2x.png deleted file mode 100644 index 9eeebf5bac0af..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-60@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-60@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-60@3x.png deleted file mode 100644 index f1934d853f207..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-60@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-76.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-76.png deleted file mode 100644 index fabe6b0820d79..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-76.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-76@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-76@2x.png deleted file mode 100644 index 762922b1bc123..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-76@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-pro-83.5@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-pro-83.5@2x.png deleted file mode 100644 index 90b7f26241b67..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon.appiconset/ios-ipad-pro-83.5@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024 1.png new file mode 100644 index 0000000000000..edb85320d03fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024 2.png new file mode 100644 index 0000000000000..edb85320d03fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024.png new file mode 100644 index 0000000000000..4f868324aef19 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/1024.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114 1.png new file mode 100644 index 0000000000000..14d1fb5fea7c4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114 2.png new file mode 100644 index 0000000000000..14d1fb5fea7c4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114.png new file mode 100644 index 0000000000000..10b2b21e477ad Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/114.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 1.png new file mode 100644 index 0000000000000..612fea396fac1 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 2.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 3.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 3.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 3.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 4.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 4.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 4.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 5.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 5.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120 5.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120.png new file mode 100644 index 0000000000000..612fea396fac1 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/120.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128 1.png new file mode 100644 index 0000000000000..d67452488a7a8 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128 2.png new file mode 100644 index 0000000000000..d67452488a7a8 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128.png new file mode 100644 index 0000000000000..c01ea76322f00 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/128.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136 1.png new file mode 100644 index 0000000000000..706a284c1367c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136 2.png new file mode 100644 index 0000000000000..80ded7922cc10 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136.png new file mode 100644 index 0000000000000..706a284c1367c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/136.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152 1.png new file mode 100644 index 0000000000000..1f67a81d34b90 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152 2.png new file mode 100644 index 0000000000000..1f67a81d34b90 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152.png new file mode 100644 index 0000000000000..d97cc3265a95b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/152.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167 1.png new file mode 100644 index 0000000000000..fa12b27cdf80b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167 2.png new file mode 100644 index 0000000000000..fa12b27cdf80b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167.png new file mode 100644 index 0000000000000..786bc189e92f3 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/167.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180 1.png new file mode 100644 index 0000000000000..b838a947033d9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180 2.png new file mode 100644 index 0000000000000..b838a947033d9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180.png new file mode 100644 index 0000000000000..1462d82a4dd52 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/180.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192 1.png new file mode 100644 index 0000000000000..650d32511a092 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192 2.png new file mode 100644 index 0000000000000..63134526535fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192.png new file mode 100644 index 0000000000000..650d32511a092 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/192.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40 1.png new file mode 100644 index 0000000000000..6100bcff9e062 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40 2.png new file mode 100644 index 0000000000000..6100bcff9e062 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40.png new file mode 100644 index 0000000000000..a1d9261ab727a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/40.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58 1.png new file mode 100644 index 0000000000000..754f87a4a619a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58 2.png new file mode 100644 index 0000000000000..754f87a4a619a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58.png new file mode 100644 index 0000000000000..cc7bbdcac713f Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/58.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60 1.png new file mode 100644 index 0000000000000..aebe27d632bb4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60 2.png new file mode 100644 index 0000000000000..aebe27d632bb4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60.png new file mode 100644 index 0000000000000..4f9f353eccd7c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/60.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76 1.png new file mode 100644 index 0000000000000..5c73f80430cf9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76 2.png new file mode 100644 index 0000000000000..5c73f80430cf9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76.png new file mode 100644 index 0000000000000..52223c6a7560e Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/76.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80 1.png new file mode 100644 index 0000000000000..dc714f3c306d5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80 2.png new file mode 100644 index 0000000000000..dc714f3c306d5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80.png new file mode 100644 index 0000000000000..87de7b2cdd318 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/80.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87 1.png new file mode 100644 index 0000000000000..31f72fee6e99b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87 2.png new file mode 100644 index 0000000000000..31f72fee6e99b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87.png new file mode 100644 index 0000000000000..3a5924eba238a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/87.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/Contents.json index 465cd15ddd6dc..f921c4f5d2342 100644 --- a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/Contents.json +++ b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/Contents.json @@ -1,152 +1,533 @@ { "images" : [ { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "ios-20@2x.png", - "scale" : "2x" + "filename" : "40 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" }, { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "ios-20@3x.png", - "scale" : "3x" + "filename" : "60 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29.png", - "scale" : "1x" + "filename" : "58 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29@2x.png", - "scale" : "2x" + "filename" : "87 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29@3x.png", - "scale" : "3x" + "filename" : "76 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "ios-40@2x.png", - "scale" : "2x" + "filename" : "114 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "ios-40@3x.png", - "scale" : "3x" + "filename" : "80 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" }, { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "1x" + "filename" : "120 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" }, { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "2x" + "filename" : "120 4.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "ios-60@2x.png", - "scale" : "2x" + "filename" : "180 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "ios-60@3x.png", - "scale" : "3x" + "filename" : "128 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "ios-20.png", - "scale" : "1x" + "filename" : "192.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "ios-20@2x-1.png", - "scale" : "2x" + "filename" : "136.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "ios-30.png", - "scale" : "1x" + "filename" : "152 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "ios-29@2x-1.png", - "scale" : "2x" + "filename" : "167 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "ios-40.png", - "scale" : "1x" + "filename" : "1024 1.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "ios-40@2x-1.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "40.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" }, { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "60.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" }, { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "58.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" }, { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "87.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" }, { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "76.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "ios-ipad-76.png", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "114.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "ios-ipad-76@2x.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "80.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" }, { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "ios-ipad-pro-83.5@2x.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "120.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" }, { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "icon-apple-app-store.png", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "120 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "180.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "128.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "192 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "136 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "152.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "167.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "40 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "60 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "58 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "87 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "76 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "114 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "80 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "120 3.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "120 5.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "180 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "128 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "192 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "136 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "152 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "167 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "1024 2.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/icon-apple-app-store.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/icon-apple-app-store.png deleted file mode 100644 index 1ec8b2373d8eb..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/icon-apple-app-store.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20.png deleted file mode 100644 index a8c86c7596760..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@2x-1.png deleted file mode 100644 index e7ea70b6a68a9..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@2x.png deleted file mode 100644 index e7ea70b6a68a9..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@3x.png deleted file mode 100644 index 2dd34ee8239cb..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-20@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29.png deleted file mode 100644 index 7405bc23fff4c..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@2x-1.png deleted file mode 100644 index 3db103fcf35c7..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@2x.png deleted file mode 100644 index 3db103fcf35c7..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@3x.png deleted file mode 100644 index 7dfe66e06da25..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-29@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-30.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-30.png deleted file mode 100644 index 7405bc23fff4c..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-30.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40.png deleted file mode 100644 index 56075e1911ae2..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@2x-1.png deleted file mode 100644 index d20f713b407d1..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@2x.png deleted file mode 100644 index d20f713b407d1..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@3x.png deleted file mode 100644 index fecf2a2ff28a3..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-40@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-60@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-60@2x.png deleted file mode 100644 index b9849a7af284e..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-60@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-60@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-60@3x.png deleted file mode 100644 index 67146a4ddb803..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-60@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-76.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-76.png deleted file mode 100644 index 183ca518db6ea..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-76.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-76@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-76@2x.png deleted file mode 100644 index da93b1df0c0e9..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-76@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-pro-83.5@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-pro-83.5@2x.png deleted file mode 100644 index 29ce3c74f8bf7..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Beta.appiconset/ios-ipad-pro-83.5@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024 1.png new file mode 100644 index 0000000000000..edb85320d03fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024 2.png new file mode 100644 index 0000000000000..edb85320d03fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024.png new file mode 100644 index 0000000000000..4f868324aef19 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/1024.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114 1.png new file mode 100644 index 0000000000000..14d1fb5fea7c4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114 2.png new file mode 100644 index 0000000000000..14d1fb5fea7c4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114.png new file mode 100644 index 0000000000000..10b2b21e477ad Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/114.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 1.png new file mode 100644 index 0000000000000..612fea396fac1 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 2.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 3.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 3.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 3.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 4.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 4.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 4.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 5.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 5.png new file mode 100644 index 0000000000000..91788e431e6f5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120 5.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120.png new file mode 100644 index 0000000000000..612fea396fac1 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/120.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128 1.png new file mode 100644 index 0000000000000..d67452488a7a8 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128 2.png new file mode 100644 index 0000000000000..d67452488a7a8 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128.png new file mode 100644 index 0000000000000..c01ea76322f00 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/128.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136 1.png new file mode 100644 index 0000000000000..706a284c1367c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136 2.png new file mode 100644 index 0000000000000..80ded7922cc10 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136.png new file mode 100644 index 0000000000000..706a284c1367c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/136.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152 1.png new file mode 100644 index 0000000000000..1f67a81d34b90 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152 2.png new file mode 100644 index 0000000000000..1f67a81d34b90 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152.png new file mode 100644 index 0000000000000..d97cc3265a95b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/152.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167 1.png new file mode 100644 index 0000000000000..fa12b27cdf80b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167 2.png new file mode 100644 index 0000000000000..fa12b27cdf80b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167.png new file mode 100644 index 0000000000000..786bc189e92f3 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/167.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180 1.png new file mode 100644 index 0000000000000..b838a947033d9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180 2.png new file mode 100644 index 0000000000000..b838a947033d9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180.png new file mode 100644 index 0000000000000..1462d82a4dd52 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/180.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192 1.png new file mode 100644 index 0000000000000..650d32511a092 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192 2.png new file mode 100644 index 0000000000000..63134526535fe Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192.png new file mode 100644 index 0000000000000..650d32511a092 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/192.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40 1.png new file mode 100644 index 0000000000000..6100bcff9e062 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40 2.png new file mode 100644 index 0000000000000..6100bcff9e062 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40.png new file mode 100644 index 0000000000000..a1d9261ab727a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/40.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58 1.png new file mode 100644 index 0000000000000..754f87a4a619a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58 2.png new file mode 100644 index 0000000000000..754f87a4a619a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58.png new file mode 100644 index 0000000000000..cc7bbdcac713f Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/58.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60 1.png new file mode 100644 index 0000000000000..aebe27d632bb4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60 2.png new file mode 100644 index 0000000000000..aebe27d632bb4 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60.png new file mode 100644 index 0000000000000..4f9f353eccd7c Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/60.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76 1.png new file mode 100644 index 0000000000000..5c73f80430cf9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76 2.png new file mode 100644 index 0000000000000..5c73f80430cf9 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76.png new file mode 100644 index 0000000000000..52223c6a7560e Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/76.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80 1.png new file mode 100644 index 0000000000000..dc714f3c306d5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80 2.png new file mode 100644 index 0000000000000..dc714f3c306d5 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80.png new file mode 100644 index 0000000000000..87de7b2cdd318 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/80.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87 1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87 1.png new file mode 100644 index 0000000000000..31f72fee6e99b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87 1.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87 2.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87 2.png new file mode 100644 index 0000000000000..31f72fee6e99b Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87 2.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87.png new file mode 100644 index 0000000000000..3a5924eba238a Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/87.png differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/Contents.json index 465cd15ddd6dc..f921c4f5d2342 100644 --- a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/Contents.json +++ b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/Contents.json @@ -1,152 +1,533 @@ { "images" : [ { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "ios-20@2x.png", - "scale" : "2x" + "filename" : "40 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" }, { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "ios-20@3x.png", - "scale" : "3x" + "filename" : "60 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29.png", - "scale" : "1x" + "filename" : "58 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29@2x.png", - "scale" : "2x" + "filename" : "87 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" }, { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "ios-29@3x.png", - "scale" : "3x" + "filename" : "76 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "ios-40@2x.png", - "scale" : "2x" + "filename" : "114 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" }, { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "ios-40@3x.png", - "scale" : "3x" + "filename" : "80 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" }, { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "1x" + "filename" : "120 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" }, { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "2x" + "filename" : "120 4.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "ios-60@2x.png", - "scale" : "2x" + "filename" : "180 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" }, { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "ios-60@3x.png", - "scale" : "3x" + "filename" : "128 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "ios-20.png", - "scale" : "1x" + "filename" : "192.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" }, { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "ios-20@2x-1.png", - "scale" : "2x" + "filename" : "136.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "ios-30.png", - "scale" : "1x" + "filename" : "152 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" }, { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "ios-29@2x-1.png", - "scale" : "2x" + "filename" : "167 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "ios-40.png", - "scale" : "1x" + "filename" : "1024 1.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" }, { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "ios-40@2x-1.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "40.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" }, { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "60.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" }, { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "58.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" }, { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "87.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" }, { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "76.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "ios-ipad-76.png", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "114.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" }, { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "ios-ipad-76@2x.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "80.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" }, { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "ios-ipad-pro-83.5@2x.png", - "scale" : "2x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "120.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" }, { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "icon-apple-app-store.png", - "scale" : "1x" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "120 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "180.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "128.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "192 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "136 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "152.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "167.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "40 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "20x20" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "60 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "20x20" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "58 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "29x29" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "87 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "29x29" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "76 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "38x38" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "114 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "38x38" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "80 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "40x40" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "120 3.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "40x40" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "120 5.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "180 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "60x60" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "128 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "192 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "3x", + "size" : "64x64" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "136 1.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "68x68" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "152 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "76x76" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "167 2.png", + "idiom" : "universal", + "platform" : "ios", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "1024 2.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/icon-apple-app-store.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/icon-apple-app-store.png deleted file mode 100644 index f25fec9267a10..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/icon-apple-app-store.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20.png deleted file mode 100644 index 7d978fb694889..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@2x-1.png deleted file mode 100644 index b555d5197f8fa..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@2x.png deleted file mode 100644 index b555d5197f8fa..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@3x.png deleted file mode 100644 index c4d15e30e6290..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-20@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29.png deleted file mode 100644 index 0447daa7eb588..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@2x-1.png deleted file mode 100644 index 68068ef5144ad..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@2x.png deleted file mode 100644 index 68068ef5144ad..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@3x.png deleted file mode 100644 index e863cc5f61bd5..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-29@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-30.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-30.png deleted file mode 100644 index 0447daa7eb588..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-30.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40.png deleted file mode 100644 index 949c2623221a9..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@2x-1.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@2x-1.png deleted file mode 100644 index 32c6680501da5..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@2x-1.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@2x.png deleted file mode 100644 index 32c6680501da5..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@3x.png deleted file mode 100644 index 67743d8c5ca07..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-40@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-60@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-60@2x.png deleted file mode 100644 index a84d4e2d292c6..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-60@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-60@3x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-60@3x.png deleted file mode 100644 index 8273105207efd..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-60@3x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-76.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-76.png deleted file mode 100644 index 7f1a50f8f995c..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-76.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-76@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-76@2x.png deleted file mode 100644 index 9acc4f6a4180c..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-76@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-pro-83.5@2x.png b/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-pro-83.5@2x.png deleted file mode 100644 index 134c0ce699918..0000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/AppIcon_Developer.appiconset/ios-ipad-pro-83.5@2x.png and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Contents.json new file mode 100644 index 0000000000000..361954bcee447 --- /dev/null +++ b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "Logo.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Logo 1.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Logo 1.pdf b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Logo 1.pdf new file mode 100644 index 0000000000000..912ee167cab04 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Logo 1.pdf differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Logo.pdf b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Logo.pdf new file mode 100644 index 0000000000000..113167039c9ab Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoBall.imageset/Logo.pdf differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/Contents.json new file mode 100644 index 0000000000000..f79d7ff543202 --- /dev/null +++ b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "ecosia-logo.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "ecosia-logo-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/ecosia-logo-dark.pdf b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/ecosia-logo-dark.pdf new file mode 100644 index 0000000000000..09403a91698d6 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/ecosia-logo-dark.pdf differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/ecosia-logo.pdf b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/ecosia-logo.pdf new file mode 100644 index 0000000000000..4d12230435006 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/ecosiaHomeHeaderLogoText.imageset/ecosia-logo.pdf differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/homeLarge.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/homeLarge.imageset/Contents.json index 90be38a9830f2..e409f3d4479f0 100644 --- a/firefox-ios/Client/Assets/Images.xcassets/homeLarge.imageset/Contents.json +++ b/firefox-ios/Client/Assets/Images.xcassets/homeLarge.imageset/Contents.json @@ -10,6 +10,7 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } } diff --git a/firefox-ios/Client/Assets/Images.xcassets/pinSlashFillLarge.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/pinSlashFillLarge.imageset/Contents.json new file mode 100644 index 0000000000000..9275514c9917d --- /dev/null +++ b/firefox-ios/Client/Assets/Images.xcassets/pinSlashFillLarge.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "pinSlashFillLarge.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/pinSlashFillLarge.imageset/pinSlashFillLarge.pdf b/firefox-ios/Client/Assets/Images.xcassets/pinSlashFillLarge.imageset/pinSlashFillLarge.pdf new file mode 100644 index 0000000000000..e1c6373314256 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/pinSlashFillLarge.imageset/pinSlashFillLarge.pdf differ diff --git a/firefox-ios/Client/Assets/Search/SearchPlugins/google-b-m.xml b/firefox-ios/Client/Assets/Search/SearchPlugins/google-b-m.xml index 232bbce2145c3..5fb4525f23d80 100644 --- a/firefox-ios/Client/Assets/Search/SearchPlugins/google-b-m.xml +++ b/firefox-ios/Client/Assets/Search/SearchPlugins/google-b-m.xml @@ -5,7 +5,10 @@ Google UTF-8 + +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAACXBIWXMAACxLAAAsSwGlPZapAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAg6SURBVHgB7ZxfbFtXHce/5147dv47I3/aMZg7EEPin4EkrDDo7Z8HnqhLh1AZUmIxJNQ+tPDCJDTF0WC8gNJIiD3wEFcaagvbmkigPSxNbiUmrU2giYSQ0KbF0/40TarZid0ls33v2TluPXVtMvv6nvsnyfm8XOfa19f5fc/5nd/vd865gEQikUgkEolEIpFIJBKJRCKRSCSS7Q7BFuBtrS/WoFANhEQpyIMENHb7rWjlM+xclr2XZS/T5SPFvGIqeh7huT26noVP8aUAC5oWaVbyg8zgh7mxmUEjsAPBHBPkUsFA6gF9Zg4+wlcCXNMe0QKqeRIwNdtG3wwuhonR7qmZFHyALwTghldVY4i91OAeadYrhr0WwlMBmOGjzPBjcNfwd+OpEJ4JsHSw7yTz70nHXI1FFCBVNNTh3fqrabiI6wL4pNVvRtow1P1uiqDARZZYOMmMPw1/Gp/DG8fC4oFvnYJLuCbA6s+ODRCVcuNH4XMUYo4ss8AALuCKC1o62D8A0BS2CJSS4Z6pK0m4gOMCXD/YH2eD7QVsEdw0PsdRAfiAG1BLV/0S6VTDbeNzAnCI29HOtBPGJy2toPlcmvAYvkJbJILVLKsVoa77eWF8jmMCBNXSiAkShTh0k9IJxSR618TUpvWczNhYtPDcn1ixTokTSvaB0Gi1L/bK+BxHXBBPstjhNGzCflyWdPecUR/+aqoj+UxdRbSlA32D7JuGNhPCS+NzhAsg0O/rLClKiEqKrh/qTbIeMXTnOa+NzxGeBzC/b6u8wFu9Segvuy/OCM1IeyZnk0zQPaAkzf/2g/E5QnvA7YF3AfXCjBP83qEj9bqbWuC/kQRK8V2Ts7ZdpAiEClB4OZhav/jpgfWZLliGGd8wlf1uF8O8RoUg1qZZHYWqY8GHclDCBopvtNV8rdLSli2tl/buNONzhI0BaimYrLwO9S2j/fj/oLQXarrWyOcSO9H4HGECKAT7PvY3M37r46+j4Qsr1S4d7bl4ZRw7FCEClC73x+kGVU4uQvPRBTR+d3HjC5nfb/3pcV8Mhl4hRABz9Wr8k94PP7pY7g13uyQDdLgxkUhjByMkCipNBhdoDXV+c6UBub9+vnzkrb976soe7HBs9wAe/dRi/PLNWA/ggzN3SeqnOnVI7BfjgoYao9YuKbsk0rU+ir/DNoeezmmmogzAA0zQUf03LbaSRtsCsJQ+ZtWR8TJyy4llIdmuqfAiGx2EBygU8+xg6/+wPwg37IpZvcSgZB7bAEKrl7qrYV+A4vV2WIRNUerYBlBFsfy/3419AYKdliufCjHT2AZQP/QAUrphWQA2bvh2ubgVCCFR2MTVhVmSe5ECeIx9AdT7YBUR0cN2wb4AipKGRUyibIl1QtVgg3AaNrEtgFm48SYswkrXX4OkjP0oKPRgGtbRsA24r5lUneyohm0BqNKYhkV48Y6+FI5ii5PJm7bLKbZrQUbhNd3ql5y++WWcf3/PIPBiEjYxi61sNm1dh03UoHGBNQxLZRVTpbYFEDIfULzUlUExW3VgvWY24tcr/XjNaOd3zs7++IUO+IQDv8tnYHFd6TfuV/f/IdGowwZC8gBCwhPVPvOfYidOZL9zy/gcikjf2R8OwgdorKSNOhb12jU+R0wiFtr9iZPq59c/hxMr32Y9oOnjbxAyFBuLex6SspLCICxCKXQIQIgAav9lnR3uqe/kaBBPrvbjdP5LG17HB+NAWHFtP9ZGaL9fixICyxM6zHdX7fW1IMgFkSwrMZ+58xz39wOZfbhU2FXt4qHes49p8AhiGEnUgUl81AM4tF37yA3984PPMuNr97qcTSCEjj1yNh6Fy7CBd7Ce1k9B0nanIisIEyDYN6mzg85DzN/mYmX3UyvcFRlEnXZTBO562GEEdUBBhyEIodXQvcuHh8+vPYR6cFMEbnzFNPmW2foCgKKqQxBCBZg99rzODjrqpCKCk+EpDzkV07jK7hZFXZCUnrSe/W+G8PmAADUSPMlCnZTLFISMffPcUaHjgsbC3f7nnhgx2l+pv+WDZ96KMPfDcWSPWO+5x1hoSevyr3fCl69QQscDpjn66rHxNOqAGz7fpJxk33aKJ3/8XOi9OEKZI7CMSYennmpNQiCO7RPuPXdU9DMhxtlEzoQKU68mRt/f4jFaCmhQ6OHNfoPKIrXGxZNQSp2oBR750GLT1/Wk2PlsxwTg7qMp1HZ1tXBTfKbLXRy9tSCqsle47LqYX2c5SaTS0qvBjd+49HOoa1+s+tmHe8iRZ59oFr6M3tGd8r94Oan9f/WN6VzxJvxMVZfkgOup4PizInhEwwdV+Jxg7lGEbzwOclfy2N6I+Qu/arG8+q9WHF8VMXPsxVRv11eERg5OUGz9F25+5imYgRsfneN+/wd7m+NwENeemBX/x4lTb+cXbUdGTsN7QCgTRzD7fTboKvtFxvwb3g8u8szlP8cvLEyOsJYVhY9h0db88egL8cTeW5u6Hb0XXIZHRwZRpv0qQmeo48xL8b+c4hVeuIBnT01kJegka2pD8As8tDXI8OxPnnd106Cnzw3lvWF32wPTb+WuReEhHaE2PbeeSdSbbdvBF0/OLRffCIY8cEs6KGv1t4qInuCrZ0c/+cofB/+99N+BzAerGhyiNdiMgKLqmfWcp4av4Munp5cHaigaS+D4bJUGuzD/3tHQNtcabJp4b/mdlJ4Y983+BF8KcCeU0kjfuR/F7m/q1N5dW+ZrSiNtDc2xjWpM7Hx2pZBn89NkrjMcyTYFwvPdoY65Zw89PedWVCORSCQSiUQikUgkEolEIpFIJBKJD/kQkCH65NcnDzgAAAAASUVORK5CYII= diff --git a/firefox-ios/Client/Assets/Search/SearchPlugins/list.json b/firefox-ios/Client/Assets/Search/SearchPlugins/list.json index bb202496ef4b6..5694c6e4967d6 100644 --- a/firefox-ios/Client/Assets/Search/SearchPlugins/list.json +++ b/firefox-ios/Client/Assets/Search/SearchPlugins/list.json @@ -1,8 +1,8 @@ { "default": { - "searchDefault": "Google", + "searchDefault": "ecosia", "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia" + "ecosia", "wikipedia", "google-b-m", "bing", "ddg" ] }, "regionOverrides": { @@ -21,14 +21,14 @@ "an": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-an" + "google-b-m", "bing", "wikipedia-an" ] } }, "ar": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-ar" + "google-b-m", "bing", "wikipedia-ar" ] } }, @@ -42,14 +42,14 @@ "ast": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-ast" + "google-b-m", "bing", "wikipedia-ast" ] } }, "az": { "default": { "visibleDefaultEngines": [ - "google-b-m", "azerdict", "bing", "ddg", "wikipedia-az" + "google-b-m", "bing", "ddg", "wikipedia-az" ] } }, @@ -104,21 +104,21 @@ "br": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-br" + "google-b-m", "bing", "wikipedia-br" ] } }, "bs": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-bs" + "google-b-m", "bing", "wikipedia-bs" ] } }, "ca": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-ca" + "google-b-m", "wikipedia-ca" ] } }, @@ -132,7 +132,7 @@ "cs": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "mapy-cz", "seznam-cz", "wikipedia-cz" + "google-b-m", "bing", "ddg", "wikipedia-cz" ] } }, @@ -146,163 +146,168 @@ "da": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-da" + "google-b-m", "wikipedia-da" ] } }, "de": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay-de", "ecosia", "qwant", "wikipedia-de" + "google-b-m", "bing", "ddg", "ecosia", "wikipedia-de" ] } }, "dsb": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-dsb" + "google-b-m", "bing", "wikipedia-dsb" ] } }, "el": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-el" + "google-b-m", "bing", "wikipedia-el" ] } }, "en-GB": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay-co-uk", "qwant", "wikipedia" + "google-b-m", "bing", "ddg", "wikipedia" ] }, "BY": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "qwant", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "KZ": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "qwant", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "RU": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "qwant", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "TR": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "qwant", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] } }, "en-RU": { "default": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "BY": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "KZ": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "RU": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "TR": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] } }, "en-US": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia" + "google-b-m", "bing", "ddg", "wikipedia" ] }, "BY": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "ebay", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "KZ": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "ebay", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "RU": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "ebay", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "TR": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ddg", "ebay", "wikipedia" + "google-com-nocodes", "bing", "ddg", "wikipedia" ] }, "experimental-hidden": { "visibleDefaultEngines": [ - "google-b-1-m" + "google-b-1-m", "duckduckgo" ] } }, "en-ZA": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia" + "google-b-m", "wikipedia" ] } }, "eo": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-eo" + "google-b-m", "bing", "ddg", "wikipedia-eo" ] } }, "es-AR": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "mercadolibre-ar", "wikipedia-es" + "google-b-m", "wikipedia-es" ] } }, "es-CL": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "mercadolibre-cl", "wikipedia-es" + "google-b-m", "bing", "wikipedia-es" ] } }, "es-ES": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay-es-es", "wikipedia-es" + "google-b-m", "bing", "ddg", "wikipedia-es" ] } }, "es-MX": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "mercadolibre-mx", "wikipedia-es" + "google-b-m", "bing", "ddg", "wikipedia-es" + ] + }, + "experimental-hidden": { + "visibleDefaultEngines": [ + "amazon-mx" ] } }, "et": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-et" + "google-b-m", "wikipedia-et" ] } }, @@ -330,14 +335,14 @@ "fi": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-fi" + "google-b-m", "wikipedia-fi" ] } }, "fr": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay-fr", "qwant", "wikipedia-fr" + "google-b-m", "bing", "ddg", "wikipedia-fr" ] } }, @@ -351,28 +356,28 @@ "ga-IE": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay-ga-ie", "wikipedia-ga-IE" + "google-b-m", "wikipedia-ga-IE" ] } }, "gd": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "faclair-beag", "wikipedia-gd" + "google-b-m", "bing", "ddg", "wikipedia-gd" ] } }, "gl": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-gl" + "google-b-m", "bing", "wikipedia-gl" ] } }, "gn": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-gn" + "google-b-m", "bing", "wikipedia-gn" ] } }, @@ -386,35 +391,35 @@ "he": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-he" + "google-b-m", "bing", "wikipedia-he" ] } }, "hi-IN": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-hi" + "google-b-m", "bing", "wikipedia-hi" ] } }, "hr": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-hr" + "google-b-m", "bing", "ddg", "wikipedia-hr" ] } }, "hsb": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-hsb" + "google-b-m", "bing", "wikipedia-hsb" ] } }, "hu": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "vatera", "wikipedia-hu" + "google-b-m", "wikipedia-hu" ] } }, @@ -428,14 +433,14 @@ "ia": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-ia" + "google-b-m", "bing", "ddg", "wikipedia-ia" ] } }, "id": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-id" + "google-b-m", "bing", "wikipedia-id" ] } }, @@ -449,14 +454,14 @@ "it": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay-it", "wikipedia-it" + "google-b-m", "bing", "ddg", "wikipedia-it" ] } }, "ja": { "default": { "visibleDefaultEngines": [ - "google-b-m", "amazon-jp", "bing", "ebay", "wikipedia-ja", "yahoo-jp" + "google-b-m", "bing", "wikipedia-ja" ] } }, @@ -477,97 +482,97 @@ "kk": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-kk" + "google-b-m", "bing", "wikipedia-kk" ] }, "KZ": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ebay", "wikipedia-kk" + "google-com-nocodes", "bing", "wikipedia-kk" ] }, "BY": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ebay", "wikipedia-kk" + "google-com-nocodes", "bing", "wikipedia-kk" ] }, "RU": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ebay", "wikipedia-kk" + "google-com-nocodes", "bing", "wikipedia-kk" ] }, "TR": { "visibleDefaultEngines": [ - "google-com-nocodes", "bing", "ebay", "wikipedia-kk" + "google-com-nocodes", "bing", "wikipedia-kk" ] } }, "km": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-km" + "google-b-m", "bing", "wikipedia-km" ] } }, "kn": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-kn", "wiktionary-kn" + "google-b-m", "bing", "wikipedia-kn", ] } }, "ko": { "default": { "visibleDefaultEngines": [ - "google-b-m", "daum-kr", "ebay" + "google-b-m" ] } }, "lij": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia" + "google-b-m", "wikipedia" ] } }, "lo": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-lo" + "google-b-m", "bing", "wikipedia-lo" ] } }, "lt": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-lt" + "google-b-m", "wikipedia-lt" ] } }, "ltg": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-ltg" + "google-b-m", "bing", "wikipedia-ltg" ] } }, "lv": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "salidzinilv", "wikipedia-lv" + "google-b-m", "wikipedia-lv" ] } }, "mai": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-hi" + "google-b-m", "bing", "wikipedia-hi" ] } }, "ml": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-ml" + "google-b-m", "bing", "ddg", "wikipedia-ml" ] } }, @@ -581,56 +586,56 @@ "ms": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-ms" + "google-b-m", "bing", "wikipedia-ms" ] } }, "my": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-my" + "google-b-m", "bing", "wikipedia-my" ] } }, "nb-NO": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ddg", "ebay", "gulesider-mobile-NO", "wikipedia-NO" + "google-b-m", "ddg", "wikipedia-NO" ] } }, "ne-NP": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-ne" + "google-b-m", "bing", "wikipedia-ne" ] } }, "nl": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay-nl", "wikipedia-nl" + "google-b-m", "bing", "ddg", "wikipedia-nl" ] } }, "nn-NO": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ddg", "ebay", "gulesider-mobile-NO", "wikipedia-NN" + "google-b-m", "ddg", "wikipedia-NN" ] } }, "oc": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "wikipedia-oc", "wiktionary-oc" + "google-b-m", "bing", "wikipedia-oc", ] } }, "or": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "wikipedia-or", "wiktionary-or" + "google-b-m", "bing", "wikipedia-or", ] } }, @@ -644,14 +649,14 @@ "pl": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-pl" + "google-b-m", "bing", "ddg", "wikipedia-pl" ] } }, "pt-BR": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-pt" + "google-b-m", "bing", "wikipedia-pt" ] } }, @@ -665,131 +670,131 @@ "rm": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "leo_ende_de", "wikipedia-rm" + "google-b-m", "bing", "ddg", "wikipedia-rm" ] } }, "ro": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-ro" + "google-b-m", "wikipedia-ro" ] } }, "ru": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-ru" + "google-b-m", "wikipedia-ru" ] }, "RU": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "BY": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "KZ": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] }, "TR": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-ru" + "google-com-nocodes", "wikipedia-ru" ] } }, "sk": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-sk" + "google-b-m", "wikipedia-sk" ] } }, "sl": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ceneje", "ebay", "odpiralni", "wikipedia-sl" + "google-b-m", "wikipedia-sl" ] } }, "son": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-fr" + "google-b-m", "bing", "wikipedia-fr" ] } }, "sq": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-sq" + "google-b-m", "bing", "wikipedia-sq" ] } }, "sr": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-sr" + "google-b-m", "bing", "ddg", "wikipedia-sr" ] } }, "sv-SE": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "prisjakt-sv-SE", "wikipedia-sv-SE" + "google-b-m", "wikipedia-sv-SE" ] } }, "ta": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "wikipedia-ta", "wiktionary-ta" + "google-b-m", "bing", "ddg", "wikipedia-ta", ] } }, "te": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "wikipedia-te", "wiktionary-te" + "google-b-m", "bing", "wikipedia-te", ] } }, "th": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-th" + "google-b-m", "wikipedia-th" ] } }, "tr": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-tr" + "google-b-m", "wikipedia-tr" ] }, "TR": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-tr" + "google-com-nocodes", "wikipedia-tr" ] }, "BY": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-tr" + "google-com-nocodes", "wikipedia-tr" ] }, "KZ": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-tr" + "google-com-nocodes", "wikipedia-tr" ] }, "RU": { "visibleDefaultEngines": [ - "google-com-nocodes", "ebay", "wikipedia-tr" + "google-com-nocodes", "wikipedia-tr" ] } }, @@ -797,35 +802,35 @@ "default": { "searchDefault": "google-b-m", "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-es" + "google-b-m", "bing", "wikipedia-es" ] } }, "uk": { "default": { "visibleDefaultEngines": [ - "google-b-m", "ebay", "wikipedia-uk" + "google-b-m", "wikipedia-uk" ] } }, "ur": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ddg", "ebay", "wikipedia-ur" + "google-b-m", "bing", "ddg", "wikipedia-ur" ] } }, "uz": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia-uz" + "google-b-m", "bing", "wikipedia-uz" ] } }, "vi": { "default": { "visibleDefaultEngines": [ - "google-b-m", "coccoc", "ddg", "ebay", "wikipedia-vi" + "google-b-m", "ddg", "wikipedia-vi" ] } }, @@ -839,7 +844,7 @@ "xh": { "default": { "visibleDefaultEngines": [ - "google-b-m", "bing", "ebay", "wikipedia" + "google-b-m", "bing", "wikipedia" ] } }, @@ -853,7 +858,7 @@ "zh-CN": { "default": { "visibleDefaultEngines": [ - "google-b-m", "baidu", "bing", "wikipedia-zh-CN" + "google-b-m", "bing", "wikipedia-zh-CN" ] }, "CN": { diff --git a/firefox-ios/Client/Configuration/Common.xcconfig b/firefox-ios/Client/Configuration/Common.xcconfig index ebccc5896d76e..8e0071d18d775 100644 --- a/firefox-ios/Client/Configuration/Common.xcconfig +++ b/firefox-ios/Client/Configuration/Common.xcconfig @@ -21,13 +21,14 @@ HEADER_SEARCH_PATHS = $(inherited) /Applications/Xcode.app/Contents/Developer/To INCLUDE_SETTINGS_BUNDLE = NO IPHONEOS_DEPLOYMENT_TARGET = 15.0 LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../../Frameworks @executable_path/Frameworks @loader_path/Frameworks -MOZ_PUBLIC_URL_SCHEME = firefox -MOZ_TODAY_WIDGET_SEARCH_DISPLAY_NAME = Firefox - Search -OTHER_LDFLAGS = -ObjC -ld_classic +MOZ_PUBLIC_URL_SCHEME = ecosia +MOZ_TODAY_WIDGET_SEARCH_DISPLAY_NAME = Ecosia - Search +OTHER_LDFLAGS = -ObjC -lxml2 OTHER_SWIFT_FLAGS_common = -DMOZ_TARGET_CLIENT PRODUCT_NAME = $(TARGET_NAME) SDKROOT = iphoneos SKIP_INSTALL = YES // Overridden for Client and extensions, so they end up in the bundle +EAGER_LINKING = NO SWIFT_VERSION = 5.3 TARGETED_DEVICE_FAMILY = 1,2 TEST_TARGET_NAME = Client @@ -35,3 +36,4 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = @rpath PRODUCT_BUNDLE_IDENTIFIER = org.mozilla.ios.$(PRODUCT_NAME) +MARKETING_VERSION = 11.7.0 diff --git a/firefox-ios/Client/Configuration/Debug.xcconfig b/firefox-ios/Client/Configuration/Debug.xcconfig index 683f19bc8c882..8b06be48b229b 100644 --- a/firefox-ios/Client/Configuration/Debug.xcconfig +++ b/firefox-ios/Client/Configuration/Debug.xcconfig @@ -8,3 +8,4 @@ GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited) ONLY_ACTIVE_ARCH = YES SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = -Onone +COPY_PHASE_STRIP = NO diff --git a/firefox-ios/Client/Configuration/Ecosia.ShareTo.xcconfig b/firefox-ios/Client/Configuration/Ecosia.ShareTo.xcconfig new file mode 100644 index 0000000000000..8d92c0357c425 --- /dev/null +++ b/firefox-ios/Client/Configuration/Ecosia.ShareTo.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Ecosia.xcconfig" + +PROVISIONING_PROFILE_SPECIFIER = match AdHoc com.ecosia.ecosiaapp.ShareTo +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/Ecosia.entitlements +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/Ecosia.WidgetKit.xcconfig b/firefox-ios/Client/Configuration/Ecosia.WidgetKit.xcconfig new file mode 100644 index 0000000000000..d35b0b3061ac7 --- /dev/null +++ b/firefox-ios/Client/Configuration/Ecosia.WidgetKit.xcconfig @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Ecosia.xcconfig" + +ECOSIA_PRODUCT_NAME = WidgetKit +PROVISIONING_PROFILE_SPECIFIER = match AdHoc com.ecosia.ecosiaapp.WidgetKit +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/Ecosia.entitlements +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(ECOSIA_PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/Ecosia.xcconfig b/firefox-ios/Client/Configuration/Ecosia.xcconfig new file mode 100644 index 0000000000000..6fc1a9fc982a1 --- /dev/null +++ b/firefox-ios/Client/Configuration/Ecosia.xcconfig @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Common.xcconfig" +#include "Release.xcconfig" +#include "Production.xcconfig" + +MOZ_PRODUCT_NAME = Ecosia +MOZ_BUNDLE_DISPLAY_NAME = Ecosia +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/Ecosia.entitlements +OTHER_SWIFT_FLAGS = $(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_RELEASE +MOZ_INTERNAL_URL_SCHEME = ecosia +OTHER_LDFLAGS = $(inherited) -ld_classic +DEVELOPMENT_TEAM = 33YMRSYD2L diff --git a/firefox-ios/Client/Configuration/EcosiaBeta.ShareTo.xcconfig b/firefox-ios/Client/Configuration/EcosiaBeta.ShareTo.xcconfig new file mode 100644 index 0000000000000..c67747edf897f --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaBeta.ShareTo.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaBeta.xcconfig" + +PROVISIONING_PROFILE_SPECIFIER = match AdHoc com.ecosia.ecosiaapp.firefox.ShareTo +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(PRODUCT_NAME) +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp.firefox diff --git a/firefox-ios/Client/Configuration/EcosiaBeta.WidgetKit.xcconfig b/firefox-ios/Client/Configuration/EcosiaBeta.WidgetKit.xcconfig new file mode 100644 index 0000000000000..07a3b69401de2 --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaBeta.WidgetKit.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaBeta.xcconfig" + +ECOSIA_PRODUCT_NAME = WidgetKit +PROVISIONING_PROFILE_SPECIFIER = match AdHoc com.ecosia.ecosiaapp.firefox.WidgetKit +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(ECOSIA_PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaBeta.xcconfig b/firefox-ios/Client/Configuration/EcosiaBeta.xcconfig new file mode 100644 index 0000000000000..bb2f34ef8fc06 --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaBeta.xcconfig @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This is a version of the Ecosia Beta config for the staging environment. + +#include "Common.xcconfig" +#include "Release.xcconfig" +#include "Staging.xcconfig" + +MOZ_BUNDLE_DISPLAY_NAME = Ecosia Beta +MOZ_PRODUCT_NAME = Ecosia +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp.firefox +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements +OTHER_SWIFT_FLAGS = $(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_BETA +MOZ_INTERNAL_URL_SCHEME = ecosia +OTHER_LDFLAGS = $(inherited) -ld_classic +DEVELOPMENT_TEAM = 33YMRSYD2L diff --git a/firefox-ios/Client/Configuration/EcosiaBetaDebug.ShareTo.xcconfig b/firefox-ios/Client/Configuration/EcosiaBetaDebug.ShareTo.xcconfig new file mode 100644 index 0000000000000..84e0889a17edc --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaBetaDebug.ShareTo.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaBetaDebug.xcconfig" + +PROVISIONING_PROFILE_SPECIFIER = match Development com.ecosia.ecosiaapp.firefox.ShareTo +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp.firefox +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaBetaDebug.WidgetKit.xcconfig b/firefox-ios/Client/Configuration/EcosiaBetaDebug.WidgetKit.xcconfig new file mode 100644 index 0000000000000..9334528641919 --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaBetaDebug.WidgetKit.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaBetaDebug.xcconfig" + +ECOSIA_PRODUCT_NAME = WidgetKit +PROVISIONING_PROFILE_SPECIFIER = match Development com.ecosia.ecosiaapp.firefox.WidgetKit +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(ECOSIA_PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaBetaDebug.xcconfig b/firefox-ios/Client/Configuration/EcosiaBetaDebug.xcconfig new file mode 100644 index 0000000000000..2370d1d7d6f89 --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaBetaDebug.xcconfig @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This is a debuggable version of the Ecosia Beta config with the same Bundle ID and setup utilized in staging. + +#include "Common.xcconfig" +#include "Debug.xcconfig" +#include "Staging.xcconfig" + +INFOPLIST_FILE = Client/Info.plist +MOZ_BUNDLE_DISPLAY_NAME = Ecosia Beta +MOZ_PRODUCT_NAME = Ecosia +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp.firefox +INCLUDE_SETTINGS_BUNDLE = YES +LEANPLUM_ENVIRONMENT = development +MOZ_TODAY_WIDGET_SEARCH_DISPLAY_NAME = Ecosia - Search +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/EcosiaBeta.entitlements +OTHER_SWIFT_FLAGS = $(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC +MOZ_INTERNAL_URL_SCHEME = ecosia +OTHER_LDFLAGS = -ObjC -lxml2 -fprofile-instr-generate -ld_classic +DEVELOPMENT_TEAM = 33YMRSYD2L diff --git a/firefox-ios/Client/Configuration/EcosiaDebug.ShareTo.xcconfig b/firefox-ios/Client/Configuration/EcosiaDebug.ShareTo.xcconfig new file mode 100644 index 0000000000000..9422367b8577c --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaDebug.ShareTo.xcconfig @@ -0,0 +1,8 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +PROVISIONING_PROFILE_SPECIFIER = match Development com.ecosia.ecosiaapp.ShareTo +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/Ecosia.entitlements +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaDebug.WidgetKit.xcconfig b/firefox-ios/Client/Configuration/EcosiaDebug.WidgetKit.xcconfig new file mode 100644 index 0000000000000..3c7c79d3ea9ee --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaDebug.WidgetKit.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaDebug.xcconfig" + +ECOSIA_PRODUCT_NAME = WidgetKit +PROVISIONING_PROFILE_SPECIFIER = match Development com.ecosia.ecosiaapp.WidgetKit +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/Ecosia.entitlements +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(ECOSIA_PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaDebug.xcconfig b/firefox-ios/Client/Configuration/EcosiaDebug.xcconfig new file mode 100644 index 0000000000000..6ac6d74cc980c --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaDebug.xcconfig @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// This is a debuggable version of the Ecosia config with the same Bundle ID and setup utilized in production. +// Points to Production as only the `Release` configuration sets the Environment to be PRODUCTION. + +#include "Common.xcconfig" +#include "Debug.xcconfig" +#include "Staging.xcconfig" + +INFOPLIST_FILE = Client/Info.plist +MOZ_PRODUCT_NAME = Ecosia +MOZ_BUNDLE_DISPLAY_NAME = Ecosia +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp +INCLUDE_SETTINGS_BUNDLE = YES +LEANPLUM_ENVIRONMENT = development +MOZ_TODAY_WIDGET_SEARCH_DISPLAY_NAME = Ecosia - Search +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/Ecosia.entitlements +OTHER_SWIFT_FLAGS = $(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC +MOZ_INTERNAL_URL_SCHEME = ecosia +OTHER_LDFLAGS = -ObjC -lxml2 -fprofile-instr-generate -ld_classic +DEVELOPMENT_TEAM = 33YMRSYD2L +AUTH0_CLIENT_ID=FBaIsy0X5hIh2sSmalmf5pACZ512dIYl diff --git a/firefox-ios/Client/Configuration/EcosiaTesting.ShareTo.xcconfig b/firefox-ios/Client/Configuration/EcosiaTesting.ShareTo.xcconfig new file mode 100644 index 0000000000000..ea8b4a0a8086f --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaTesting.ShareTo.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaTesting.xcconfig" + +PROVISIONING_PROFILE_SPECIFIER = match Development com.ecosia.ecosiaapp.firefox.ShareTo +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements +MOZ_BUNDLE_ID = com.ecosia.ecosiaapp.firefox +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaTesting.WidgetKit.xcconfig b/firefox-ios/Client/Configuration/EcosiaTesting.WidgetKit.xcconfig new file mode 100644 index 0000000000000..4a3068142aaff --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaTesting.WidgetKit.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaTesting.xcconfig" + +ECOSIA_PRODUCT_NAME = WidgetKit +PROVISIONING_PROFILE_SPECIFIER = match Development com.ecosia.ecosiaapp.firefox.WidgetKit +CODE_SIGN_ENTITLEMENTS = Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements +PRODUCT_BUNDLE_IDENTIFIER = $(MOZ_BUNDLE_ID).$(ECOSIA_PRODUCT_NAME) diff --git a/firefox-ios/Client/Configuration/EcosiaTesting.xcconfig b/firefox-ios/Client/Configuration/EcosiaTesting.xcconfig new file mode 100644 index 0000000000000..5a825c03b0a24 --- /dev/null +++ b/firefox-ios/Client/Configuration/EcosiaTesting.xcconfig @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "EcosiaBetaDebug.xcconfig" + +SWIFT_ACTIVE_COMPILATION_CONDITIONS = TESTING; +OTHER_SWIFT_FLAGS = $(OTHER_SWIFT_FLAGS_common) -DMOZ_CHANNEL_FENNEC -suppress-warnings +SWIFT_TREAT_WARNINGS_AS_ERRORS=NO +SWIFT_STRICT_CONCURRENCY=minimal diff --git a/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift b/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift index 6617e1c6c6412..490f4d95b0b6b 100644 --- a/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Browser/BrowserCoordinator.swift @@ -9,6 +9,7 @@ import Shared import Storage import Redux import TabDataStore +import Ecosia import enum MozillaAppServices.VisitType import struct MozillaAppServices.CreditCard @@ -97,7 +98,12 @@ class BrowserCoordinator: BaseCoordinator, // MARK: - LaunchCoordinatorDelegate func didFinishLaunch(from coordinator: LaunchCoordinator) { + /* Ecosia: Animate transition from welcome screen router.dismiss(animated: true, completion: nil) + */ + router.dismiss(animated: true) { [weak self] in + self?.browserViewController.animateToolbarsIn() + } remove(child: coordinator) // Once launch is done, we check for any saved Route @@ -109,6 +115,16 @@ class BrowserCoordinator: BaseCoordinator, } } + // Ecosia: Handle sign-in request from welcome screen + func didRequestSignIn(from coordinator: LaunchCoordinator) { + router.dismiss(animated: true) { [weak self] in + guard let self else { return } + self.browserViewController.animateToolbarsIn() + EcosiaAuth.performWelcomeSignIn(browserViewController: self.browserViewController) + } + remove(child: coordinator) + } + // MARK: - BrowserDelegate func showLegacyHomepage( @@ -215,7 +231,14 @@ class BrowserCoordinator: BaseCoordinator, isZeroSearch: inline, toastContainer: toastContainer, tabManager: tabManager, - overlayManager: overlayManager) + overlayManager: overlayManager, + // Ecosia: Add Referrals + referrals: browserViewController.referrals, + // Ecosia: Add HomePageViewControllerDelegate + delegate: browserViewController, + // Ecosia: Add EcosiaAuth + auth: EcosiaAuth(browserViewController: browserViewController) + ) legacyHomepageViewController.homePanelDelegate = homepanelDelegate legacyHomepageViewController.libraryPanelDelegate = libraryPanelDelegate legacyHomepageViewController.statusBarScrollDelegate = statusBarScrollDelegate @@ -245,7 +268,10 @@ class BrowserCoordinator: BaseCoordinator, } switch route { + /* Ecosia: Add referrals case .searchQuery, .search, .searchURL, .glean, .homepanel, .action, .fxaSignIn, .defaultBrowser: + */ + case .searchQuery, .search, .searchURL, .glean, .homepanel, .action, .fxaSignIn, .defaultBrowser, .referrals: return true case let .settings(section): return canHandleSettings(with: section) @@ -300,6 +326,15 @@ class BrowserCoordinator: BaseCoordinator, case .tutorial: startLaunch(with: .defaultBrowser) } + + // Ecosia: Add referrals + case let .referrals(code): + User.shared.referrals.pendingClaim = code + // on first start, browser is not in view hierarchy yet + guard !User.shared.firstTime else { return } + browserViewController.openBlankNewTab(focusLocationField: false) + // Intro logic will trigger claiming referral + browserViewController.presentIntroViewController() } } @@ -428,7 +463,10 @@ class BrowserCoordinator: BaseCoordinator, } func openDebugTestTabs(count: Int) { + /* Ecosia: Update debug Tabs URL guard let url = URL(string: "https://www.mozilla.org") else { return } + */ + guard let url = URL(string: "https://www.ecosia.org") else { return } browserViewController.debugOpen(numberOfNewTabs: count, at: url) } @@ -542,12 +580,22 @@ class BrowserCoordinator: BaseCoordinator, } private func makeMenuNavViewController() -> DismissableNavigationViewController? { - guard !childCoordinators.contains(where: { $0 is MainMenuCoordinator }) else { return nil } + if let mainMenuCoordinator = childCoordinators.first(where: { $0 is MainMenuCoordinator }) as? MainMenuCoordinator { + logger.log( + "MainMenuCoordinator already exists when it shouldn't. Removing and recreating it to access menu", + level: .fatal, + category: .mainMenu, + extra: ["existing mainMenuCoordinator UUID": "\(mainMenuCoordinator.windowUUID)", + "BrowserCoordinator windowUUID": "\(windowUUID)"] + ) + + mainMenuCoordinator.dismissMenuModal(animated: false) + } let navigationController = DismissableNavigationViewController() - if navigationController.shouldUseiPadSetup() { + navigationController.modalPresentationStyle = .formSheet + if !navigationController.shouldUseiPadSetup() { navigationController.modalPresentationStyle = .formSheet - } else { navigationController.sheetPresentationController?.detents = [.medium(), .large()] navigationController.sheetPresentationController?.prefersGrabberVisible = true } @@ -562,6 +610,14 @@ class BrowserCoordinator: BaseCoordinator, add(child: coordinator) coordinator.start() + navigationController.onViewDismissed = { [weak self] in + self?.logger.log( + "MainMenu NavigationController - onViewDismissed", + level: .info, + category: .mainMenu + ) + } + return navigationController } @@ -1012,3 +1068,82 @@ class BrowserCoordinator: BaseCoordinator, } } } + +// Ecosia: BrowserCoordinator extension that implements the overlay card logic +extension BrowserCoordinator { + + func showPendingOverlayCard(inline: Bool) { + if inline, + !User.shared.firstTime { + // -> delay of 0.5s to wait for animations and dismissals to finish + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.presentInsightfulSheetsIfNeeded() + // At this stage, we consider it a safe place where storing the current version + EcosiaInstallType.evaluateCurrentEcosiaInstallType(storeUpgradeVersion: true) + } + } + } + + private func presentInsightfulSheetsIfNeeded() { + let shouldShowLoadingScreen = User.shared.referrals.pendingClaim != nil + // If not on homepage or if there's a pending referral claim, do nothing + guard isHomePage(), !shouldShowLoadingScreen else { return } + // Get first function that returns `true` for a cards needs presenting. + _ = presentableCards.first(where: { $0() }) + } + + private var shouldShowDefaultBrowserPromo: Bool { + browserViewController.profile.prefs.intForKey(PrefsKeys.IntroSeen) == nil && + User.shared.searchCount >= DefaultBrowserViewController.minSearchCountToTrigger + } + + private var presentableCards: [() -> Bool] { + [ + presentDefaultBrowserPromoIfNeeded, + presentWhatsNewPageIfNeeded + ] + } + + private var shouldShowWhatsNewPageScreen: Bool { browserViewController.whatsNewDataProvider.shouldShowWhatsNewPage } + + private func isHomePage() -> Bool { + browserViewController.tabManager.selectedTab?.url.flatMap { InternalURL($0)?.isAboutHomeURL } ?? false + } + + @discardableResult + private func presentDefaultBrowserPromoIfNeeded() -> Bool { + guard shouldShowDefaultBrowserPromo else { return false } + + if #available(iOS 14, *) { + let defaultPromo = DefaultBrowserViewController(windowUUID: windowUUID, delegate: browserViewController) + present(defaultPromo, animated: true) + } else { + profile.prefs.setInt(1, forKey: PrefsKeys.IntroSeen) + } + return true + } + + @discardableResult + private func presentWhatsNewPageIfNeeded() -> Bool { + guard shouldShowWhatsNewPageScreen else { return false } + let viewModel = WhatsNewViewModel(provider: browserViewController.whatsNewDataProvider) + WhatsNewViewController.presentOn(browserViewController, viewModel: viewModel, windowUUID: windowUUID) + return true + } +} + +// Ecosia: Set BrowserCoordinator as Braze's browserDelegate to handle push urls +extension BrowserCoordinator: BrazeBrowserDelegate { + func openBrazeURLInNewTab(_ url: URL?) { + guard let url else { return } + + if tabManager.isRestoringTabs { + // On cold start we need to wait for tab restoration to finish + AppEventQueue.wait(for: .tabRestoration(windowUUID)) { [weak self] in + self?.openURLInNewTab(url) + } + } else { + openURLInNewTab(url) + } + } +} diff --git a/firefox-ios/Client/Coordinators/Browser/BrowserDelegate.swift b/firefox-ios/Client/Coordinators/Browser/BrowserDelegate.swift index 0650a070a4962..2a1b97ececa97 100644 --- a/firefox-ios/Client/Coordinators/Browser/BrowserDelegate.swift +++ b/firefox-ios/Client/Coordinators/Browser/BrowserDelegate.swift @@ -39,4 +39,13 @@ protocol BrowserDelegate: AnyObject { /// Show the Error page to the user func showNativeErrorPage(overlayManager: OverlayModeManager) + + /* + Ecosia: Add a new behavior that allows the browser to show + pending overlay cards on top of the Browser View if needed. + E.g. Default Browser Card + The `inline` param keeps companibility with the legacy homepage + showing method + */ + func showPendingOverlayCard(inline: Bool) } diff --git a/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift b/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift index cae51e04e6983..69892ea0cdb70 100644 --- a/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift @@ -4,9 +4,12 @@ import Common import Foundation +import Ecosia protocol LaunchCoordinatorDelegate: AnyObject { func didFinishLaunch(from coordinator: LaunchCoordinator) + // Ecosia: Handle sign-in request from welcome screen + func didRequestSignIn(from coordinator: LaunchCoordinator) } // Manages different types of onboarding that gets shown at the launch of the application @@ -46,35 +49,58 @@ class LaunchCoordinator: BaseCoordinator, // MARK: - Intro private func presentIntroOnboarding(with manager: IntroScreenManager, isFullScreen: Bool) { - let onboardingModel = NimbusOnboardingFeatureLayer().getOnboardingModel(for: .freshInstall) - let telemetryUtility = OnboardingTelemetryUtility(with: onboardingModel) - let introViewModel = IntroViewModel(introScreenManager: manager, - profile: profile, - model: onboardingModel, - telemetryUtility: telemetryUtility) - let introViewController = IntroViewController(viewModel: introViewModel, windowUUID: windowUUID) - introViewController.qrCodeNavigationHandler = self - introViewController.didFinishFlow = { [weak self] in + // Wait for feature flags to be available since Onboarding is behind an experiment + AppEventQueue.wait(for: .featureManagementInitialized) { [weak self] in guard let self = self else { return } - self.parentCoordinator?.didFinishLaunch(from: self) - } - if isFullScreen { - introViewController.modalPresentationStyle = .fullScreen - router.present(introViewController, animated: false) - } else { - introViewController.preferredContentSize = CGSize( - width: ViewControllerConsts.PreferredSize.IntroViewController.width, - height: ViewControllerConsts.PreferredSize.IntroViewController.height) - introViewController.modalPresentationStyle = .formSheet - // Disables dismissing the view by tapping outside the view, based on - // Nimbus's configuration - if !introViewModel.isDismissable { - introViewController.isModalInPresentation = true + // Ecosia: Hide onboarding out of experiment + guard OnboardingProductTourExperiment.isEnabled else { + self.parentCoordinator?.didFinishLaunch(from: self) + return + } + + // Ecosia: Initialize product tour state for first launch + ProductTourManager.shared.resetTour() + + /* Ecosia: custom onboarding + let onboardingModel = NimbusOnboardingFeatureLayer().getOnboardingModel(for: .freshInstall) + let telemetryUtility = OnboardingTelemetryUtility(with: onboardingModel) + let introViewModel = IntroViewModel(introScreenManager: manager, + profile: profile, + model: onboardingModel, + telemetryUtility: telemetryUtility) + + let introViewController = IntroViewController(viewModel: introViewModel, windowUUID: windowUUID) + introViewController.qrCodeNavigationHandler = self + introViewController.didFinishFlow = { [weak self] in + guard let self = self else { return } + self.parentCoordinator?.didFinishLaunch(from: self) } - router.present(introViewController, animated: true) { - introViewController.closeOnboarding() + + if isFullScreen { + introViewController.modalPresentationStyle = .fullScreen + router.present(introViewController, animated: false) + } else { + introViewController.preferredContentSize = CGSize( + width: ViewControllerConsts.PreferredSize.IntroViewController.width, + height: ViewControllerConsts.PreferredSize.IntroViewController.height) + introViewController.modalPresentationStyle = .formSheet + // Disables dismissing the view by tapping outside the view, based on + // Nimbus's configuration + if !introViewModel.isDismissable { + introViewController.isModalInPresentation = true + } + router.present(introViewController, animated: true) } + */ + let introViewController = WelcomeNavigation( + rootViewController: WelcomeViewController(delegate: self, windowUUID: self.windowUUID), + windowUUID: self.windowUUID + ) + introViewController.isNavigationBarHidden = true + introViewController.edgesForExtendedLayout = UIRectEdge(rawValue: 0) + introViewController.modalPresentationStyle = .fullScreen + self.router.present(introViewController, animated: false) } } @@ -171,3 +197,14 @@ class LaunchCoordinator: BaseCoordinator, parentCoordinator?.didFinishLaunch(from: self) } } + +// Ecosia: custom onboarding +extension LaunchCoordinator: WelcomeDelegate { + func welcomeDidFinish(_ welcome: WelcomeViewController) { + self.parentCoordinator?.didFinishLaunch(from: self) + } + + func welcomeDidRequestSignIn(_ welcome: WelcomeViewController) { + self.parentCoordinator?.didRequestSignIn(from: self) + } +} diff --git a/firefox-ios/Client/Coordinators/LaunchView/LaunchScreenViewController.swift b/firefox-ios/Client/Coordinators/LaunchView/LaunchScreenViewController.swift index 10cda494ba8ea..66e996c71ac94 100644 --- a/firefox-ios/Client/Coordinators/LaunchView/LaunchScreenViewController.swift +++ b/firefox-ios/Client/Coordinators/LaunchView/LaunchScreenViewController.swift @@ -6,7 +6,9 @@ import Common import Foundation class LaunchScreenViewController: UIViewController, LaunchFinishedLoadingDelegate, FeatureFlaggable { - private lazy var launchScreen = LaunchScreenView.fromNib() + // Ecosia: Custom launch screen + // private lazy var launchScreen = LaunchScreenView.fromNib() + private lazy var launchScreen = EcosiaLaunchScreenView.fromNib() private weak var coordinator: LaunchFinishedLoadingDelegate? private var viewModel: LaunchScreenViewModel private var mainQueue: DispatchQueueInterface diff --git a/firefox-ios/Client/Coordinators/Library/LibraryCoordinator.swift b/firefox-ios/Client/Coordinators/Library/LibraryCoordinator.swift index cd825e2abdeef..e60a0417a8a9d 100644 --- a/firefox-ios/Client/Coordinators/Library/LibraryCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Library/LibraryCoordinator.swift @@ -71,8 +71,12 @@ class LibraryCoordinator: BaseCoordinator, return [ ThemedNavigationController(rootViewController: bookmarksPanel, windowUUID: windowUUID), ThemedNavigationController(rootViewController: historyPanel, windowUUID: windowUUID), + /* Ecosia: Invert Download and Reading list positions in the LibraryViewController ThemedNavigationController(rootViewController: downloadsPanel, windowUUID: windowUUID), ThemedNavigationController(rootViewController: readingListPanel, windowUUID: windowUUID) + */ + ThemedNavigationController(rootViewController: readingListPanel, windowUUID: windowUUID), + ThemedNavigationController(rootViewController: downloadsPanel, windowUUID: windowUUID) ] } diff --git a/firefox-ios/Client/Coordinators/Router/Route.swift b/firefox-ios/Client/Coordinators/Router/Route.swift index 68a24be3d23d1..d67925d5faad3 100644 --- a/firefox-ios/Client/Coordinators/Router/Route.swift +++ b/firefox-ios/Client/Coordinators/Router/Route.swift @@ -66,6 +66,11 @@ enum Route: Equatable { /// settings to be displayed. case defaultBrowser(section: DefaultBrowserSection) + /// Represents the referrals route that takes a string as invite code. + /// + /// - Parameter code: represents the invite code + case referrals(code: String) + /// An enumeration representing different sections of the home panel. enum HomepanelSection: String, CaseIterable, Equatable { case bookmarks diff --git a/firefox-ios/Client/Coordinators/Router/RouteBuilder.swift b/firefox-ios/Client/Coordinators/Router/RouteBuilder.swift index b64076ee9c08a..a6dc9b1e917c1 100644 --- a/firefox-ios/Client/Coordinators/Router/RouteBuilder.swift +++ b/firefox-ios/Client/Coordinators/Router/RouteBuilder.swift @@ -5,6 +5,7 @@ import Foundation import CoreSpotlight import Shared +import Ecosia final class RouteBuilder { private var isPrivate = false @@ -124,9 +125,19 @@ final class RouteBuilder { } } else if urlScanner.isHTTPScheme { TelemetryWrapper.gleanRecordEvent(category: .action, method: .open, object: .asDefaultBrowser) + + // Ecosia: Track app open as default browser + Analytics.shared.appOpenAsDefaultBrowser() + RatingPromptManager.isBrowserDefault = true // Use the last browsing mode the user was in return .search(url: url, isPrivate: isPrivate, options: [.focusLocationField]) + + // Ecosia: Referral deeplink + } else if urlScanner.scheme == "ecosia", urlScanner.host == "invite" { + let paths = url.absoluteString.split(separator: "/") + guard let componentPath = paths[safe: 2] else { return nil } + return .referrals(code: String(componentPath)) } else { return nil } diff --git a/firefox-ios/Client/Coordinators/Scene/SceneCoordinator.swift b/firefox-ios/Client/Coordinators/Scene/SceneCoordinator.swift index b7342b9d9e0fb..34cf7d9734843 100644 --- a/firefox-ios/Client/Coordinators/Scene/SceneCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Scene/SceneCoordinator.swift @@ -6,6 +6,7 @@ import Common import UIKit import Shared import Storage +import Ecosia /// Each scene has it's own scene coordinator, which is the root coordinator for a scene. class SceneCoordinator: BaseCoordinator, LaunchCoordinatorDelegate, LaunchFinishedLoadingDelegate { @@ -80,7 +81,10 @@ class SceneCoordinator: BaseCoordinator, LaunchCoordinatorDelegate, LaunchFinish let profile: Profile = AppContainer.shared.resolve() let introManager = IntroScreenManager(prefs: profile.prefs) let launchType = LaunchType.intro(manager: introManager) - if launchType.canLaunch(fromType: .SceneCoordinator) { + // Ecosia: custom onboarding + // if launchType.canLaunch(fromType: .SceneCoordinator) { + if launchType.canLaunch(fromType: .SceneCoordinator), + User.shared.firstTime { startLaunch(with: launchType) } } @@ -126,6 +130,9 @@ class SceneCoordinator: BaseCoordinator, LaunchCoordinatorDelegate, LaunchFinish screenshotService: screenshotService, tabManager: tabManager) + // Ecosia: Set BrowserCoordinator as Braze's browserDelegate to handle push urls + BrazeService.shared.browserDelegate = browserCoordinator + let windowInfo = AppWindowInfo(tabManager: tabManager, sceneCoordinator: self) windowManager.newBrowserWindowConfigured(windowInfo, uuid: windowUUID) @@ -140,8 +147,41 @@ class SceneCoordinator: BaseCoordinator, LaunchCoordinatorDelegate, LaunchFinish // MARK: - LaunchCoordinatorDelegate func didFinishLaunch(from coordinator: LaunchCoordinator) { + /* Ecosia: Custom transition router.dismiss(animated: true) remove(child: coordinator) + */ + startBrowser(with: nil) + + // Ecosia: Animate transition from welcome screen + guard let browserCoordinator = childCoordinators.first(where: { $0 is BrowserCoordinator }) as? BrowserCoordinator else { + router.dismiss(animated: true) + remove(child: coordinator) + return + } + + browserCoordinator.browserViewController.prepareToolbarsForWelcomeTransition() + router.dismiss(animated: true) { + browserCoordinator.browserViewController.animateToolbarsIn() + } + remove(child: coordinator) + } + + // Ecosia: Handle sign-in request from welcome screen + func didRequestSignIn(from coordinator: LaunchCoordinator) { startBrowser(with: nil) + + guard let browserCoordinator = childCoordinators.first(where: { $0 is BrowserCoordinator }) as? BrowserCoordinator else { + router.dismiss(animated: true) + remove(child: coordinator) + return + } + + browserCoordinator.browserViewController.prepareToolbarsForWelcomeTransition() + router.dismiss(animated: true) { + browserCoordinator.browserViewController.animateToolbarsIn() + EcosiaAuth.performWelcomeSignIn(browserViewController: browserCoordinator.browserViewController) + } + remove(child: coordinator) } } diff --git a/firefox-ios/Client/Coordinators/SettingsCoordinator.swift b/firefox-ios/Client/Coordinators/SettingsCoordinator.swift index b9dc99fe8edd1..9812d2d02ec65 100644 --- a/firefox-ios/Client/Coordinators/SettingsCoordinator.swift +++ b/firefox-ios/Client/Coordinators/SettingsCoordinator.swift @@ -96,12 +96,17 @@ class SettingsCoordinator: BaseCoordinator, return viewController case .homePage: + /* Ecosia: Update Home page customization route let viewController = HomePageSettingViewController(prefs: profile.prefs, settingsDelegate: self, tabManager: tabManager) - viewController.profile = profile - return viewController - + viewController.profile = profile + return viewController + */ + let customizationViewController = NTPCustomizationSettingsViewController(windowUUID: windowUUID) + customizationViewController.profile = profile + customizationViewController.settingsDelegate = self + return customizationViewController case .mailto: let viewController = OpenWithSettingsViewController(prefs: profile.prefs, windowUUID: windowUUID) return viewController @@ -181,6 +186,11 @@ class SettingsCoordinator: BaseCoordinator, parentCoordinator?.didFinishSettings(from: self) } + // Ecosia: SettingsDelegate + func reloadHomepage() { + (parentCoordinator as? BrowserCoordinator)?.legacyHomepageViewController?.reloadView() + } + // MARK: - SettingsFlowDelegate func showDevicePassCode() { let passcodeViewController = DevicePasscodeRequiredViewController(windowUUID: windowUUID) @@ -307,11 +317,17 @@ class SettingsCoordinator: BaseCoordinator, // MARK: GeneralSettingsDelegate func pressedHome() { + /* Ecosia: Update Home page customization route let viewController = HomePageSettingViewController(prefs: profile.prefs, settingsDelegate: self, tabManager: tabManager) - viewController.profile = profile - router.push(viewController) + viewController.profile = profile + router.push(viewController) + */ + let customizationViewController = NTPCustomizationSettingsViewController(windowUUID: windowUUID) + customizationViewController.profile = profile + customizationViewController.settingsDelegate = self + router.push(customizationViewController) } func pressedMailApp() { diff --git a/firefox-ios/Client/Ecosia/Account/AccountsProviderWrapper.swift b/firefox-ios/Client/Ecosia/Account/AccountsProviderWrapper.swift new file mode 100644 index 0000000000000..6c61520e0b31d --- /dev/null +++ b/firefox-ios/Client/Ecosia/Account/AccountsProviderWrapper.swift @@ -0,0 +1,29 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia + +/// Wrapper for AccountsProvider that adds error simulation capability for hidden settings. +/// This keeps the Ecosia framework clean while allowing Client module to inject simulation behavior. +/// +/// This wrapper is configured in AppDelegate and used throughout the app via dependency injection. +final class AccountsProviderWrapper: AccountsProviderProtocol { + + private let wrapped: AccountsProviderProtocol + + init(wrapped: AccountsProviderProtocol = AccountsProvider()) { + self.wrapped = wrapped + } + + func registerVisit(accessToken: String) async throws -> AccountVisitResponse { + // Debug: Simulate impact API error if enabled + if UserDefaults.standard.bool(forKey: SimulateImpactAPIErrorSetting.debugKey) { + EcosiaLogger.accounts.info("🐛 [DEBUG] Simulating impact API error") + throw NSError(domain: "EcosiaDebug", code: -1, userInfo: [NSLocalizedDescriptionKey: "Debug: Simulated impact API error"]) + } + + return try await wrapped.registerVisit(accessToken: accessToken) + } +} diff --git a/firefox-ios/Client/Ecosia/Account/Auth/EcosiaAuth.swift b/firefox-ios/Client/Ecosia/Account/Auth/EcosiaAuth.swift new file mode 100644 index 0000000000000..b3888635bf42d --- /dev/null +++ b/firefox-ios/Client/Ecosia/Account/Auth/EcosiaAuth.swift @@ -0,0 +1,235 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia +import Common + +/** + EcosiaAuth provides authentication management for the Ecosia browser. + + This class provides a clean chainable API and delegates to specialized components + for improved maintainability and testability. + + ## Usage + + ```swift + ecosiaAuth + .onNativeAuthCompleted { + // Called when Auth0 authentication completes + } + .onAuthFlowCompleted { success in + // Called when entire flow completes + } + .onError { error in + // Called when authentication fails + } + .login() // Starts login authentication + ``` + + ## Architecture + + - **EcosiaAuth**: Main entry point with chainable API + - **AuthFlow**: Core authentication orchestration + - **InvisibleTabSession**: Web session management + - **TabAutoCloseManager**: Automatic tab cleanup + */ +final class EcosiaAuth { + + // MARK: - Dependencies + + private let authService: Ecosia.EcosiaAuthenticationService + private weak var browserViewController: BrowserViewController? + + // MARK: - Authentication Flow Properties + + private var onNativeAuthCompletedCallback: (() -> Void)? + private var onAuthFlowCompletedCallback: ((Bool) -> Void)? + private var onErrorCallback: ((AuthError) -> Void)? + private var delayedCompletionTime: TimeInterval = 0.0 + + // MARK: - Initialization + + /// Initializes EcosiaAuth with required dependencies + /// - Parameters: + /// - browserViewController: The browser view controller for tab operations + /// - authService: The authentication service for auth operations (defaults to Ecosia.EcosiaAuthenticationService.shared) + init(browserViewController: BrowserViewController, + authService: Ecosia.EcosiaAuthenticationService = Ecosia.EcosiaAuthenticationService.shared) { + self.authService = authService + self.browserViewController = browserViewController + self.browserViewController?.ecosiaAuth = self + EcosiaLogger.auth.info("EcosiaAuth initialized") + } + + // MARK: - Authentication Flow + + /// Sets callback for when native Auth0 authentication completes + /// - Parameter callback: Closure called when Auth0 authentication finishes + /// - Returns: Self for chaining + @discardableResult + func onNativeAuthCompleted(_ callback: @escaping () -> Void) -> EcosiaAuth { + onNativeAuthCompletedCallback = callback + return self + } + + /// Sets callback for when the complete authentication flow finishes + /// - Parameter callback: Closure called with success status when entire flow completes + /// - Returns: Self for chaining + @discardableResult + func onAuthFlowCompleted(_ callback: @escaping (Bool) -> Void) -> EcosiaAuth { + onAuthFlowCompletedCallback = callback + return self + } + + /// Sets callback for when an error occurs during the authentication flow + /// - Parameter callback: Closure called with the error when authentication fails + /// - Returns: Self for chaining + @discardableResult + func onError(_ callback: @escaping (AuthError) -> Void) -> EcosiaAuth { + onErrorCallback = callback + return self + } + + /// Sets the delay before firing the onNativeAuthCompleted callback + /// - Parameter delay: Delay in seconds before calling onNativeAuthCompleted + /// - Returns: Self for chaining + @discardableResult + func withDelayedCompletion(_ delay: TimeInterval) -> EcosiaAuth { + delayedCompletionTime = delay + return self + } + + /// Starts the login authentication flow + func login() { + guard let browserViewController = browserViewController else { + fatalError("BrowserViewController not available for auth flow") + } + + let flow = EcosiaAuthFlow( + type: .login, + authService: authService, + browserViewController: browserViewController + ) + + // Start the authentication process + Task { + await performLogin(flow) + } + } + + /// Starts the logout authentication flow + func logout() { + guard let browserViewController = browserViewController else { + fatalError("BrowserViewController not available for auth flow") + } + + let flow = EcosiaAuthFlow( + type: .logout, + authService: authService, + browserViewController: browserViewController + ) + + // Start the authentication process + Task { + await performLogout(flow) + } + } + + // MARK: - Private Implementation + + private func performLogin(_ flow: EcosiaAuthFlow) async { + let result = await flow.startLogin( + delayedCompletion: delayedCompletionTime, + onNativeAuthCompleted: onNativeAuthCompletedCallback, + onFlowCompleted: onAuthFlowCompletedCallback, + onError: onErrorCallback + ) + + switch result { + case .success: + EcosiaLogger.auth.debug("Login flow completed successfully") + case .failure(let error): + EcosiaLogger.auth.error("Login flow failed: \(error)") + if case .userCancelled = error { + Analytics.shared.accountSignInCancelled() + } else { + // Show error toast for non-cancellation errors + await MainActor.run { + if #available(iOS 16.0, *) { + browserViewController?.showAuthFlowErrorToast(isLogin: true) + } + } + } + } + } + + private func performLogout(_ flow: EcosiaAuthFlow) async { + let result = await flow.startLogout( + delayedCompletion: delayedCompletionTime, + onNativeAuthCompleted: onNativeAuthCompletedCallback, + onFlowCompleted: onAuthFlowCompletedCallback, + onError: onErrorCallback + ) + + switch result { + case .success: + EcosiaLogger.auth.info("Logout flow completed successfully") + case .failure(let error): + EcosiaLogger.auth.error("Logout flow failed: \(error)") + // Show error toast for logout errors + await MainActor.run { + if #available(iOS 16.0, *) { + browserViewController?.showAuthFlowErrorToast(isLogin: false) + } + } + } + } + + // MARK: - Welcome Screen Sign-In + + /// Performs the sign-in flow triggered from the welcome/onboarding screen. + /// + /// This encapsulates the shared logic used by both `BrowserCoordinator` and + /// `SceneCoordinator` when the user requests sign-in during onboarding: + /// - Signals `ProductTourManager` that the sign-in flow has started + /// - Creates an `EcosiaAuth` instance and begins the login flow + /// - Ensures `ProductTourManager` is notified when the flow ends (success or error) + /// + /// - Parameter browserViewController: The `BrowserViewController` to use for the auth flow. + static func performWelcomeSignIn(browserViewController: BrowserViewController) { + ProductTourManager.shared.signInFlowDidStart() + + EcosiaAuth(browserViewController: browserViewController) + .onAuthFlowCompleted { _ in + ProductTourManager.shared.signInFlowDidEnd() + } + .onError { _ in + ProductTourManager.shared.signInFlowDidEnd() + } + .login() + } + + // MARK: - State Queries + + var isLoggedIn: Bool { + authService.isLoggedIn + } + + var idToken: String? { + authService.idToken + } + + var accessToken: String? { + authService.accessToken + } + + var userProfile: UserProfile? { + authService.userProfile + } + + func renewCredentialsIfNeeded() async throws { + try await authService.renewCredentialsIfNeeded() + } +} diff --git a/firefox-ios/Client/Ecosia/Account/Auth/EcosiaAuthFlow.swift b/firefox-ios/Client/Ecosia/Account/Auth/EcosiaAuthFlow.swift new file mode 100644 index 0000000000000..2bfc87aad6cdf --- /dev/null +++ b/firefox-ios/Client/Ecosia/Account/Auth/EcosiaAuthFlow.swift @@ -0,0 +1,268 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia + +/// Result of authentication flow operations +public enum EcosiaAuthFlowResult { + case success + case failure(error: AuthError) +} + +/// Orchestrates complete authentication flows with invisible tab sessions +/// Provides core functionality for authentication operations +final class EcosiaAuthFlow { + + public enum FlowType { + case login + case logout + } + + // MARK: - Core Properties + + private let authService: EcosiaAuthenticationService + private weak var browserViewController: BrowserViewController? + private let type: FlowType + + // Active session (retained until completion) + private var activeSession: InvisibleTabSession? + + // MARK: - Initialization + + /// Initializes the auth flow coordinator + /// - Parameters: + /// - type: Type of authentication flow (login or logout) + /// - authService: Authentication service for auth operations + /// - browserViewController: Browser view controller for tab operations + init(type: FlowType, + authService: Ecosia.EcosiaAuthenticationService, + browserViewController: BrowserViewController) { + self.type = type + self.authService = authService + self.browserViewController = browserViewController + + EcosiaLogger.auth.info("AuthFlow initialized for \(type)") + } + + // MARK: - Public Core API + + /// Starts the login authentication flow + /// - Parameters: + /// - delayedCompletion: Delay before calling onNativeAuthCompleted + /// - onNativeAuthCompleted: Callback when Auth0 authentication completes + /// - onFlowCompleted: Callback when entire flow completes + /// - onError: Callback when authentication fails + /// - Returns: Result of the authentication operation + public func startLogin( + delayedCompletion: TimeInterval = 0.0, + onNativeAuthCompleted: (() -> Void)? = nil, + onFlowCompleted: ((Bool) -> Void)? = nil, + onError: ((AuthError) -> Void)? = nil + ) async -> EcosiaAuthFlowResult { + return await performAuthentication( + type: .login, + delayedCompletion: delayedCompletion, + onNativeAuthCompleted: onNativeAuthCompleted, + onFlowCompleted: onFlowCompleted, + onError: onError + ) + } + + /// Starts the logout authentication flow + /// - Parameters: + /// - delayedCompletion: Delay before calling onNativeAuthCompleted + /// - onNativeAuthCompleted: Callback when Auth0 authentication completes + /// - onFlowCompleted: Callback when entire flow completes + /// - onError: Callback when authentication fails + /// - Returns: Result of the authentication operation + public func startLogout( + delayedCompletion: TimeInterval = 0.0, + onNativeAuthCompleted: (() -> Void)? = nil, + onFlowCompleted: ((Bool) -> Void)? = nil, + onError: ((AuthError) -> Void)? = nil + ) async -> EcosiaAuthFlowResult { + return await performAuthentication( + type: .logout, + delayedCompletion: delayedCompletion, + onNativeAuthCompleted: onNativeAuthCompleted, + onFlowCompleted: onFlowCompleted, + onError: onError + ) + } + + // MARK: - Private Implementation + + private func performAuthentication(type: FlowType, + delayedCompletion: TimeInterval = 0.0, + onNativeAuthCompleted: (() -> Void)? = nil, + onFlowCompleted: ((Bool) -> Void)? = nil, + onError: ((AuthError) -> Void)? = nil + ) async -> EcosiaAuthFlowResult { + + EcosiaLogger.auth.info("Starting \(type) flow") + + do { + switch type { + case .login: + // Step 1: Native Auth0 authentication + try await performNativeAuthentication() + + // Step 2: Handle native auth completion callback + await handleNativeAuthCompleted( + delayedCompletion: delayedCompletion, + onNativeAuthCompleted: onNativeAuthCompleted + ) + + // Step 3: Session transfer and invisible tab flow + try await performSessionTransfer(onFlowCompleted: onFlowCompleted) + + case .logout: + // Step 1: Native Auth0 logout + try await performNativeLogout() + + // Step 2: Handle native logout completion callback + await handleNativeAuthCompleted( + delayedCompletion: delayedCompletion, + onNativeAuthCompleted: onNativeAuthCompleted + ) + + // Step 3: Session cleanup and invisible tab flow + try await performSessionCleanup(onFlowCompleted: onFlowCompleted) + } + + return .success + } catch { + let authError = mapToAuthError(error) + await handleAuthFailure(authError, onError: onError) + return .failure(error: authError) + } + } + + private func performNativeAuthentication() async throws { + // Debug: Simulate auth error if enabled + if UserDefaults.standard.bool(forKey: SimulateAuthErrorSetting.debugKey) { + EcosiaLogger.auth.info("🐛 [DEBUG] Simulating login error") + throw AuthError.authenticationFailed(NSError(domain: "EcosiaDebug", code: -1, userInfo: [NSLocalizedDescriptionKey: "Debug: Simulated authentication error"])) + } + + EcosiaLogger.auth.info("Performing native Auth0 authentication") + try await authService.login() + EcosiaLogger.auth.info("Native Auth0 authentication completed") + } + + private func performNativeLogout() async throws { + // Debug: Simulate auth error if enabled + if UserDefaults.standard.bool(forKey: SimulateAuthErrorSetting.debugKey) { + EcosiaLogger.auth.info("🐛 [DEBUG] Simulating logout error") + throw AuthError.sessionClearingFailed(NSError(domain: "EcosiaDebug", code: -1, userInfo: [NSLocalizedDescriptionKey: "Debug: Simulated logout error"])) + } + + EcosiaLogger.auth.info("Performing native Auth0 logout") + try await authService.logout() + EcosiaLogger.auth.info("Native Auth0 logout completed") + } + + @MainActor + private func handleNativeAuthCompleted(delayedCompletion: TimeInterval, + onNativeAuthCompleted: (() -> Void)?) async { + if delayedCompletion > 0 { + DispatchQueue.main.asyncAfter(deadline: .now() + delayedCompletion) { + onNativeAuthCompleted?() + } + } else { + onNativeAuthCompleted?() + } + } + + private func performSessionTransfer(onFlowCompleted: ((Bool) -> Void)?) async throws { + + guard let browserViewController = browserViewController else { + throw AuthError.authFlowConfigurationError("BrowserViewController not available") + } + + // Get session transfer URL + let signUpURL = EcosiaEnvironment.current.urlProvider.signUpURL + + EcosiaLogger.session.info("Retrieving session transfer token for SSO") + await authService.getSessionTransferToken() + + // Create invisible tab session (must be on main thread for UI operations) + EcosiaLogger.invisibleTabs.info("Creating invisible tab session for login") + let session = try await MainActor.run { + try InvisibleTabSession( + url: signUpURL, + browserViewController: browserViewController, + authService: authService, + timeout: 10.0 + ) + } + + // Retain session until completion + activeSession = session + + // Set up session cookies + session.setupSessionCookies() + + // Wait for session completion + await withCheckedContinuation { continuation in + session.startMonitoring { [weak self] success in + self?.activeSession = nil // Release session + EcosiaLogger.auth.info("Ecosia auth flow completed: \(success)") + onFlowCompleted?(success) + continuation.resume() + } + } + } + + private func performSessionCleanup(onFlowCompleted: ((Bool) -> Void)?) async throws { + + guard let browserViewController = browserViewController else { + throw AuthError.authFlowConfigurationError("BrowserViewController not available") + } + + // Get logout URL + let logoutURL = EcosiaEnvironment.current.urlProvider.logoutURL + + // Create invisible tab session for logout (must be on main thread for UI operations) + EcosiaLogger.invisibleTabs.info("Creating invisible tab session for logout") + let session = try await MainActor.run { + try InvisibleTabSession( + url: logoutURL, + browserViewController: browserViewController, + authService: authService, + timeout: 10.0 + ) + } + + // Retain session until completion + activeSession = session + + // Wait for session completion + await withCheckedContinuation { continuation in + session.startMonitoring { [weak self] success in + self?.activeSession = nil // Release session + EcosiaLogger.auth.info("Ecosia logout flow completed: \(success)") + onFlowCompleted?(success) + continuation.resume() + } + } + } + + @MainActor + private func handleAuthFailure(_ error: AuthError, + onError: ((AuthError) -> Void)?) async { + activeSession = nil + + EcosiaLogger.auth.error("Auth flow failed: \(error)") + onError?(error) + } + + private func mapToAuthError(_ error: Error) -> AuthError { + if let authError = error as? AuthError { + return authError + } + return AuthError.authFlowConfigurationError(error.localizedDescription) + } +} diff --git a/firefox-ios/Client/Ecosia/Account/Auth/InvisibleTabSession.swift b/firefox-ios/Client/Ecosia/Account/Auth/InvisibleTabSession.swift new file mode 100644 index 0000000000000..21be46dc980e7 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Account/Auth/InvisibleTabSession.swift @@ -0,0 +1,169 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia +import Common + +/// Encapsulates an invisible tab session with its monitoring and cleanup +/// Single responsibility: manage one invisible tab through its complete lifecycle +final class InvisibleTabSession: TabEventHandler { + + // MARK: - Properties + + private let tab: Tab + private let url: URL + private let timeout: TimeInterval + private weak var browserViewController: BrowserViewController? + private let authService: Ecosia.EcosiaAuthenticationService + + // State + private var isCompleted = false + private var completion: ((Bool) -> Void)? + + // MARK: - Initialization + + /// Creates an invisible tab session + /// - Parameters: + /// - url: URL to load in the tab + /// - browserViewController: Browser view controller for tab operations + /// - authService: Authentication service for session operations + /// - timeout: Fallback timeout for completion + init(url: URL, + browserViewController: BrowserViewController, + authService: Ecosia.EcosiaAuthenticationService, + timeout: TimeInterval = 10.0) throws { + self.url = url + self.browserViewController = browserViewController + self.authService = authService + self.timeout = timeout + + // Create the tab immediately + self.tab = try Self.createInvisibleTab(url: url, browserViewController: browserViewController) + + EcosiaLogger.invisibleTabs.info("InvisibleTabSession created for: \(url)") + } + + // MARK: - Session Management + + /// Sets up session cookies for the tab + func setupSessionCookies() { + guard let sessionCookie = authService.getSessionTokenCookie() else { + EcosiaLogger.cookies.notice("No session cookie available for tab") + return + } + + Task { @MainActor in + tab.webView?.configuration.websiteDataStore.httpCookieStore.setCookie(sessionCookie) + EcosiaLogger.cookies.info("Session cookie set for tab: \(self.tab.tabUUID)") + } + } + + /// Starts monitoring for session completion (page load + auth) + /// - Parameter completion: Called when session completes or times out + func startMonitoring(_ completion: @escaping (Bool) -> Void) { + self.completion = completion + + setupTabAutoCloseManager() + + EcosiaLogger.invisibleTabs.info("Starting session monitoring: \(tab.tabUUID)") + } + + // MARK: - Private Implementation + + private static func createInvisibleTab(url: URL, browserViewController: BrowserViewController) throws -> Tab { + let profile = browserViewController.profile + + guard let tabManager = browserViewController.tabManager as? LegacyTabManager else { + throw AuthError.authFlowConfigurationError("TabManager not available") + } + + // Create invisible tab + let newTab = Tab(profile: profile, isPrivate: false, windowUUID: tabManager.windowUUID) + newTab.url = url + newTab.isInvisible = true + + tabManager.configureTab(newTab, + request: URLRequest(url: url), + afterTab: nil, + flushToDisk: true, + zombie: false) + + // Mark as invisible in the manager + InvisibleTabManager.shared.markTabAsInvisible(newTab) + + EcosiaLogger.invisibleTabs.info("Invisible tab created: \(newTab.tabUUID)") + return newTab + } + + private func setupTabAutoCloseManager() { + // Ensure InvisibleTabAutoCloseManager has the TabManager reference + if let tabManager = browserViewController?.tabManager { + InvisibleTabAutoCloseManager.shared.setTabManager(tabManager) + } + + Task { @MainActor in + // Setup auto-close monitoring + InvisibleTabAutoCloseManager.shared.setupAutoCloseForTab( + self.tab, + on: .EcosiaAuthStateChanged, + timeout: self.timeout + ) + + // Register for tab close events + register(self, forTabEvents: .didClose) + } + } + + private func handleTabClosed() { + guard !isCompleted else { return } + isCompleted = true + + cleanup() + + EcosiaLogger.invisibleTabs.info("Session completed for tab: \(tab.tabUUID), success: true") + completion?(true) + } + + private func cleanup() { + // Cancel auto-close monitoring in InvisibleTabAutoCloseManager + InvisibleTabAutoCloseManager.shared.cancelAutoCloseForTab(tab.tabUUID) + } + + private func closeTab() { + guard let browserViewController = browserViewController else { + return + } + + let tabManager = browserViewController.tabManager + + // Remove from invisible tracking + InvisibleTabManager.shared.markTabAsVisible(tab) + + // Remove the tab + tabManager.removeTab(tab) { + EcosiaLogger.invisibleTabs.info("Tab closed: \(self.tab.tabUUID)") + } + } + + // MARK: - TabEventHandler + + var tabEventWindowResponseType: TabEventHandlerWindowResponseType { + // Only respond to events for the specific window this session belongs to + return .singleWindow(browserViewController?.tabManager.windowUUID ?? WindowUUID.unavailable) + } + + func tabDidClose(_ tab: Tab) { + // Only handle close events for our specific tab + guard tab.tabUUID == self.tab.tabUUID else { return } + handleTabClosed() + } + + // MARK: - Cleanup + + deinit { + cleanup() + EcosiaLogger.invisibleTabs.debug("InvisibleTabSession deallocated") + } +} diff --git a/firefox-ios/Client/Ecosia/Account/Auth/TabManagement/InvisibleTabManager.swift b/firefox-ios/Client/Ecosia/Account/Auth/TabManagement/InvisibleTabManager.swift new file mode 100644 index 0000000000000..c39bbf41c60d1 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Account/Auth/TabManagement/InvisibleTabManager.swift @@ -0,0 +1,89 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +final class InvisibleTabManager { + + // MARK: - Singleton + + static let shared = InvisibleTabManager() + + // MARK: - Private Properties + + private let queue = DispatchQueue(label: "ecosia.invisible.tabs", attributes: .concurrent) + private var _invisibleTabUUIDs: Set = [] + + // MARK: - Initialization + + private init() {} + + // MARK: - Public Interface + + /// Array of invisible tab UUIDs + var invisibleTabUUIDs: [TabUUID] { + return queue.sync { + Array(_invisibleTabUUIDs) + } + } + + /// Check if a tab is invisible + /// - Parameter tab: The tab to check + /// - Returns: True if the tab is invisible + func isTabInvisible(_ tab: Tab) -> Bool { + return queue.sync { + _invisibleTabUUIDs.contains(tab.tabUUID) + } + } + + /// Mark a tab as invisible + /// - Parameter tab: The tab to mark as invisible + func markTabAsInvisible(_ tab: Tab) { + queue.sync(flags: .barrier) { + _invisibleTabUUIDs.insert(tab.tabUUID) + } + } + + /// Mark a tab as visible + /// - Parameter tab: The tab to mark as visible + func markTabAsVisible(_ tab: Tab) { + queue.sync(flags: .barrier) { + _invisibleTabUUIDs.remove(tab.tabUUID) + } + } + + /// Get visible tabs from a collection + /// - Parameter tabs: Collection of tabs to filter + /// - Returns: Array of visible tabs + func getVisibleTabs(from tabs: [Tab]) -> [Tab] { + return queue.sync { + tabs.filter { !_invisibleTabUUIDs.contains($0.tabUUID) } + } + } + + /// Get invisible tabs from a collection + /// - Parameter tabs: Collection of tabs to filter + /// - Returns: Array of invisible tabs + func getInvisibleTabs(from tabs: [Tab]) -> [Tab] { + return queue.sync { + tabs.filter { _invisibleTabUUIDs.contains($0.tabUUID) } + } + } + + /// Clean up tracking for removed tabs + /// - Parameter existingTabUUIDs: Set of tab UUIDs that still exist + func cleanupRemovedTabs(existingTabUUIDs: Set) { + queue.sync(flags: .barrier) { + _invisibleTabUUIDs = _invisibleTabUUIDs.intersection(existingTabUUIDs) + } + } + + /// Clear all invisible tabs (useful for testing) + func clearAllInvisibleTabs() { + queue.sync(flags: .barrier) { + _invisibleTabUUIDs.removeAll() + } + } +} diff --git a/firefox-ios/Client/Ecosia/Account/Auth/TabManagement/TabAutoCloseManager.swift b/firefox-ios/Client/Ecosia/Account/Auth/TabManagement/TabAutoCloseManager.swift new file mode 100644 index 0000000000000..9af563424bc2a --- /dev/null +++ b/firefox-ios/Client/Ecosia/Account/Auth/TabManagement/TabAutoCloseManager.swift @@ -0,0 +1,348 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia + +/// Configuration for tab auto-close behavior +struct TabAutoCloseConfig { + /// Timeout for fallback closure if notification doesn't arrive + static let fallbackTimeout: TimeInterval = 10.0 + + /// Maximum number of invisible tabs that can be auto-closed simultaneously + static let maxConcurrentAutoCloseTabs: Int = 5 + + /// Debounce interval to prevent multiple rapid auto-close operations + static let debounceInterval: TimeInterval = 0.5 +} + +/// Manager for automatic closing of invisible tabs based on notifications +/// Handles authentication completion notifications and fallback timeouts +final class InvisibleTabAutoCloseManager { + + // MARK: - Properties + + /// Singleton instance for app-wide auto-close management + static let shared = InvisibleTabAutoCloseManager() + + /// Dictionary mapping tab UUIDs to their notification observers + private var authTabObservers: [String: NSObjectProtocol] = [:] + + /// Dictionary mapping tab UUIDs to their fallback timeout work items + private var fallbackTimeouts: [String: DispatchWorkItem] = [:] + + /// Queue for thread-safe access to observer dictionaries + private let observerQueue = DispatchQueue(label: "com.ecosia.tabAutoClose", attributes: .concurrent) + + /// Notification center for observing auth completion + private let notificationCenter: NotificationCenter + + /// Weak reference to tab manager for tab removal operations + private weak var tabManager: TabManager? + + // MARK: - Initialization + + /// Private initializer to enforce singleton pattern + /// - Parameter notificationCenter: Notification center for observing, defaults to default center + private init(notificationCenter: NotificationCenter = .default) { + self.notificationCenter = notificationCenter + } + + /// Injects tab manager dependency + /// - Parameter tabManager: Tab manager to use for tab operations + func setTabManager(_ tabManager: TabManager) { + self.tabManager = tabManager + } + + // MARK: - Auto-Close Setup + + /// Sets up automatic closing for a tab when authentication completes + /// - Parameters: + /// - tab: The tab to setup auto-close for + /// - notification: The notification name to observe for completion + /// - timeout: Custom timeout for fallback closure, defaults to config value + func setupAutoCloseForTab(_ tab: Tab, + on notification: Notification.Name = .EcosiaAuthStateChanged, + timeout: TimeInterval = TabAutoCloseConfig.fallbackTimeout) { + + guard tab.isInvisible else { + EcosiaLogger.invisibleTabs.notice("Attempted to setup auto-close for visible tab: \(tab.tabUUID)") + return + } + + EcosiaLogger.invisibleTabs.info("Setting up auto-close for tab: \(tab.tabUUID)") + + observerQueue.sync(flags: .barrier) { + cleanupObserver(for: tab.tabUUID) + createObserver(for: tab, notification: notification, timeout: timeout) + } + } + + /// Sets up automatic closing for multiple tabs + /// - Parameters: + /// - tabs: Array of tabs to setup auto-close for + /// - notification: The notification name to observe for completion + /// - timeout: Custom timeout for fallback closure + func setupAutoCloseForTabs(_ tabs: [Tab], + on notification: Notification.Name = .EcosiaAuthStateChanged, + timeout: TimeInterval = TabAutoCloseConfig.fallbackTimeout) { + + let invisibleTabs = tabs.filter { $0.isInvisible } + + guard invisibleTabs.count <= TabAutoCloseConfig.maxConcurrentAutoCloseTabs else { + EcosiaLogger.invisibleTabs.notice("Too many tabs for concurrent auto-close: \(invisibleTabs.count)") + return + } + + for tab in invisibleTabs { + setupAutoCloseForTab(tab, on: notification, timeout: timeout) + } + } + + // MARK: - Private Implementation + + /// Creates notification observer and fallback timeout for a tab + /// - Parameters: + /// - tab: The tab to create observer for + /// - notification: The notification name to observe + /// - timeout: Fallback timeout interval + private func createObserver(for tab: Tab, + notification: Notification.Name, + timeout: TimeInterval) { + + let tabUUID = tab.tabUUID + + // Create notification observer for auth completion + let observer = notificationCenter.addObserver( + forName: notification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.handleAuthenticationCompletion(for: tabUUID) + } + + // Store observer + authTabObservers[tabUUID] = observer + + // Set up page load monitoring for invisible tabs + setupPageLoadMonitoring(for: tab) + + // Create fallback timeout + let fallbackWorkItem = DispatchWorkItem { [weak self] in + EcosiaLogger.invisibleTabs.info("Fallback timeout reached for tab: \(tabUUID)") + self?.handleAuthenticationCompletion(for: tabUUID, isFallback: true) + } + + fallbackTimeouts[tabUUID] = fallbackWorkItem + + // Schedule fallback timeout + DispatchQueue.main.asyncAfter(deadline: .now() + timeout, execute: fallbackWorkItem) + + EcosiaLogger.invisibleTabs.info("Auto-close setup completed for tab: \(tabUUID)") + } + + /// Sets up page load monitoring for an invisible tab + /// - Parameter tab: The tab to monitor + private func setupPageLoadMonitoring(for tab: Tab) { + EcosiaLogger.invisibleTabs.debug("Setting up page load monitoring for tab: \(tab.tabUUID)") + EcosiaLogger.invisibleTabs.debug("Tab URL: \(tab.url?.absoluteString ?? "nil")") + EcosiaLogger.invisibleTabs.debug("WebView URL: \(tab.webView?.url?.absoluteString ?? "nil")") + + let pageLoadObserver = notificationCenter.addObserver( + forName: .OnLocationChange, + object: nil, + queue: .main + ) { [weak self] notification in + // Process immediately without delay to catch all events + self?.handlePageLoadCompletion(notification, for: tab) + } + + /* + We need to track both auth and pageload observers for each tab. + Using "_pageload" suffix lets us distinguish between the two types + when counting or cleaning up later. + */ + authTabObservers["\(tab.tabUUID)_pageload"] = pageLoadObserver + + EcosiaLogger.invisibleTabs.info("Page load monitoring setup for tab: \(tab.tabUUID)") + } + + /// Handles page load completion for invisible tabs + /// - Parameters: + /// - notification: The location change notification + /// - tab: The tab being monitored + private func handlePageLoadCompletion(_ notification: Notification, for tab: Tab) { + guard let userInfo = notification.userInfo, + let url = userInfo["url"] as? URL else { + return + } + + // Always log OnLocationChange events for debugging + EcosiaLogger.invisibleTabs.debug("OnLocationChange: \(url) (isPrivate: \(userInfo["isPrivate"] ?? "unknown"))") + + // Check if this is for our invisible tab by comparing webView + guard let tabWebView = tab.webView else { + EcosiaLogger.invisibleTabs.debug("Tab \(tab.tabUUID) has no webView") + return + } + + EcosiaLogger.invisibleTabs.info("Ecosia page load detected for invisible tab: \(url)") + // Wait a moment for any final redirects/auth to complete + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.handleAuthenticationCompletion(for: tab.tabUUID) + } + } + + /// Handles authentication completion by closing the tab + /// - Parameters: + /// - tabUUID: UUID of the tab to close + /// - isFallback: Whether this was triggered by fallback timeout + private func handleAuthenticationCompletion(for tabUUID: String, isFallback: Bool = false) { + observerQueue.async(flags: .barrier) { [weak self] in + guard let self = self else { return } + + self.cleanupObserver(for: tabUUID) + + // Close the tab on main queue + DispatchQueue.main.async { [weak self] in + self?.closeTab(with: tabUUID, isFallback: isFallback) + } + } + } + + /// Closes a tab with the given UUID + /// - Parameters: + /// - tabUUID: UUID of the tab to close + /// - isFallback: Whether this was triggered by fallback timeout + private func closeTab(with tabUUID: String, isFallback: Bool) { + guard let tabManager = tabManager else { + EcosiaLogger.invisibleTabs.notice("No tab manager available for closing tab: \(tabUUID)") + return + } + + guard let tab = tabManager.tabs.first(where: { $0.tabUUID == tabUUID }) else { + EcosiaLogger.invisibleTabs.notice("Tab not found for closing: \(tabUUID)") + return + } + + // Only close if tab is still invisible + guard tab.isInvisible else { + EcosiaLogger.invisibleTabs.notice("Tab is no longer invisible, skipping close: \(tabUUID)") + return + } + + EcosiaLogger.invisibleTabs.info("Closing tab: \(tabUUID) \(isFallback ? "(fallback)" : "")") + + // Remove the tab + tabManager.removeTab(tab) { [weak self] in + // Select appropriate tab if needed + self?.selectAppropriateTabAfterRemoval(tabManager: tabManager) + + // Clean up invisible tab tracking + tabManager.cleanupInvisibleTabTracking() + + EcosiaLogger.invisibleTabs.info("Tab closed successfully: \(tabUUID)") + } + } + + /// Selects an appropriate tab after removal if no tab is currently selected + /// - Parameter tabManager: Tab manager to use for selection + private func selectAppropriateTabAfterRemoval(tabManager: TabManager) { + guard tabManager.selectedTab == nil else { return } + + // Try to select the last visible normal tab + if let lastVisibleTab = tabManager.visibleNormalTabs.last { + tabManager.selectTab(lastVisibleTab) + EcosiaLogger.invisibleTabs.info("Selected last visible normal tab") + } else if let lastVisibleTab = tabManager.visibleTabs.last { + tabManager.selectTab(lastVisibleTab) + EcosiaLogger.invisibleTabs.info("Selected last visible tab") + } + } + + /// Cleans up observer and timeout for a tab UUID + /// - Parameter tabUUID: UUID of the tab to clean up + private func cleanupObserver(for tabUUID: String) { + // Remove auth notification observer + if let observer = authTabObservers[tabUUID] { + notificationCenter.removeObserver(observer) + authTabObservers.removeValue(forKey: tabUUID) + } + + // Remove page load notification observer + if let pageLoadObserver = authTabObservers["\(tabUUID)_pageload"] { + notificationCenter.removeObserver(pageLoadObserver) + authTabObservers.removeValue(forKey: "\(tabUUID)_pageload") + } + + // Cancel and remove fallback timeout + if let timeout = fallbackTimeouts[tabUUID] { + timeout.cancel() + fallbackTimeouts.removeValue(forKey: tabUUID) + } + } + + // MARK: - Public Cleanup + + /// Cancels auto-close for a specific tab + /// - Parameter tabUUID: UUID of the tab to cancel auto-close for + func cancelAutoCloseForTab(_ tabUUID: String) { + observerQueue.sync(flags: .barrier) { + cleanupObserver(for: tabUUID) + EcosiaLogger.invisibleTabs.info("Cancelled auto-close for tab: \(tabUUID)") + } + } + + /// Cancels auto-close for multiple tabs + /// - Parameter tabUUIDs: Array of tab UUIDs to cancel auto-close for + func cancelAutoCloseForTabs(_ tabUUIDs: [String]) { + for tabUUID in tabUUIDs { + cancelAutoCloseForTab(tabUUID) + } + } + + /// Cleans up all observers and timeouts + func cleanupAllObservers() { + observerQueue.sync(flags: .barrier) { + // Clean up all observers + for (tabUUID, observer) in authTabObservers { + notificationCenter.removeObserver(observer) + EcosiaLogger.invisibleTabs.info("Cleaned up observer for tab: \(tabUUID)") + } + + // Cancel all timeouts + for (tabUUID, timeout) in fallbackTimeouts { + timeout.cancel() + EcosiaLogger.invisibleTabs.info("Cancelled timeout for tab: \(tabUUID)") + } + + // Clear dictionaries + authTabObservers.removeAll() + fallbackTimeouts.removeAll() + + EcosiaLogger.invisibleTabs.info("All observers and timeouts cleaned up") + } + } + + /// Returns the current number of tabs being tracked for auto-close + var trackedTabCount: Int { + return observerQueue.sync { + /* + Each tab has two observers (auth + pageload), but we only want + to count tabs, not individual observers + */ + authTabObservers.keys.filter { !$0.contains("_pageload") }.count + } + } + + /// Returns the UUIDs of all tabs currently being tracked for auto-close + var trackedTabUUIDs: [String] { + return observerQueue.sync { + /* + Filter out the pageload observer keys to get just the tab UUIDs + */ + Array(authTabObservers.keys.filter { !$0.contains("_pageload") }) + } + } +} diff --git a/firefox-ios/Client/Ecosia/Bookmarks/BookmarksExchange.swift b/firefox-ios/Client/Ecosia/Bookmarks/BookmarksExchange.swift new file mode 100644 index 0000000000000..cadc41eaeeea4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Bookmarks/BookmarksExchange.swift @@ -0,0 +1,210 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Storage +import Common + +import class MozillaAppServices.BookmarkFolderData +import Ecosia + +protocol BookmarksExchangable { + func export(bookmarks: [Ecosia.BookmarkItem], in viewController: UIViewController, barButtonItem: UIBarButtonItem) async throws + func `import`(from fileURL: URL, in viewController: UIViewController) async throws +} + +final class BookmarksExchange: BookmarksExchangable { + + enum Error: Swift.Error { + case couldNotReadData + } + + private let profile: Profile + private lazy var dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .short + return formatter + }() + + init(profile: Profile) { + self.profile = profile + } + + @MainActor + func export(bookmarks: [Ecosia.BookmarkItem], in viewController: UIViewController, barButtonItem: UIBarButtonItem) async throws { + guard let view = viewController.view else { return } + + let theme = themeFromView(view: view) + + let activityIndicator = UIActivityIndicatorView(style: .medium) + activityIndicator.color = theme.colors.ecosia.brandPrimary + activityIndicator.startAnimating() + + let toast = SimpleToast() + + toast.ecosiaShowAlertWithText( + .localized(.exportingBookmarks), + image: .view(activityIndicator), + bottomContainer: view, + theme: theme, + bottomInset: view.layoutMargins.bottom + ) + + let serializer = BookmarkSerializer() + + let htmlExport = try await serializer.serializeBookmarks(bookmarks) + + let exportedBooksmarksUrl = FileManager.default.temporaryDirectory.appendingPathComponent("Bookmarks.html") + try htmlExport.data(using: .utf8)?.write(to: exportedBooksmarksUrl) + + let activityViewController = UIActivityViewController(activityItems: [exportedBooksmarksUrl], applicationActivities: nil) + activityViewController.popoverPresentationController?.barButtonItem = barButtonItem + activityViewController.completionWithItemsHandler = { _, completed, _, error in + guard completed, error == nil else { return } + SimpleToast().ecosiaShowAlertWithText( + .localized(.bookmarksExported), + image: .named("bookmarkSuccess"), + bottomContainer: view, + theme: theme, + bottomInset: view.layoutMargins.bottom + ) + } + viewController.present(activityViewController, animated: true) {} + } + + @MainActor + func `import`(from fileURL: URL, in viewController: UIViewController) async throws { + guard let view = viewController.view else { return } + let theme = themeFromView(view: view) + + let activityIndicator = UIActivityIndicatorView(style: .medium) + activityIndicator.color = theme.colors.ecosia.brandPrimary + activityIndicator.startAnimating() + + let toast = SimpleToast() + + toast.ecosiaShowAlertWithText( + .localized(.importingBookmarks), + image: .view(activityIndicator), + bottomContainer: view, + theme: theme, + bottomInset: view.layoutMargins.bottom + ) + + do { + let (data, _) = try await URLSession.shared.data(from: fileURL) + guard let html = String(data: data, encoding: .utf8) else { throw Error.couldNotReadData } + let parser = try BookmarkParser(html: html) + let bookmarks = try await parser.parseBookmarks() + try await importBookmarks(bookmarks, viewController: viewController, toast: toast) + Analytics.shared.bookmarksImportEnded(.success) + } catch { + Analytics.shared.bookmarksImportEnded(.error) + throw error + } + } + + private func importBookmarks( + _ bookmarks: [Ecosia.BookmarkItem], + viewController: UIViewController, + toast: SimpleToast + ) async throws { + /// create folder with date by import + let importGuid: GUID + + if await hasBookmarks() { + importGuid = try await createFolder( + parentGUID: .mobileBookmarksGUID, + title: .init(format: .localized(.importedBookmarkFolderName), dateFormatter.string(from: Date())) + ) + } else { + importGuid = .mobileBookmarksGUID + } + + try await processBookmarks(bookmarks, parentGUID: importGuid) + + await showImportSuccess(using: toast, in: viewController.view) + } + + @MainActor + private func showImportSuccess(using toast: SimpleToast, in view: UIView) async { + + SimpleToast().ecosiaShowAlertWithText( + .localized(.bookmarksImported), + image: .named("bookmarkSuccess"), + bottomContainer: view, + theme: themeFromView(view: view), + bottomInset: view.layoutMargins.bottom + ) + } + + private func hasBookmarks() async -> Bool { + await withCheckedContinuation { continuation in + profile.places.getBookmark(guid: .mobileBookmarksGUID).uponQueue(DispatchQueue.main) { result in + guard let bookmarkNode = result.successValue, + let bookmarkFolder = bookmarkNode as? BookmarkFolderData + else { + continuation.resume(with: .success(false)) + return + } + continuation.resume(with: .success(bookmarkFolder.isNonEmptyFolder)) + } + } + } + + private func processBookmarks(_ bookmarks: [Ecosia.BookmarkItem], parentGUID: GUID) async throws { + for bookmark in bookmarks { + switch bookmark { + case let .folder(title, children, _): + let subParentGuid = try await createFolder(parentGUID: parentGUID, title: title) + try await processBookmarks(children, parentGUID: subParentGuid) + case let .bookmark(title, url, _): + try await createBookmark(parentGUID: parentGUID, url: url, title: title) + } + } + } + + private func themeFromView(view: UIView) -> Theme { + let themeManager: ThemeManager = AppContainer.shared.resolve() + return themeManager.getCurrentTheme(for: view.currentWindowUUID) + } +} + +private extension BookmarksExchange { + @discardableResult + func createFolder(parentGUID: GUID, title: String, position: UInt32? = nil) async throws -> GUID { + try await withCheckedThrowingContinuation { continuation in + profile.places.createFolder(parentGUID: parentGUID, title: title, position: position) + .uponQueue(.main) { result in + switch result { + case let .success(guid): + continuation.resume(returning: guid) + case let .failure(error): + continuation.resume(throwing: error) + } + } + } + } + + @discardableResult + func createBookmark(parentGUID: GUID, url: String, title: String?, position: UInt32? = nil) async throws -> GUID { + try await withCheckedThrowingContinuation { continuation in + profile.places.createBookmark(parentGUID: parentGUID, url: url, title: title, position: position) + .uponQueue(.main) { result in + switch result { + case let .success(guid): + continuation.resume(returning: guid) + case let .failure(error): + continuation.resume(throwing: error) + } + } + } + } +} + +extension GUID { + static let mobileBookmarksGUID = "mobile______" +} diff --git a/firefox-ios/Client/Ecosia/Extensions/AppSettingsTableViewController+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/AppSettingsTableViewController+Ecosia.swift new file mode 100644 index 0000000000000..27954c35ab7b1 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/AppSettingsTableViewController+Ecosia.swift @@ -0,0 +1,225 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit +import Shared +import Ecosia + +extension AppSettingsTableViewController { + + func getEcosiaSettingsSectionsShowingDebug(_ isDebugSectionEnabled: Bool) -> [SettingSection] { + var sections = [ + getSearchSection(), + getCustomizationSection(), + getEcosiaGeneralSection(), + getEcosiaPrivacySection(), + getEcosiaSupportSection(), + getEcosiaAboutSection() + ] + + if User.shared.shouldShowDefaultBrowserSettingNudgeCard { + sections.insert(getEcosiaDefaultBrowserSection(), at: 0) + } + + if isDebugSectionEnabled { + sections.append(getEcosiaDebugSupportSection()) + sections.append(getEcosiaDebugUnleashSection()) + sections.append(getEcosiaDebugAccountsSection()) + } + + return sections + } +} + +extension AppSettingsTableViewController { + + // We need this section as a placeholder for the default browser nudge card. + private func getEcosiaDefaultBrowserSection() -> SettingSection { + .init(children: [DefaultBrowserSetting(theme: themeManager.getCurrentTheme(for: windowUUID))]) + } + + private func getSearchSection() -> SettingSection { + + let settings: [Setting] = [ + EcosiaDefaultBrowserSettings(), + SearchAreaSetting(settings: self), + SafeSearchSettings(settings: self), + AutoCompleteSettings(prefs: profile.prefs, theme: themeManager.getCurrentTheme(for: windowUUID)), + PersonalSearchSettings(prefs: profile.prefs, theme: themeManager.getCurrentTheme(for: windowUUID)), + AIOverviewsSearchSettings(prefs: profile.prefs, theme: themeManager.getCurrentTheme(for: windowUUID)) + ] + + return .init(title: .init(string: .localized(.search)), + children: settings) + } + + private func getCustomizationSection() -> SettingSection { + + var customizationSettings: [Setting] = [ + HomepageSettings(settings: self, settingsDelegate: settingsDelegate) + ] + + let inactiveTabsAreBuildActive = featureFlags.isFeatureEnabled(.inactiveTabs, checking: .buildOnly) + + if inactiveTabsAreBuildActive { + customizationSettings.append(TabsSetting(theme: themeManager.getCurrentTheme(for: windowUUID), settingsDelegate: parentCoordinator)) + } + + if isSearchBarLocationFeatureEnabled { + customizationSettings.append(SearchBarSetting(settings: self, settingsDelegate: parentCoordinator)) + } + + return .init(title: .init(string: .localized(.customization)), + children: customizationSettings) + } + + private func getEcosiaGeneralSection() -> SettingSection { + + let generalSettings: [Setting] = [ + OpenWithSetting(settings: self, settingsDelegate: parentCoordinator), + ThemeSetting(settings: self, settingsDelegate: parentCoordinator), + SiriPageSetting(settings: self, settingsDelegate: parentCoordinator), + BlockPopupSetting(settings: self), + NoImageModeSetting(settings: self), + BoolSetting( + prefs: profile.prefs, + theme: themeManager.getCurrentTheme(for: windowUUID), + prefKey: "showClipboardBar", + defaultValue: false, + titleText: .SettingsOfferClipboardBarTitle, + statusText: String(format: .SettingsOfferClipboardBarStatus, AppName.shortName.rawValue) + ), + BoolSetting( + prefs: profile.prefs, + theme: themeManager.getCurrentTheme(for: windowUUID), + prefKey: PrefsKeys.ContextMenuShowLinkPreviews, + defaultValue: true, + titleText: .SettingsShowLinkPreviewsTitle, + statusText: .SettingsShowLinkPreviewsStatus + ) + ] + + return .init(title: .init(string: .SettingsGeneralSectionTitle), + children: generalSettings) + } + + private func getEcosiaSupportSection() -> SettingSection { + let helpCenterSetting = HelpCenterSetting() + let sendFeedbackSetting = EcosiaSendFeedbackSetting(settings: self) + + return .init(title: NSAttributedString(string: .AppSettingsSupport), + children: [helpCenterSetting, sendFeedbackSetting]) + } + + private func getEcosiaPrivacySection() -> SettingSection { + let privacySettings = [ + PasswordManagerSetting(settings: self, settingsDelegate: parentCoordinator), + ClearPrivateDataSetting(settings: self, settingsDelegate: parentCoordinator), + EcosiaSendAnonymousUsageDataSetting(prefs: profile.prefs, theme: themeManager.getCurrentTheme(for: windowUUID)), + BoolSetting(prefs: profile.prefs, + theme: themeManager.getCurrentTheme(for: windowUUID), + prefKey: PrefsKeys.Settings.closePrivateTabs, + // Ecosia: Default value is different from Firefox + defaultValue: PrefsKeysDefaultValues.Settings.closePrivateTabs, + titleText: .AppSettingsClosePrivateTabsTitle, + statusText: .AppSettingsClosePrivateTabsDescription), + ContentBlockerSetting(settings: self, settingsDelegate: parentCoordinator), + EcosiaPrivacyPolicySetting(settings: self), + EcosiaTermsSetting(settings: self) + ] + + return .init(title: NSAttributedString(string: .AppSettingsPrivacyTitle), + children: privacySettings) + } + + private func getEcosiaAboutSection() -> SettingSection { + let aboutSettings = [ + AppStoreReviewSetting(settingsDelegate: parentCoordinator), + VersionSetting(settingsDelegate: self), + LicenseAndAcknowledgementsSetting(settingsDelegate: parentCoordinator), + ] + + return .init(title: NSAttributedString(string: .AppSettingsAbout), + children: aboutSettings) + } + + private func getEcosiaDebugSupportSection() -> SettingSection { + var hiddenDebugSettings: [Setting] = [ + ExportBrowserDataSetting(settings: self), + ForceCrashSetting(settings: self), + PushBackInstallation(settings: self), + OpenFiftyTabsDebugOption(settings: self, settingsDelegate: self), + ToggleDefaultBrowserPromo(settings: self), + ToggleImpactIntro(settings: self), + ResetOnboardingProductTour(settings: self), + CreateReferralCode(settings: self), + AddReferral(settings: self), + AddClaim(settings: self), + ChangeSearchCount(settings: self), + ResetSearchCount(settings: self), + ResetDefaultBrowserNudgeCard(settings: self), + FasterInactiveTabs(settings: self, settingsDelegate: self), + AnalyticsIdentifierSetting(settings: self) + ] + + if EcosiaEnvironment.current == .staging { + hiddenDebugSettings.append(AnalyticsStagingUrlSetting(settings: self)) + } + + return SettingSection(title: NSAttributedString(string: "Debug"), children: hiddenDebugSettings) + } + + private func getEcosiaDebugUnleashSection() -> SettingSection { + let unleashSettings: [Setting] = [ + UnleashBrazeIntegrationSetting(settings: self), + UnleashAISearchMVPSetting(settings: self), + UnleashOnboardingSetting(settings: self), + UnleashIdentifierSetting(settings: self) + ] + + return SettingSection(title: NSAttributedString(string: "Debug - Unleash"), children: unleashSettings) + } + + private func getEcosiaDebugAccountsSection() -> SettingSection { + let accountSettings: [Setting] = [ + ResetAccountImpactNudgeCard(settings: self), + DebugAddSeedsLoggedOut(settings: self), + DebugAddSeedsLoggedIn(settings: self), + DebugAddCustomSeeds(settings: self), + DebugForceLevelUp(settings: self), + SimulateAuthErrorSetting(settings: self), + SimulateImpactAPIErrorSetting(settings: self) + ] + + return SettingSection(title: NSAttributedString(string: "Debug - Accounts"), children: accountSettings) + } +} + +// MARK: - Default Browser Nudge Card helpers + +extension AppSettingsTableViewController { + + func isDefaultBrowserCell(_ section: Int) -> Bool { + settings[section].children.first?.accessibilityIdentifier == AccessibilityIdentifiers.Settings.DefaultBrowser.defaultBrowser + } + + func shouldShowDefaultBrowserNudgeCardInSection(_ section: Int) -> Bool { + isDefaultBrowserCell(section) && + User.shared.shouldShowDefaultBrowserSettingNudgeCard + } + + func hideDefaultBrowserNudgeCardInSection(_ section: Int) { + guard section < settings.count else { return } + self.settings.remove(at: section) + self.tableView.deleteSections(IndexSet(integer: section), with: .automatic) + } + + func showDefaultBrowserDetailView() { + DefaultBrowserCoordinator.makeDefaultCoordinatorAndShowDetailViewFrom(navigationController, + analyticsLabel: .settingsNudgeCard, + topViewContentBackground: EcosiaColor.DarkGreen50.color, + with: themeManager.getCurrentTheme(for: windowUUID)) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+Ecosia.swift new file mode 100644 index 0000000000000..8137847775211 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+Ecosia.swift @@ -0,0 +1,285 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SwiftUI +import Shared +import Ecosia + +// MARK: HomepageViewControllerDelegate +extension BrowserViewController: HomepageViewControllerDelegate { + func homeDidTapSearchButton(_ home: HomepageViewController) { + urlBar.tabLocationViewDidTapLocation(self.urlBar.locationView) + } +} + +// MARK: DefaultBrowserDelegate +extension BrowserViewController: DefaultBrowserDelegate { + @available(iOS 14, *) + func defaultBrowserDidShow(_ defaultBrowser: DefaultBrowserViewController) { + profile.prefs.setInt(1, forKey: PrefsKeys.IntroSeen) +// homepageViewController?.reloadTooltip() + } +} + +// MARK: WhatsNewViewDelegate +extension BrowserViewController: WhatsNewViewDelegate { + func whatsNewViewDidShow(_ viewController: WhatsNewViewController) { + whatsNewDataProvider.markPreviousVersionsAsSeen() +// homepageViewController?.reloadTooltip() + } +} + +// MARK: PageActionsShortcutsDelegate +extension BrowserViewController: PageActionsShortcutsDelegate { + func pageOptionsOpenHome() { + tabToolbarDidPressHome(toolbar, button: .init()) + dismiss(animated: true) + Analytics.shared.menuClick(.home) + } + + func pageOptionsNewTab() { + openBlankNewTab(focusLocationField: false) + dismiss(animated: true) + Analytics.shared.menuClick(.newTab) + } + + func pageOptionsSettings() { + homePanelDidRequestToOpenSettings(at: .general) + dismiss(animated: true) + Analytics.shared.menuClick(.settings) + } + + func pageOptionsShare() { + dismiss(animated: true) { + guard let item = self.menuHelper?.getSharingAction().items.first, + let handler = item.tapHandler else { return } + handler(item) + } + } +} + +// MARK: URL Bar +extension BrowserViewController { + + func updateURLBarFollowingPrivateModeUI() { + let isPrivate = tabManager.selectedTab?.isPrivate ?? false + urlBar.applyUIMode(isPrivate: isPrivate, theme: themeManager.getCurrentTheme(for: windowUUID)) + } +} + +// MARK: Present intro +extension BrowserViewController { + + func presentIntroViewController(_ alwaysShow: Bool = false) { + if showLoadingScreen(for: .shared) { + presentLoadingScreen() + } else if User.shared.firstTime { + handleFirstTimeUserActions() + } + } + + private func presentLoadingScreen() { + present(LoadingScreen(profile: profile, referrals: referrals, windowUUID: windowUUID, referralCode: User.shared.referrals.pendingClaim), animated: true) + } + + private func handleFirstTimeUserActions() { + User.shared.firstTime = false + User.shared.migrated = true + User.shared.hideBookmarksImportExportTooltip() + toolbarContextHintVC.deactivateHintForNewUsers() + } + + private func showLoadingScreen(for user: User) -> Bool { + user.referrals.pendingClaim != nil + } +} + +// MARK: Claim Referral +extension BrowserViewController { + + func openBlankNewTabAndClaimReferral(code: String) { + User.shared.referrals.pendingClaim = code + + // on first start, browser is not in view hierarchy yet + guard !User.shared.firstTime else { return } + popToBVC() + openURLInNewTab(nil, isPrivate: false) + // Intro logic will trigger claiming referral + presentIntroViewController() + } +} + +// MARK: Ecosia URL Detection and Handling + +extension BrowserViewController { + /// Detects Ecosia-specific URLs (auth, profile, etc.) and triggers native flows. + /// - Returns: `true` if the URL was handled and navigation should be cancelled, `false` otherwise + func detectAndHandleEcosiaURL(_ url: URL, for tab: Tab) -> Bool { + guard !tab.isInvisible else { return false } + + let interceptor = EcosiaURLInterceptor() + let interceptedType = interceptor.interceptedType(for: url) + + switch interceptedType { + case .signUp, .signIn: + return handleSignInAndSignUpDetection(url, tab: tab) + case .signOut: + return handleSignOutDetection(url) + case .profile: + return handleProfilePageDetection(url) + case .none: + return false + } + } + + private func handleSignInAndSignUpDetection(_ url: URL, tab: Tab) -> Bool { + guard let ecosiaAuth = ecosiaAuth else { + EcosiaLogger.auth.notice("No EcosiaAuth instance available for authentication detection") + return false + } + + if !ecosiaAuth.isLoggedIn { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Sign-up URL detected in navigation: \(url)") + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Triggering native authentication flow") + + ecosiaAuth + .onNativeAuthCompleted { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Native authentication completed from navigation detection") + } + .onAuthFlowCompleted { [weak self] success in + if success { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Complete authentication flow successful from navigation") + // Refresh the current page to reflect auth state changes + DispatchQueue.main.async { + self?.tabManager.selectedTab?.reload() + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Page refreshed after successful sign-in") + } + } else { + EcosiaLogger.auth.notice("🔐 [WEB-AUTH] Authentication flow completed with issues from navigation") + } + } + .onError { error in + EcosiaLogger.auth.error("🔐 [WEB-AUTH] Authentication failed from navigation: \(error)") + } + .login() + } else { + EcosiaLogger.auth.notice("🔐 [WEB-AUTH] Inconsistent state detected: web thinks user is logged out but native doesn't") + EcosiaLogger.auth.notice("🔐 [WEB-AUTH] Failing entire process to avoid user getting locked") + + ecosiaAuth + .onAuthFlowCompleted { _ in + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Logout completed to resolve inconsistency") + ecosiaAuth + .onAuthFlowCompleted { [weak self] success in + if success { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Re-authentication successful after resolving inconsistency") + // Refresh the current page to reflect auth state changes + DispatchQueue.main.async { + self?.tabManager.selectedTab?.reload() + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Page refreshed after inconsistency resolution") + } + } else { + EcosiaLogger.auth.error("🔐 [WEB-AUTH] Re-authentication failed after resolving inconsistency") + } + } + .onError { error in + EcosiaLogger.auth.error("🔐 [WEB-AUTH] Re-authentication error after resolving inconsistency: \(error)") + } + .login() + } + .onError { error in + EcosiaLogger.auth.error("🔐 [WEB-AUTH] Logout failed while resolving inconsistency: \(error)") + } + .logout() + } + + return true + } + + private func handleSignOutDetection(_ url: URL) -> Bool { + guard let ecosiaAuth = ecosiaAuth else { + EcosiaLogger.auth.notice("No EcosiaAuth instance available for sign-out detection") + return false + } + + // Always perform logout on web-triggered sign-out to clear inconsistent state + if !ecosiaAuth.isLoggedIn { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] User already logged out, but we perform logout anyways to clear inconsistent state for: \(url) as if it's triggered, it means the User sees the page and clicks logout. It may happen sometimes. Noticed especially on iPad.") + } + + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Sign-out URL detected in navigation: \(url)") + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Triggering native logout flow") + + ecosiaAuth + .onNativeAuthCompleted { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Native logout completed from navigation detection") + } + .onAuthFlowCompleted { [weak self] success in + if success { + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Complete logout flow successful from navigation") + + // Refresh the current page to reflect auth state changes + DispatchQueue.main.async { + self?.tabManager.selectedTab?.reload() + EcosiaLogger.auth.info("🔐 [WEB-AUTH] Page refreshed after successful sign-out") + } + } else { + EcosiaLogger.auth.notice("🔐 [WEB-AUTH] Logout flow completed with issues from navigation") + } + } + .onError { error in + EcosiaLogger.auth.error("🔐 [WEB-AUTH] Logout failed from navigation: \(error)") + } + .logout() + + return true + } + + private func handleProfilePageDetection(_ url: URL) -> Bool { + EcosiaLogger.auth.info("🔐 [WEB-PROFILE] Profile URL detected in navigation: \(url)") + EcosiaLogger.auth.info("🔐 [WEB-PROFILE] Opening native profile modal") + + DispatchQueue.main.async { [weak self] in + self?.presentProfileModal() + } + + return true + } +} + +// MARK: Profile Modal Presentation +extension BrowserViewController { + /// Presents the profile page as a modal, similar to EcosiaAccountImpactView + func presentProfileModal() { + guard #available(iOS 16.0, *) else { + EcosiaLogger.auth.notice("Profile modal requires iOS 16.0+") + return + } + + let profileView = EcosiaWebViewModal( + url: Environment.current.urlProvider.profileURL, + windowUUID: windowUUID, + userAgent: UserAgentBuilder.ecosiaMobileUserAgent().userAgent(), + onLoadComplete: { + Analytics.shared.accountProfileViewed() + }, + onDismiss: { + Analytics.shared.accountProfileDismissed() + } + ) + + let hostingController = UIHostingController(rootView: profileView) + hostingController.modalPresentationStyle = .pageSheet + + if let sheet = hostingController.sheetPresentationController { + sheet.detents = [.large()] + sheet.prefersGrabberVisible = true + } + + present(hostingController, animated: true) { + EcosiaLogger.auth.info("🔐 [WEB-PROFILE] Profile modal presented successfully") + } + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+EcosiaErrorHandling.swift b/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+EcosiaErrorHandling.swift new file mode 100644 index 0000000000000..d2dd9073853db --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+EcosiaErrorHandling.swift @@ -0,0 +1,98 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SwiftUI +import Common +import Ecosia + +/// Container view to host SwiftUI error toast in UIKit BrowserViewController +@available(iOS 16.0, *) +class EcosiaErrorToastContainerView: UIView { + private var hostingController: UIHostingController? + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .clear + isUserInteractionEnabled = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func show( + subtitle: String, + windowUUID: WindowUUID, + in viewController: UIViewController, + onDismiss: @escaping () -> Void + ) { + let toastView = EcosiaErrorToast( + subtitle: subtitle, + windowUUID: windowUUID, + onDismiss: { [weak self] in + self?.removeFromSuperview() + onDismiss() + } + ) + + let hostingController = UIHostingController(rootView: AnyView(toastView)) + hostingController.view.backgroundColor = .clear + self.hostingController = hostingController + + viewController.addChild(hostingController) + addSubview(hostingController.view) + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + hostingController.view.leadingAnchor.constraint(equalTo: leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: trailingAnchor), + hostingController.view.topAnchor.constraint(equalTo: topAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + + hostingController.didMove(toParent: viewController) + } +} + +@available(iOS 16.0, *) +extension BrowserViewController { + + /// Shows an error toast for auth flow failures + /// - Parameters: + /// - isLogin: Whether this was a login (true) or logout (false) error + func showAuthFlowErrorToast(isLogin: Bool, errorMessage: String? = nil) { + // Remove any existing error toast + view.subviews + .compactMap { $0 as? EcosiaErrorToastContainerView } + .forEach { $0.removeFromSuperview() } + + var subtitle = isLogin + ? String.localized(.signInErrorMessage) + : String.localized(.signOutErrorMessage) + + #if MOZ_CHANNEL_BETA + if let errorMessage { + subtitle += "Additional details: \(errorMessage)" + } + #endif + + let container = EcosiaErrorToastContainerView() + container.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(container) + + NSLayoutConstraint.activate([ + container.leadingAnchor.constraint(equalTo: view.leadingAnchor), + container.trailingAnchor.constraint(equalTo: view.trailingAnchor), + container.bottomAnchor.constraint(equalTo: bottomContentStackView.topAnchor) + ]) + + container.show( + subtitle: subtitle, + windowUUID: windowUUID, + in: self, + onDismiss: {} + ) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+EcosiaNavigationHandling.swift b/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+EcosiaNavigationHandling.swift new file mode 100644 index 0000000000000..d45a197630119 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/BrowserViewController+EcosiaNavigationHandling.swift @@ -0,0 +1,52 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import WebKit +import Ecosia + +// MARK: - Ecosia Web View Event Handling +extension BrowserViewController { + + /// Handles any Ecosia-specific tracking when a navigation action is allowed. + /// Stores a pending URL to be tracked at didCommit. + /// - Parameters: + /// - url: The URL being navigated to + /// - navigationAction: The navigation action that triggered this check + func ecosiaHandleNavigationAction(url: URL, navigationAction: WKNavigationAction) { + // Clear any stale pending tracking from a previous navigation + pendingInappSearchUrl = nil + + guard url.isEcosiaSearchVertical() else { return } + + // Back/forward navigations are suppressed: on web, bfcache keeps the page mounted so + // Vue never refires. Tab switching doesn't reach this delegate at all, so any other + // navigation type arriving here is a genuine user action and should always track. + guard navigationAction.navigationType != .backForward else { return } + + // Store the URL; the event fires in ecosiaHandleDidCommit when content starts rendering + pendingInappSearchUrl = url + } + + /// Fires the in-app search event when the web content starts to be received (didCommit). + /// This matches the timing of Vue's mounted event on web (DOM ready, before full page load). + /// - Parameter url: The URL that just committed + func ecosiaHandleDidCommit(url: URL) { + guard url == pendingInappSearchUrl else { return } + pendingInappSearchUrl = nil + Analytics.shared.inappSearch(url: url) + } + + /// Handles any tasks that should run after a page finishes loading. + /// - Parameter url: The URL that finished loading + func ecosiaHandlePageLoadCompletion(url: URL) { + if ProductTourManager.shared.isInProductTour { + if url.isEcosiaSearchVertical() { + ProductTourManager.shared.completeFirstSearchIfNeeded() + } else if url.isBrowser() && !url.isEcosia() { + ProductTourManager.shared.completeExternalWebsiteVisitIfNeeded() + } + } + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/DispatchQueueHelper+BuildChannel.swift b/firefox-ios/Client/Ecosia/Extensions/DispatchQueueHelper+BuildChannel.swift new file mode 100644 index 0000000000000..95048f9e7a220 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/DispatchQueueHelper+BuildChannel.swift @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common + +/// Executes a block of code on the main thread. +/// +/// - If the build channel is `.release`, the block is executed immediately. +/// - Otherwise, it is executed after a specified delay. This might be useful for QA testing. +/// +/// - Parameters: +/// - work: A closure to be executed on the main thread. +/// - delay: The time interval (in seconds) to delay the execution if the build channel is not `.release`. The default value is 5.0 seconds. +public func executeOnMainThreadWithDelayForNonReleaseBuild(execute work: @escaping @convention(block) () -> Swift.Void, + delayedBy delay: TimeInterval = 5.0) { + if BrowserKitInformation.shared.buildChannel == .release { + work() + } else { + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { + work() + } + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/ErrorPageHandler+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/ErrorPageHandler+Ecosia.swift new file mode 100644 index 0000000000000..ba0e27d4846d3 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/ErrorPageHandler+Ecosia.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// These strings modify the default title, message and button text +/// of the Firefox no connection error page +/// when the error is `NSURLErrorDomain` +/// more info in `ErrorPageHandler` + +extension ErrorPageHandler { + + var noConnectionErrorTitle: String { + .localized(.noConnectionNSURLErrorTitle) + } + + var noConnectionErrorMessage: String { + .localized(.noConnectionNSURLErrorMessage) + } + + var noConnectionErrorButtonTitle: String { + .localized(.noConnectionNSURLErrorRefresh) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/HomepageViewController+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/HomepageViewController+Ecosia.swift new file mode 100644 index 0000000000000..ce11820229d65 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/HomepageViewController+Ecosia.swift @@ -0,0 +1,127 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Ecosia + +protocol HomepageViewControllerDelegate: AnyObject { + func homeDidTapSearchButton(_ home: HomepageViewController) +} + +protocol SharedHomepageCellDelegate: AnyObject { + func openLink(url: URL) +} + +extension LegacyHomepageViewController: SharedHomepageCellDelegate { + func openLink(url: URL) { + homePanelDelegate?.homePanel(didSelectURL: url, visitType: .link, isGoogleTopSite: false) + } +} + +protocol SharedHomepageCellLayoutDelegate: AnyObject { + func invalidateLayout(at indexPaths: [IndexPath]) +} + +extension LegacyHomepageViewController: SharedHomepageCellLayoutDelegate { + func invalidateLayout(at indexPaths: [IndexPath]) { + let context = UICollectionViewLayoutInvalidationContext() + context.invalidateItems(at: indexPaths) + collectionView.collectionViewLayout.invalidateLayout(with: context) + } +} + +extension LegacyHomepageViewController: NTPTooltipDelegate { + func ntpTooltipTapped(_ tooltip: NTPTooltip?) { + handleTooltipTapped(tooltip) + } + + func ntpTooltipCloseTapped(_ tooltip: NTPTooltip?) { + handleTooltipTapped(tooltip) + } + + private func handleTooltipTapped(_ tooltip: NTPTooltip?) { + guard let ntpHighlight = NTPTooltip.highlight() else { return } + + UIView.animate(withDuration: 0.3) { + tooltip?.alpha = 0 + } completion: { [weak self] _ in + switch ntpHighlight { + case .gotClaimed, .successfulInvite: + User.shared.referrals.accept() + case .referralSpotlight: + Analytics.shared.referral(action: .open, label: .promo) + User.shared.hideReferralSpotlight() + case .collectiveImpactIntro: + User.shared.hideImpactIntro() + } + self?.reloadTooltip() + } + } + + func reloadTooltip() { + reloadView() + } +} + +extension LegacyHomepageViewController: NTPLibraryDelegate { + func libraryCellOpenBookmarks() { + homePanelDelegate?.homePanelDidRequestToOpenLibrary(panel: .bookmarks) + } + + func libraryCellOpenHistory() { + homePanelDelegate?.homePanelDidRequestToOpenLibrary(panel: .history) + } + + func libraryCellOpenReadlist() { + homePanelDelegate?.homePanelDidRequestToOpenLibrary(panel: .readingList) + } + + func libraryCellOpenDownloads() { + homePanelDelegate?.homePanelDidRequestToOpenLibrary(panel: .downloads) + } +} + +extension LegacyHomepageViewController: NTPImpactCellDelegate { + func impactCellButtonClickedWithInfo(_ info: ClimateImpactInfo) { + switch info { + case .referral: + let invite = MultiplyImpact(referrals: referrals, windowUUID: windowUUID) + invite.delegate = self + let nav = EcosiaNavigation(rootViewController: invite) + present(nav, animated: true) + default: + return + } + } +} + +extension LegacyHomepageViewController: NTPNewsCellDelegate { + func openSeeAllNews() { + let news = NewsController(items: viewModel.newsViewModel.items, + windowUUID: windowUUID) + news.delegate = self + let nav = EcosiaNavigation(rootViewController: news) + present(nav, animated: true) + Analytics.shared.navigation(.open, label: .news) + } +} + +extension LegacyHomepageViewController: NTPCustomizationCellDelegate { + func openNTPCustomizationSettings() { + Analytics.shared.ntpCustomisation(.click, label: .customize) + browserNavigationHandler?.show(settings: .homePage) + } +} + +extension LegacyHomepageViewController: NTPHeaderDelegate { + func headerOpenAISearch() { + openLink(url: Environment.current.urlProvider.aiSearch(origin: .ntp)) + } +} + +extension LegacyHomepageViewController: NTPFirstSearchCellViewModelDelegate { + func searchWithQuery(_ query: String) { + openLink(url: URL.ecosiaSearchWithQuery(query)) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/NumberFormatter+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/NumberFormatter+Ecosia.swift new file mode 100644 index 0000000000000..fb532d7857bba --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/NumberFormatter+Ecosia.swift @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension NumberFormatter { + static func ecosiaCurrency(withoutEuroSymbol: Bool = false) -> NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .currency + formatter.currencySymbol = withoutEuroSymbol ? "" : "€" + formatter.maximumFractionDigits = 0 + formatter.usesGroupingSeparator = true + formatter.currencyGroupingSeparator = "," + return formatter + } + + static func ecosiaDecimalNumberFormatter() -> NumberFormatter { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + formatter.usesGroupingSeparator = true + return formatter + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/PrefsKeys+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/PrefsKeys+Ecosia.swift new file mode 100644 index 0000000000000..4164915666ad2 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/PrefsKeys+Ecosia.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +/// This is based on `PrefsKeys`(at `Shared/Prefs`) and meant to store Ecosia's custom default values for preference keys +public struct PrefsKeysDefaultValues { + public struct Settings { + public static let closePrivateTabs = false + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/SearchViewController+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/SearchViewController+Ecosia.swift new file mode 100644 index 0000000000000..151afaaaa4c0c --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/SearchViewController+Ecosia.swift @@ -0,0 +1,132 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UIKit +import Common +import Ecosia + +// MARK: - AI Search Autocomplete Extensions +extension SearchViewController { + + // MARK: - Helper Methods + + func suggestionsCount() -> Int? { + let max = 4 // Taken from Firefox code (it was hardcoded there) + guard let count = viewModel.suggestions?.count else { + return nil + } + return min(count, max) + } + + /// Check if current row is the AI Search item + func isAISearchRow(_ indexPath: IndexPath) -> Bool { + guard SearchListSection(rawValue: indexPath.section) == .searchSuggestions else { return false } + let shouldShowAISearch = AISearchMVPExperiment.isEnabled && !viewModel.searchQuery.isEmpty + guard shouldShowAISearch, let lastIndex = suggestionsCount() else { return false } + return indexPath.row == lastIndex // Item after last suggestion (0-based index) + } + + /// Calculate number of rows including AI Search item if enabled + func numberOfRowsForSearchSuggestions() -> Int { + guard let count = suggestionsCount() else { return 0 } + let shouldShowAISearch = AISearchMVPExperiment.isEnabled && !viewModel.searchQuery.isEmpty + return shouldShowAISearch ? count + 1 : count + } + + // MARK: - AI Search Navigation + + /// Handle AI Search navigation when item is selected + func handleAISearchSelection(_ indexPath: IndexPath) { + let url = Environment.current.urlProvider.aiSearch(origin: .autocomplete) + let finalURL = url.appendingQueryItems([URLQueryItem(name: "q", value: viewModel.searchQuery)]) + + searchDelegate?.searchViewController(self, didSelectURL: finalURL, searchTerm: viewModel.searchQuery) + Analytics.shared.aiSearchAutocompleteForQuery(viewModel.searchQuery) + } + + // MARK: - AI Search Cell Configuration + + /// Configure AI Search cell appearance + func configureAISearchCell(_ cell: OneLineTableViewCell) -> OneLineTableViewCell { + let theme = themeManager.getCurrentTheme(for: windowUUID) + + cell.titleLabel.text = viewModel.searchQuery + + let aiSearchImage = UIImage(named: "searchLarge")?.withRenderingMode(.alwaysTemplate) + cell.leftImageView.contentMode = .center + cell.leftImageView.layer.borderWidth = 0 + cell.leftImageView.manuallySetImage(aiSearchImage ?? UIImage()) + cell.leftImageView.tintColor = theme.colors.ecosia.buttonBackgroundPrimary + cell.leftImageView.backgroundColor = nil + + let standardImageSize: CGFloat = OneLineTableViewCell.UX.leftImageViewSize + let dynamicImageSize = min(UIFontMetrics.default.scaledValue(for: standardImageSize), 2 * standardImageSize) + cell.leftImageView.widthAnchor.constraint(equalToConstant: dynamicImageSize).isActive = true + cell.leftImageView.heightAnchor.constraint(equalToConstant: dynamicImageSize).isActive = true + + let twinkleImageView = UIImageView() + twinkleImageView.image = UIImage(named: "ai-sparkle", in: .ecosia, with: nil)?.withRenderingMode(.alwaysTemplate) + twinkleImageView.tintColor = theme.colors.ecosia.textInversePrimary + twinkleImageView.contentMode = .scaleAspectFit + + let aiSearchLabel = UILabel() + aiSearchLabel.text = String.localized(.aiSearch) + aiSearchLabel.textColor = theme.colors.ecosia.textInversePrimary + aiSearchLabel.font = .preferredFont(forTextStyle: .caption1) + aiSearchLabel.sizeToFit() + + let twinkleSize: CGFloat = 16 + let internalPadding: CGFloat = 8 + let spacing: CGFloat = 2 + + // Create the actual pill container matching titleLabel height + let pillContainer = UIView() + pillContainer.backgroundColor = theme.colors.ecosia.buttonBackgroundPrimary + + let pillWidth = internalPadding + twinkleSize + spacing + aiSearchLabel.frame.width + internalPadding + // Ensure layout is up-to-date before reading frames + cell.layoutIfNeeded() + let pillHeight = cell.titleLabel.frame.height + internalPadding / 2 + + // Calculate Y position to center pill with leftImageView + let leftImageCenterY = cell.leftImageView.frame.midY + let pillY = leftImageCenterY - (pillHeight / 2) + + pillContainer.frame = CGRect(x: 0, y: pillY, width: pillWidth, height: pillHeight) + pillContainer.layer.cornerRadius = pillHeight / 2 + + twinkleImageView.frame = CGRect(x: internalPadding, y: (pillHeight - twinkleSize) / 2, width: twinkleSize, height: twinkleSize) + aiSearchLabel.frame = CGRect(x: internalPadding + twinkleSize + spacing, y: (pillHeight - aiSearchLabel.frame.height) / 2, width: aiSearchLabel.frame.width, height: aiSearchLabel.frame.height) + + pillContainer.addSubview(twinkleImageView) + pillContainer.addSubview(aiSearchLabel) + + cell.accessoryView = pillContainer + + return cell + } + + // MARK: - AI Search Highlighting + + /// Handle AI Search highlighting + func handleAISearchHighlight(_ indexPath: IndexPath) { + searchDelegate?.searchViewController(self, didHighlightText: viewModel.searchQuery, search: false) + } + + // MARK: - Safe Array Access + + /// Safely access suggestions array with bounds checking + func safeSuggestion(at index: Int) -> String? { + guard let suggestions = viewModel.suggestions, + index < min(suggestions.count, 4) else { return nil } + return suggestions[index] + } + + /// Check if index is valid for suggestions array access + func isValidSuggestionIndex(_ index: Int) -> Bool { + guard let suggestions = viewModel.suggestions else { return false } + return index < min(suggestions.count, 4) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/SimpleToast+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/SimpleToast+Ecosia.swift new file mode 100644 index 0000000000000..d18cac8e78635 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/SimpleToast+Ecosia.swift @@ -0,0 +1,87 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common +import Ecosia + +extension SimpleToast { + + enum AccessoryImage { + case named(String), view(UIView) + } + + @discardableResult + // Ecosia: Migrated to be able to customize the accessory image shown, as well as bottom inset + func ecosiaShowAlertWithText( + _ text: String, + image: AccessoryImage? = nil, + bottomContainer: UIView, + theme: Theme, + bottomInset: CGFloat? = nil + ) -> SimpleToast { + let toast = self.createView(text: text, image: image, theme: theme) + toast.layer.cornerRadius = .ecosia.borderRadius._l + toast.layer.masksToBounds = true + + bottomContainer.addSubview(toast) + toast.snp.makeConstraints { (make) in + make.left.equalTo(bottomContainer).offset(CGFloat(16)) + make.right.equalTo(bottomContainer).offset(-CGFloat(16)) + make.height.equalTo(Toast.UX.toastHeight) + make.bottom.equalTo(bottomContainer).offset(-((bottomInset ?? 0) + CGFloat(12))) + } + animate(toast) + return self + } + + fileprivate func createView(text: String, + image: AccessoryImage?, + theme: Theme) -> UIStackView { + let stack = UIStackView() + stack.axis = .horizontal + stack.alignment = .center + stack.distribution = .fill + stack.spacing = .ecosia.space._1s + stack.layer.cornerRadius = .ecosia.borderRadius._l + stack.backgroundColor = theme.colors.ecosia.buttonBackgroundPrimaryActive + + let toast = UILabel() + toast.text = text + toast.numberOfLines = 1 + toast.textColor = theme.colors.ecosia.textInversePrimary + // Ecosia: Use consistent font style with main implementation + toast.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .body, size: 17) + toast.adjustsFontForContentSizeCategory = true + toast.adjustsFontSizeToFitWidth = true + toast.textAlignment = .left + toast.setContentHuggingPriority(.defaultLow, for: .horizontal) + + let leftSpace = UIView() + leftSpace.widthAnchor.constraint(equalToConstant: .ecosia.space._1s).isActive = true + stack.addArrangedSubview(leftSpace) + stack.addArrangedSubview(toast) + let rightSpace = UIView() + rightSpace.widthAnchor.constraint(equalToConstant: .ecosia.space._1s).isActive = true + stack.addArrangedSubview(rightSpace) + + if let image { + let imageView: UIView + + switch image { + case let .named(name): + imageView = UIImageView(image: .init(named: name)?.withRenderingMode(.alwaysTemplate)) + imageView.tintColor = theme.colors.ecosia.iconInverseStrong + imageView.contentMode = .scaleAspectFit + imageView.setContentHuggingPriority(.required, for: .horizontal) + case let .view(view): + imageView = view + } + + stack.insertArrangedSubview(imageView, at: 1) + } + + return stack + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/SnapKit+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/SnapKit+Ecosia.swift new file mode 100644 index 0000000000000..d05b047ce99f3 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/SnapKit+Ecosia.swift @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapKit + +public extension ConstraintPriority { + + static var veryHigh: ConstraintPriority { + return .init(999) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/Tab+InvisibleTab.swift b/firefox-ios/Client/Ecosia/Extensions/Tab+InvisibleTab.swift new file mode 100644 index 0000000000000..e1bb23faf3a6b --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/Tab+InvisibleTab.swift @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Extension to add invisible tab capability to existing Tab class +/// Provides a convenient interface without modifying the core Tab implementation +extension Tab { + + /// Indicates whether this tab is invisible (hidden from user interface) + /// + /// When set to true, the tab will be excluded from: + /// - Tab count displays + /// - Tab switcher interfaces + /// - Inactive tab management + /// - Other UI representations + /// + /// This property is commonly used for authentication tabs and other + /// background operations that should not be visible to users. + var isInvisible: Bool { + get { + return InvisibleTabManager.shared.isTabInvisible(self) + } + set { + if newValue { + InvisibleTabManager.shared.markTabAsInvisible(self) + } else { + InvisibleTabManager.shared.markTabAsVisible(self) + } + } + } + + /// Convenience method to mark this tab as invisible + /// Equivalent to setting `isInvisible = true` + func markAsInvisible() { + isInvisible = true + } + + /// Convenience method to mark this tab as visible + /// Equivalent to setting `isInvisible = false` + func markAsVisible() { + isInvisible = false + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/TabManager+InvisibleTab.swift b/firefox-ios/Client/Ecosia/Extensions/TabManager+InvisibleTab.swift new file mode 100644 index 0000000000000..4897e9aa584d0 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/TabManager+InvisibleTab.swift @@ -0,0 +1,68 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Extension to add invisible tab filtering capabilities to existing TabManager +/// Provides filtered tab collections that exclude invisible tabs +extension TabManager { + + /// Returns the count of visible tabs only (excludes invisible tabs) + /// This should be used for UI display purposes instead of the raw `count` property + var visibleTabCount: Int { + return InvisibleTabManager.shared.getVisibleTabs(from: tabs).count + } + + /// Returns the count of invisible tabs only + var invisibleTabCount: Int { + return InvisibleTabManager.shared.getInvisibleTabs(from: tabs).count + } + + /// Returns only visible normal tabs (excludes private and invisible tabs) + /// This is the filtered equivalent of `normalTabs` + var visibleNormalTabs: [Tab] { + return InvisibleTabManager.shared.getVisibleTabs(from: normalTabs) + } + + /// Returns only visible normal active tabs (excludes private, inactive, and invisible tabs) + /// This is the filtered equivalent of `normalActiveTabs` + var visibleNormalActiveTabs: [Tab] { + return InvisibleTabManager.shared.getVisibleTabs(from: normalActiveTabs) + } + + /// Returns only visible private tabs (excludes invisible tabs) + /// This is the filtered equivalent of `privateTabs` + var visiblePrivateTabs: [Tab] { + return InvisibleTabManager.shared.getVisibleTabs(from: privateTabs) + } + + /// Returns only visible inactive tabs (excludes invisible tabs) + /// This is the filtered equivalent of `inactiveTabs` + var visibleInactiveTabs: [Tab] { + return InvisibleTabManager.shared.getVisibleTabs(from: inactiveTabs) + } + + /// Returns only visible recently accessed normal tabs (excludes invisible tabs) + /// This is the filtered equivalent of `recentlyAccessedNormalTabs` + var visibleRecentlyAccessedNormalTabs: [Tab] { + return InvisibleTabManager.shared.getVisibleTabs(from: recentlyAccessedNormalTabs) + } + + /// Returns all invisible tabs + var invisibleTabs: [Tab] { + return InvisibleTabManager.shared.getInvisibleTabs(from: tabs) + } + + /// Returns all visible tabs (the opposite of invisibleTabs) + var visibleTabs: [Tab] { + return InvisibleTabManager.shared.getVisibleTabs(from: tabs) + } + + /// Cleanup invisible tab tracking when tabs are removed + /// This should be called when tabs are removed to prevent memory leaks + func cleanupInvisibleTabTracking() { + let existingTabUUIDs = Set(tabs.map { $0.tabUUID }) + InvisibleTabManager.shared.cleanupRemovedTabs(existingTabUUIDs: existingTabUUIDs) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/Task+Sleep.swift b/firefox-ios/Client/Ecosia/Extensions/Task+Sleep.swift new file mode 100644 index 0000000000000..b85cc2cb8d535 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/Task+Sleep.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Task where Success == Never, Failure == Never { + /// Sleep for a given duration in seconds, with iOS 15 compatibility + /// When iOS 16+ becomes minimum, this can be replaced with Task.sleep(for: .seconds()) + static func sleep(duration: TimeInterval) async throws { + if #available(iOS 16.0, *) { + try await Task.sleep(for: .seconds(duration)) + } else { + try await Task.sleep(nanoseconds: UInt64(duration * 1_000_000_000)) + } + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/UIButton+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/UIButton+Ecosia.swift new file mode 100644 index 0000000000000..bea710e089e94 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/UIButton+Ecosia.swift @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +// Ecosia: extension kept from Today widget +extension UIButton { + func setBackgroundColor(_ color: UIColor, forState state: UIControl.State) { + let colorView = UIView(frame: CGRect(width: 1, height: 1)) + colorView.backgroundColor = color + + UIGraphicsBeginImageContext(colorView.bounds.size) + if let context = UIGraphicsGetCurrentContext() { + colorView.layer.render(in: context) + } + let colorImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + self.setBackgroundImage(colorImage, for: state) + } +} + +// Ecosia: kept on 122 -> 133 upgrade +extension UIButton { + public func setInsets( + forContentPadding contentPadding: UIEdgeInsets, + imageTitlePadding: CGFloat) { + let isLTR = effectiveUserInterfaceLayoutDirection == .leftToRight + + contentEdgeInsets = UIEdgeInsets( + top: contentPadding.top, + left: isLTR ? contentPadding.left : contentPadding.right + imageTitlePadding, + bottom: contentPadding.bottom, + right: isLTR ? contentPadding.right + imageTitlePadding : contentPadding.left + ) + + titleEdgeInsets = UIEdgeInsets( + top: 0, + left: isLTR ? imageTitlePadding : -imageTitlePadding, + bottom: 0, + right: isLTR ? -imageTitlePadding: imageTitlePadding + ) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/UIFont+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/UIFont+Ecosia.swift new file mode 100644 index 0000000000000..62aa6c213ad33 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/UIFont+Ecosia.swift @@ -0,0 +1,29 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +extension UIFont { + func withTraits(traits: UIFontDescriptor.SymbolicTraits) -> UIFont { + let descriptor = fontDescriptor.withSymbolicTraits(traits) + return UIFont(descriptor: descriptor!, size: 0) // size 0 means keep the size as it is + } + + func bold() -> UIFont { + return withTraits(traits: .traitBold) + } + + func semibold() -> UIFont { + return .systemFont(ofSize: pointSize, weight: .semibold) + } + + func italic() -> UIFont { + return withTraits(traits: .traitItalic) + } + + func monospace() -> UIFont { + let descriptor = fontDescriptor.addingAttributes( [.featureSettings: [[UIFontDescriptor.FeatureKey.featureIdentifier: kNumberSpacingType, .typeIdentifier: kMonospacedNumbersSelector]]]) + return UIFont(descriptor: descriptor, size: 0) + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/UIView+maskedCorners.swift b/firefox-ios/Client/Ecosia/Extensions/UIView+maskedCorners.swift new file mode 100644 index 0000000000000..413c59547e768 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/UIView+maskedCorners.swift @@ -0,0 +1,39 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension UIView { + private struct Positions: OptionSet { + static let top = Positions(rawValue: 1) + static let bottom = Positions(rawValue: 1 << 1) + let rawValue: Int8 + + static func derive(row: Int, totalCount: Int) -> Positions { + var pos = Positions() + if row == 0 { pos.insert(.top) } + if row == totalCount - 1 { pos.insert(.bottom) } + return pos + } + } + + /// Updates `layer.maskedCorners` based on the position of the view inside any sort of list. + /// It masks top left and right if the view is the first of the list and bottom left and right if the view is the last. + /// - Parameters: + /// - row: The position of the view. + /// - totalCount: The total count of views on the list. + func setMaskedCornersUsingPosition(row: Int, totalCount: Int) { + let pos = Positions.derive(row: row, totalCount: totalCount) + var masked: CACornerMask = [] + if pos.contains(.top) { + masked.formUnion(.layerMinXMinYCorner) + masked.formUnion(.layerMaxXMinYCorner) + } + if pos.contains(.bottom) { + masked.formUnion(.layerMinXMaxYCorner) + masked.formUnion(.layerMaxXMaxYCorner) + } + layer.maskedCorners = masked + } +} diff --git a/firefox-ios/Client/Ecosia/Extensions/URL+Ecosia.swift b/firefox-ios/Client/Ecosia/Extensions/URL+Ecosia.swift new file mode 100644 index 0000000000000..38eb3d5299cf4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Extensions/URL+Ecosia.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Shared + +extension URL { + + public var isHTTPS: Bool { + scheme == "https" + } + + /// This computed var is utilized to determine whether a Website is considered secure from the Ecosia's perspective + /// We use it mainly to define the UI that tells the user that the currently visited website is secure + /// When the URL page isn't loaded properly for some reason, we still lookup the website that is being loaded, and determine its security + /// so the end user alway have an idea of the website being loaded shown in the URL bar and avoid any UI misalignment showing the `warning` icon for a secure website + /// having issues to load + /// In case at least one of the flags evaluates to `true`, we consider the URL secure. + public var isSecure: Bool { + let isOriginalUrlFromErrorPageSecure = InternalURL(self)?.originalURLFromErrorPage?.isHTTPS == true + let internalUrlIsNotErrorPage = InternalURL(self)?.isErrorPage == false + let securityFlags = [isOriginalUrlFromErrorPageSecure, internalUrlIsNotErrorPage, isHTTPS, isReaderModeURL] + return securityFlags.first(where: { $0 == true }) ?? false + } +} diff --git a/firefox-ios/Client/Ecosia/Frontend/Home/EcosiaHomepageSectionType.swift b/firefox-ios/Client/Ecosia/Frontend/Home/EcosiaHomepageSectionType.swift new file mode 100644 index 0000000000000..096cae70efb4c --- /dev/null +++ b/firefox-ios/Client/Ecosia/Frontend/Home/EcosiaHomepageSectionType.swift @@ -0,0 +1,118 @@ +/* + Ecosia: This file replaces the one contained in Client/Frontend/Home + It is done so that we will have minimum conflicts when major updates are needed + */ + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +enum HomepageSectionType: Int, CaseIterable { + case header + case homepageHeader + case libraryShortcuts + case topSites + case impact + case news + case ntpCustomization + case firstSearch + + var cellIdentifier: String { + switch self { + case .header: + if #available(iOS 16.0, *) { + return NTPHeader.cellIdentifier + } else { + return "" // Fallback for iOS < 16.0 + } + case .homepageHeader: return NTPLogoCell.cellIdentifier + case .libraryShortcuts: return NTPLibraryCell.cellIdentifier + case .topSites: return "" // Top sites has more than 1 cell type, dequeuing is done through FxHomeSectionHandler protocol + case .impact: return NTPImpactCell.cellIdentifier + case .news: return NTPNewsCell.cellIdentifier + case .ntpCustomization: return NTPCustomizationCell.cellIdentifier + case .firstSearch: return NTPFirstSearchCell.cellIdentifier + } + } + + static var cellTypes: [ReusableCell.Type] { + var types: [ReusableCell.Type] = [] + + if #available(iOS 16.0, *) { + types.append(NTPHeader.self) + } + + types.append(contentsOf: [ + NTPLogoCell.self, + TopSiteItemCell.self, + EmptyTopSiteCell.self, + NTPLibraryCell.self, + NTPImpactCell.self, + NTPNewsCell.self, + NTPCustomizationCell.self, + NTPFirstSearchCell.self + ]) + + return types + } + + init(_ section: Int) { + self.init(rawValue: section)! + } +} + +// Ecosia +private let MinimumInsets: CGFloat = 16 +extension HomepageSectionType { + var customizableConfig: CustomizableNTPSettingConfig? { + switch self { + case .header, .homepageHeader, .libraryShortcuts, .ntpCustomization, .firstSearch: return nil + case .topSites: return .topSites + case .impact: return .climateImpact + case .news: return .ecosiaNews + } + } + + func sectionInsets(_ traits: UITraitCollection, + topSpacing: CGFloat = 0, + bottomSpacing: CGFloat = 32) -> NSDirectionalEdgeInsets { + switch self { + case .libraryShortcuts, .topSites, .impact, .news, .ntpCustomization, .firstSearch: + guard let window = UIApplication.shared.windows.first(where: \.isKeyWindow) else { + return NSDirectionalEdgeInsets(top: 0, + leading: MinimumInsets, + bottom: bottomSpacing, + trailing: MinimumInsets) + } + var horizontal: CGFloat = traits.horizontalSizeClass == .regular ? 100 : 0 + let safeAreaInsets = window.safeAreaInsets.left + horizontal += MinimumInsets + safeAreaInsets + + let orientation: UIInterfaceOrientation = window.windowScene?.interfaceOrientation ?? .portrait + + /* Ecosia: center layout in iphone landscape or regular size class */ + if traits.horizontalSizeClass == .regular || (orientation.isLandscape && traits.userInterfaceIdiom == .phone) { + horizontal = window.bounds.width / 4 + } + return NSDirectionalEdgeInsets(top: topSpacing, + leading: horizontal, + bottom: bottomSpacing, + trailing: horizontal) + case .homepageHeader, .header: + return .init(top: 0, leading: 0, bottom: 0, trailing: 0) + } + } +} + +// Ecosia Product Tour +extension HomepageSectionType { + var isProductTourSection: Bool { + switch self { + case .header, .homepageHeader, .firstSearch: true + case .libraryShortcuts, .topSites, .impact, .news, .ntpCustomization: false + } + } +} diff --git a/firefox-ios/Client/Ecosia/Frontend/Home/TopSites/Cell/EcosiaTopSiteItemCell.swift b/firefox-ios/Client/Ecosia/Frontend/Home/TopSites/Cell/EcosiaTopSiteItemCell.swift new file mode 100644 index 0000000000000..ab8f848e0bed6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Frontend/Home/TopSites/Cell/EcosiaTopSiteItemCell.swift @@ -0,0 +1,242 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +/* + Ecosia: This file replaces the one contained in Client/Frontend/Home/TopSites/Cell + It is done so that we will have minimum conflicts when major updates are needed + */ + +import Common +import Foundation +import Shared +import SiteImageView +import Storage +import UIKit +import Ecosia + +/// The TopSite cell that appears in the ASHorizontalScrollView. +class TopSiteItemCell: UICollectionViewCell, ReusableCell { + // MARK: - Variables + + private var homeTopSite: TopSite? + + struct UX { + static let titleOffset: CGFloat = 4 + static let iconSize = CGSize(width: 32, height: 32) + static let imageBackgroundSize = CGSize(width: 52, height: 52) + static let pinAlignmentSpacing: CGFloat = 2 + static let pinIconSize = CGSize(width: 12, height: 12) + static let textSafeSpace: CGFloat = 6 + static let bottomSpace: CGFloat = 8 + static let imageTopSpace: CGFloat = 12 + static let imageBottomSpace: CGFloat = 12 + static let imageLeadingTrailingSpace: CGFloat = 12 + static let titleFontSize: CGFloat = 12 + static let cellCornerRadius: CGFloat = 26 + static let iconCornerRadius: CGFloat = 4 + static let overlayColor = UIColor(white: 0.0, alpha: 0.25) + } + + private var rootContainer: UIView = .build { view in + view.backgroundColor = .clear + view.layer.cornerRadius = UX.cellCornerRadius + } + + lazy var imageView: FaviconImageView = .build { _ in } + + private lazy var pinImageView: UIImageView = .build { imageView in + imageView.image = UIImage.templateImageNamed(StandardImageIdentifiers.Small.pinBadgeFill) + imageView.isHidden = true + } + + private lazy var titleLabel: UILabel = .build { titleLabel in + titleLabel.textAlignment = .center + titleLabel.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .caption1, + size: UX.titleFontSize) + titleLabel.adjustsFontForContentSizeCategory = true + titleLabel.allowsDefaultTighteningForTruncation = true + titleLabel.lineBreakMode = .byTruncatingTail + titleLabel.numberOfLines = 2 + titleLabel.backgroundColor = .clear + titleLabel.setContentHuggingPriority(.required, for: .vertical) + titleLabel.setContentCompressionResistancePriority(.required, for: .vertical) + } + + private lazy var selectedOverlay: UIView = .build { selectedOverlay in + selectedOverlay.isHidden = true + selectedOverlay.layer.cornerRadius = UX.cellCornerRadius + selectedOverlay.backgroundColor = UX.overlayColor + } + + override var isSelected: Bool { + didSet { + selectedOverlay.isHidden = !isSelected + } + } + + override var isHighlighted: Bool { + didSet { + selectedOverlay.isHidden = !isHighlighted + } + } + + private var textColor: UIColor? + + // MARK: - Inits + + override init(frame: CGRect) { + super.init(frame: frame) + isAccessibilityElement = true + accessibilityIdentifier = AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell + + setupLayout() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func prepareForReuse() { + super.prepareForReuse() + + titleLabel.text = nil + pinImageView.isHidden = true + } + + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + super.touchesEnded(touches, with: event) + selectedOverlay.isHidden = true + } + + override func layoutSubviews() { + super.layoutSubviews() + + rootContainer.setNeedsLayout() + rootContainer.layoutIfNeeded() + + rootContainer.layer.shadowPath = UIBezierPath(roundedRect: rootContainer.bounds, + cornerRadius: UX.cellCornerRadius).cgPath + } + + // MARK: - Public methods + + func configure(_ topSite: TopSite, + position: Int, + theme: Theme, + textColor: UIColor?) { + homeTopSite = topSite + accessibilityLabel = topSite.accessibilityLabel + self.textColor = textColor + + let siteURLString = topSite.site.url + + titleLabel.text = topSite.title + var imageResource: SiteResource? + + if let site = topSite.site as? SponsoredTile, + let url = URL(string: site.imageURL, invalidCharacters: false) { + imageResource = .remoteURL(url: url) + } else if let site = topSite.site as? PinnedSite { + imageResource = site.faviconResource + } else if let site = topSite.site as? SuggestedSite { + imageResource = site.faviconResource + } + + let viewModel = FaviconImageViewModel(siteURLString: siteURLString, + siteResource: imageResource, + faviconCornerRadius: UX.iconCornerRadius) + imageView.setFavicon(viewModel) + + configurePinnedSite(topSite) + applyTheme(theme: theme) + } + + // MARK: - Setup Helper methods + + private func setupLayout() { + rootContainer.addSubview(imageView) + rootContainer.addSubview(pinImageView) + contentView.addSubview(selectedOverlay) + contentView.addSubview(titleLabel) + contentView.addSubview(rootContainer) + NSLayoutConstraint.activate([ + rootContainer.topAnchor.constraint(equalTo: contentView.topAnchor), + rootContainer.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + /* Ecosia: Update constraints + rootContainer.widthAnchor.constraint(greaterThanOrEqualToConstant: UX.imageBackgroundSize.width), + rootContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: UX.imageBackgroundSize.height), + */ + rootContainer.widthAnchor.constraint(equalToConstant: UX.imageBackgroundSize.width), + rootContainer.heightAnchor.constraint(equalToConstant: UX.imageBackgroundSize.height), + + imageView.topAnchor.constraint(equalTo: rootContainer.topAnchor, + constant: UX.imageTopSpace), + imageView.leadingAnchor.constraint(equalTo: rootContainer.leadingAnchor, + constant: UX.imageLeadingTrailingSpace), + imageView.trailingAnchor.constraint(equalTo: rootContainer.trailingAnchor, + constant: -UX.imageLeadingTrailingSpace), + imageView.bottomAnchor.constraint(equalTo: rootContainer.bottomAnchor, + constant: -UX.imageBottomSpace), + imageView.widthAnchor.constraint(equalToConstant: UX.iconSize.width), + imageView.heightAnchor.constraint(equalToConstant: UX.iconSize.height), + + titleLabel.topAnchor.constraint(equalTo: rootContainer.bottomAnchor, constant: UX.titleOffset), + titleLabel.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -UX.bottomSpace), + titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: UX.textSafeSpace), + titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -UX.textSafeSpace), + titleLabel.heightAnchor.constraint(equalToConstant: 10).priority(.defaultHigh), + + selectedOverlay.topAnchor.constraint(equalTo: rootContainer.topAnchor), + selectedOverlay.leadingAnchor.constraint(equalTo: rootContainer.leadingAnchor), + selectedOverlay.trailingAnchor.constraint(equalTo: rootContainer.trailingAnchor), + selectedOverlay.bottomAnchor.constraint(equalTo: rootContainer.bottomAnchor), + pinImageView.widthAnchor.constraint(equalToConstant: UX.pinIconSize.width), + pinImageView.heightAnchor.constraint(equalToConstant: UX.pinIconSize.height), + ]) + } + + private func configurePinnedSite(_ topSite: TopSite) { + guard topSite.isPinned else { return } + + pinImageView.isHidden = false + } + + private func configureSponsoredSite(_ topSite: TopSite) { + guard topSite.isSponsoredTile else { return } + } + + private func setupShadow(theme: Theme) { + rootContainer.layer.cornerRadius = UX.cellCornerRadius + rootContainer.layer.shadowPath = UIBezierPath(roundedRect: rootContainer.bounds, + cornerRadius: UX.cellCornerRadius).cgPath + rootContainer.layer.shadowColor = theme.colors.shadowDefault.cgColor + rootContainer.layer.shadowOpacity = HomepageViewModel.UX.shadowOpacity + rootContainer.layer.shadowOffset = HomepageViewModel.UX.shadowOffset + rootContainer.layer.shadowRadius = HomepageViewModel.UX.shadowRadius + } +} + +// MARK: ThemeApplicable +extension TopSiteItemCell: ThemeApplicable { + func applyTheme(theme: Theme) { + pinImageView.tintColor = textColor ?? theme.colors.iconPrimary + titleLabel.textColor = textColor ?? theme.colors.textPrimary + selectedOverlay.backgroundColor = theme.colors.layer5Hover.withAlphaComponent(0.25) + rootContainer.backgroundColor = theme.colors.ecosia.backgroundElevation1 + } +} + +// MARK: - Blurrable +extension TopSiteItemCell: Blurrable { + func adjustBlur(theme: Theme) { + if shouldApplyWallpaperBlur { + rootContainer.addBlurEffectWithClearBackgroundAndClipping(using: .systemThickMaterial) + } else { + // If blur is disabled set background color + rootContainer.removeVisualEffectView() + rootContainer.backgroundColor = theme.colors.layer5 + setupShadow(theme: theme) + } + } +} diff --git a/firefox-ios/Client/Ecosia/Network/WebsiteConnectionStatus.swift b/firefox-ios/Client/Ecosia/Network/WebsiteConnectionStatus.swift new file mode 100644 index 0000000000000..61268285d5d90 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Network/WebsiteConnectionStatus.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum WebsiteConnectionTypeStatus { + case secure + case unsecure +} diff --git a/firefox-ios/Client/Ecosia/PersistedGenerated/FxNimbus.swift b/firefox-ios/Client/Ecosia/PersistedGenerated/FxNimbus.swift new file mode 100644 index 0000000000000..5536b5f11945f --- /dev/null +++ b/firefox-ios/Client/Ecosia/PersistedGenerated/FxNimbus.swift @@ -0,0 +1,3094 @@ +// This file was autogenerated by the `nimbus-fml` crate. +// Trust me, you don't want to mess with it! +#if canImport(Foundation) + import Foundation +#endif +#if canImport(MozillaAppServices) + import MozillaAppServices +#endif +#if canImport(UIKit) + import UIKit +#endif + +/// +/// An object for safely accessing feature configuration from Nimbus. +/// +/// This is generated. +public class FxNimbus : FeatureManifestInterface { + public typealias Features = FxNimbusFeatures + + /// + /// This should be populated at app launch; this method of initializing features + /// will be removed in favor of the `initialize` function. + /// + public var api: FeaturesInterface? + + /// + /// This method should be called as early in the startup sequence of the app as possible. + /// This is to connect the Nimbus SDK (and thus server) with the `FxNimbus` + /// class. + /// + /// The lambda MUST be threadsafe in its own right. + public func initialize(with getSdk: @escaping () -> FeaturesInterface?) { + self.getSdk = getSdk + self.features.accountSettingsReduxFeature.with(sdk: getSdk) + self.features.addressAutofillEdit.with(sdk: getSdk) + self.features.bookmarkRefactorFeature.with(sdk: getSdk) + self.features.contextualHintFeature.with(sdk: getSdk) + self.features.creditCardAutofill.with(sdk: getSdk) + self.features.feltPrivacyFeature.with(sdk: getSdk) + self.features.firefoxSuggestFeature.with(sdk: getSdk) + self.features.generalAppFeatures.with(sdk: getSdk) + self.features.gleanServerKnobs.with(sdk: getSdk) + self.features.homepageRebuildFeature.with(sdk: getSdk) + self.features.homescreenFeature.with(sdk: getSdk) + self.features.loginAutofill.with(sdk: getSdk) + self.features.menuRefactorFeature.with(sdk: getSdk) + self.features.microsurveyFeature.with(sdk: getSdk) + self.features.nativeErrorPageFeature.with(sdk: getSdk) + self.features.nightModeFeature.with(sdk: getSdk) + self.features.onboardingFrameworkFeature.with(sdk: getSdk) + self.features.passwordGeneratorFeature.with(sdk: getSdk) + self.features.reduxSearchSettingsFeature.with(sdk: getSdk) + self.features.remoteTabManagement.with(sdk: getSdk) + self.features.search.with(sdk: getSdk) + self.features.shopping2023.with(sdk: getSdk) + self.features.splashScreen.with(sdk: getSdk) + self.features.spotlightSearch.with(sdk: getSdk) + self.features.tabTrayRefactorFeature.with(sdk: getSdk) + self.features.tabTrayFeature.with(sdk: getSdk) + self.features.toolbarRefactorFeature.with(sdk: getSdk) + self.features.trackingProtectionRefactor.with(sdk: getSdk) + self.features.zoomFeature.with(sdk: getSdk) + FxNimbusMessaging.shared.initialize(with: getSdk) + self.reinitialize() + } + + fileprivate lazy var getSdk: GetSdk = { [self] in self.api } + + /// + /// Represents all the features supported by Nimbus + /// + public let features = Features() + + public func getCoenrollingFeatureIds() -> [String] { + ["messaging"] + } + + /// Introspection utility method. + public func getFeature(featureId: String) -> FeatureHolderAny? { + switch featureId { + case "account-settings-redux-feature": return FeatureHolderAny(wrapping: features.accountSettingsReduxFeature) + case "address-autofill-edit": return FeatureHolderAny(wrapping: features.addressAutofillEdit) + case "bookmark-refactor-feature": return FeatureHolderAny(wrapping: features.bookmarkRefactorFeature) + case "contextual-hint-feature": return FeatureHolderAny(wrapping: features.contextualHintFeature) + case "credit-card-autofill": return FeatureHolderAny(wrapping: features.creditCardAutofill) + case "felt-privacy-feature": return FeatureHolderAny(wrapping: features.feltPrivacyFeature) + case "firefox-suggest-feature": return FeatureHolderAny(wrapping: features.firefoxSuggestFeature) + case "general-app-features": return FeatureHolderAny(wrapping: features.generalAppFeatures) + case "glean-server-knobs": return FeatureHolderAny(wrapping: features.gleanServerKnobs) + case "homepage-rebuild-feature": return FeatureHolderAny(wrapping: features.homepageRebuildFeature) + case "homescreenFeature": return FeatureHolderAny(wrapping: features.homescreenFeature) + case "login-autofill": return FeatureHolderAny(wrapping: features.loginAutofill) + case "menu-refactor-feature": return FeatureHolderAny(wrapping: features.menuRefactorFeature) + case "microsurvey-feature": return FeatureHolderAny(wrapping: features.microsurveyFeature) + case "native-error-page-feature": return FeatureHolderAny(wrapping: features.nativeErrorPageFeature) + case "night-mode-feature": return FeatureHolderAny(wrapping: features.nightModeFeature) + case "onboarding-framework-feature": return FeatureHolderAny(wrapping: features.onboardingFrameworkFeature) + case "password-generator-feature": return FeatureHolderAny(wrapping: features.passwordGeneratorFeature) + case "redux-search-settings-feature": return FeatureHolderAny(wrapping: features.reduxSearchSettingsFeature) + case "remote-tab-management": return FeatureHolderAny(wrapping: features.remoteTabManagement) + case "search": return FeatureHolderAny(wrapping: features.search) + case "shopping2023": return FeatureHolderAny(wrapping: features.shopping2023) + case "splash-screen": return FeatureHolderAny(wrapping: features.splashScreen) + case "spotlight-search": return FeatureHolderAny(wrapping: features.spotlightSearch) + case "tab-tray-refactor-feature": return FeatureHolderAny(wrapping: features.tabTrayRefactorFeature) + case "tabTrayFeature": return FeatureHolderAny(wrapping: features.tabTrayFeature) + case "toolbar-refactor-feature": return FeatureHolderAny(wrapping: features.toolbarRefactorFeature) + case "tracking-protection-refactor": return FeatureHolderAny(wrapping: features.trackingProtectionRefactor) + case "zoom-feature": return FeatureHolderAny(wrapping: features.zoomFeature) + default: return nil + } + } + + /// + /// All generated initialization code. Clients shouldn't need to override or call + /// this. + /// We put it in a separate method because we have to be quite careful about what order + /// the initialization happens in— e.g. when importing other FML files. + /// + private func reinitialize() { + + FxNimbusMessaging.shared.features.messaging.with(initializer: { variables, prefs in + Messaging( + variables, + prefs, + actions: ["OPEN_SETTINGS": "://deep-link?url=settings/general", "OPEN_SETTINGS_NEW_TAB": "://deep-link?url=settings/newtab", "OPEN_SETTINGS_HOMESCREEN": "://deep-link?url=settings/homepage", "OPEN_SETTINGS_WALLPAPERS": "://deep-link?url=settings/wallpaper", "OPEN_SETTINGS_EMAIL": "://deep-link?url=settings/mailto", "OPEN_SETTINGS_SEARCH_ENGINE": "://deep-link?url=settings/search", "OPEN_SETTINGS_PRIVACY": "://deep-link?url=settings/clear-private-data", "OPEN_SETTINGS_FXA": "://deep-link?url=settings/fxa", "OPEN_SETTINGS_THEME": "://deep-link?url=settings/theme", "OPEN_URL": "://open-url", "VIEW_BOOKMARKS": "://deep-link?url=homepanel/bookmarks", "VIEW_TOP_SITES": "://deep-link?url=homepanel/top-sites", "VIEW_READING_LIST": "://deep-link?url=homepanel/reading-list", "VIEW_HISTORY": "://deep-link?url=homepanel/history", "VIEW_DOWNLOADS": "://deep-link?url=homepanel/downloads", "ENABLE_PRIVATE_BROWSING": "://deep-link?url=homepanel/new-private-tab", "OPEN_NEW_TAB": "://deep-link?url=homepanel/new-tab", "MAKE_DEFAULT_BROWSER": "://deep-link?url=default-browser/system-settings", "MAKE_DEFAULT_BROWSER_WITH_TUTORIAL": "://deep-link?url=default-browser/tutorial"], + messageUnderExperiment: nil, + messages: ["default-browser": MessageData(action: "MAKE_DEFAULT_BROWSER_WITH_TUTORIAL", buttonLabel: "Default Browser/DefaultBrowserCard.Button.v2", exceptIfAny: ["I_AM_DEFAULT_BROWSER"], style: "FALLBACK", surface: .newTabCard, text: "Default Browser/DefaultBrowserCard.Description", title: "Default Browser/DefaultBrowserCard.Title", triggerIfAll: ["SUPPORTS_DEFAULT_BROWSER", "ON_FOURTH_LAUNCH_THIS_YEAR"]), "homepage-microsurvey-message": MessageData(buttonLabel: "Microsurvey/Microsurvey.Prompt.Button.v127", microsurveyConfig: MicrosurveyConfig(icon: "homeLarge", options: ["Microsurvey/Microsurvey.Survey.Options.VerySatisfied.v132", "Microsurvey/Microsurvey.Survey.Options.Satisfied.v132", "Microsurvey/Microsurvey.Survey.Options.Neutral.v132", "Microsurvey/Microsurvey.Survey.Options.Dissatisfied.v132", "Microsurvey/Microsurvey.Survey.Options.VeryDissatisfied.v132", "Microsurvey/Microsurvey.Survey.Options.NotApplicable.v132"], utmContent: "homepage"), style: "MICROSURVEY", surface: .microsurvey, text: "How satisfied are you with your Firefox homepage?", title: "Microsurvey/Microsurvey.Prompt.TitleLabel.v127", triggerIfAll: ["SECOND_HOMEPAGE_VIEW"]), "survey-surface-message": MessageData(action: "OPEN_URL", actionParams: ["url": "https://www.macrumors.com"], buttonLabel: "ResearchSurface/PrimaryButton.Label.v112", style: "SURVEY", surface: .survey, text: "ResearchSurface/Body.Text.v112", triggerIfAll: ["NEVER"])], + onControl: .showNextMessage, + styles: ["FALLBACK": StyleData(maxDisplayCount: 20, priority: 40), "DEFAULT": StyleData(maxDisplayCount: 5, priority: 50), "MICROSURVEY": StyleData(maxDisplayCount: 1, priority: 50), "NOTIFICATION": StyleData(maxDisplayCount: 1, priority: 50), "PERSISTENT": StyleData(maxDisplayCount: 20, priority: 50), "SURVEY": StyleData(maxDisplayCount: 10, priority: 55), "WARNING": StyleData(maxDisplayCount: 10, priority: 60), "URGENT": StyleData(maxDisplayCount: 10, priority: 100)], + triggers: ["ALWAYS": "true", "NEVER": "false", "USER_RECENTLY_INSTALLED": "days_since_install < 7", "USER_RECENTLY_UPDATED": "days_since_update < 7 && days_since_install != days_since_update", "USER_TIER_ONE_COUNTRY": "('US' in locale || 'GB' in locale || 'CA' in locale || 'DE' in locale || 'FR' in locale)", "USER_EN_SPEAKER": "'en' in locale", "USER_ES_SPEAKER": "'es' in locale", "USER_DE_SPEAKER": "'de' in locale", "USER_FR_SPEAKER": "'fr' in locale", "DEVICE_ANDROID": "os == 'Android'", "DEVICE_IOS": "os == 'iOS'", "DAY_1_AFTER_INSTALL": "days_since_install == 1", "DAY_2_AFTER_INSTALL": "days_since_install == 2", "DAY_3_AFTER_INSTALL": "days_since_install == 3", "DAY_4_AFTER_INSTALL": "days_since_install == 4", "DAY_5_AFTER_INSTALL": "days_since_install == 5", "MORE_THAN_24H_SINCE_INSTALLED_OR_UPDATED": "days_since_update >= 1", "NOT_LAUNCHED_YESTERDAY": "'app_cycle.foreground'|eventLastSeen('Days', 1) > 1", "AFTER_THREE_LAUNCHES_THIS_WEEK": "'app_cycle.foreground'|eventSum('Weeks', 1, 0) >= 3", "I_AM_DEFAULT_BROWSER": "is_default_browser", "I_AM_NOT_DEFAULT_BROWSER": "is_default_browser == false", "SUPPORTS_DEFAULT_BROWSER": "os_version|versionCompare('14.!') >= 0", "INACTIVE_NEW_USER": "is_inactive_new_user", "ALLOWED_TIPS_NOTIFICATIONS": "allowed_tips_notifications", "FXA_CURRENTLY_SIGNED_IN": "is_fxa_signed_in == true", "SECOND_HOMEPAGE_VIEW": "'homepage_viewed'|eventSum('Years', 4, 0) >= 2", "ON_FOURTH_LAUNCH_THIS_YEAR": "'app_cycle.foreground'|eventSum('Years', 1, 0) > 3"], + experiment: "{experiment}" + ) + }) + } + + /// + /// Refresh the cache of configuration objects. + /// + /// For performance reasons, the feature configurations are constructed once then cached. + /// This method is to clear that cache for all features configured with Nimbus. + /// + /// It must be called whenever the Nimbus SDK finishes the `applyPendingExperiments()` method. + /// + public func invalidateCachedValues() { + features.accountSettingsReduxFeature.with(cachedValue: nil) + features.addressAutofillEdit.with(cachedValue: nil) + features.bookmarkRefactorFeature.with(cachedValue: nil) + features.contextualHintFeature.with(cachedValue: nil) + features.creditCardAutofill.with(cachedValue: nil) + features.feltPrivacyFeature.with(cachedValue: nil) + features.firefoxSuggestFeature.with(cachedValue: nil) + features.generalAppFeatures.with(cachedValue: nil) + features.gleanServerKnobs.with(cachedValue: nil) + features.homepageRebuildFeature.with(cachedValue: nil) + features.homescreenFeature.with(cachedValue: nil) + features.loginAutofill.with(cachedValue: nil) + features.menuRefactorFeature.with(cachedValue: nil) + features.microsurveyFeature.with(cachedValue: nil) + features.nativeErrorPageFeature.with(cachedValue: nil) + features.nightModeFeature.with(cachedValue: nil) + features.onboardingFrameworkFeature.with(cachedValue: nil) + features.passwordGeneratorFeature.with(cachedValue: nil) + features.reduxSearchSettingsFeature.with(cachedValue: nil) + features.remoteTabManagement.with(cachedValue: nil) + features.search.with(cachedValue: nil) + features.shopping2023.with(cachedValue: nil) + features.splashScreen.with(cachedValue: nil) + features.spotlightSearch.with(cachedValue: nil) + features.tabTrayRefactorFeature.with(cachedValue: nil) + features.tabTrayFeature.with(cachedValue: nil) + features.toolbarRefactorFeature.with(cachedValue: nil) + features.trackingProtectionRefactor.with(cachedValue: nil) + features.zoomFeature.with(cachedValue: nil) + FxNimbusMessaging.shared.invalidateCachedValues() + } + + /// + /// A singleton instance of FxNimbus + /// + public static let shared = FxNimbus() +} + +public class FxNimbusFeatures { + /// This feature is for managing the roll out of the Account Settings Redux + /// implementation + /// + public lazy var accountSettingsReduxFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "account-settings-redux-feature") { variables, prefs in + AccountSettingsReduxFeature(variables, prefs) + } + }() + /// This property defines if the address editing is enabled in Settings + public lazy var addressAutofillEdit: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "address-autofill-edit") { variables, prefs in + AddressAutofillEdit(variables, prefs) + } + }() + /// The Feature for managing the roll out of the Bookmark refactor feature + /// + public lazy var bookmarkRefactorFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "bookmark-refactor-feature") { variables, prefs in + BookmarkRefactorFeature(variables, prefs) + } + }() + /// This set holds all features pertaining to contextual hints. + public lazy var contextualHintFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "contextual-hint-feature") { variables, prefs in + ContextualHintFeature(variables, prefs) + } + }() + /// This property defines the credit card autofill feature + public lazy var creditCardAutofill: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "credit-card-autofill") { variables, prefs in + CreditCardAutofill(variables, prefs) + } + }() + /// The feature that enhances private browsing mode + public lazy var feltPrivacyFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "felt-privacy-feature") { variables, prefs in + FeltPrivacyFeature(variables, prefs) + } + }() + /// Configuration for the Firefox Suggest feature. + public lazy var firefoxSuggestFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "firefox-suggest-feature") { variables, prefs in + FirefoxSuggestFeature(variables, prefs) + } + }() + /// The feature that contains feature flags for the entire application + public lazy var generalAppFeatures: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "general-app-features") { variables, prefs in + GeneralAppFeatures(variables, prefs) + } + }() + /// A feature that provides server-side configurations for Glean metrics (aka + /// Server Knobs). + public lazy var gleanServerKnobs: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "glean-server-knobs") { variables, prefs in + GleanServerKnobs(variables, prefs) + } + }() + /// This feature is for managing the roll out of the Homepage rebuild feature + /// + public lazy var homepageRebuildFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "homepage-rebuild-feature") { variables, prefs in + HomepageRebuildFeature(variables, prefs) + } + }() + /// The homescreen that the user goes to when they press home or new tab. + public lazy var homescreenFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "homescreenFeature") { variables, prefs in + HomescreenFeature(variables, prefs) + } + }() + /// This property defines the login autofill feature for automatically filling + /// in usernames and passwords. + public lazy var loginAutofill: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "login-autofill") { variables, prefs in + LoginAutofill(variables, prefs) + } + }() + /// Controls the menu refactor feature + /// + public lazy var menuRefactorFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "menu-refactor-feature") { variables, prefs in + MenuRefactorFeature(variables, prefs) + } + }() + /// A feature that shows the microsurvey for users to interact with and submit + /// responses. + /// + public lazy var microsurveyFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "microsurvey-feature") { variables, prefs in + MicrosurveyFeature(variables, prefs) + } + }() + /// This feature is for managing the roll out of the native error page feature + /// + public lazy var nativeErrorPageFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "native-error-page-feature") { variables, prefs in + NativeErrorPageFeature(variables, prefs) + } + }() + /// Describes the night mode feature's configuration + /// + public lazy var nightModeFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "night-mode-feature") { variables, prefs in + NightModeFeature(variables, prefs) + } + }() + /// The new onboarding framework feature that will allow onboarding to be + /// experimentable through initial experiments. + /// + public lazy var onboardingFrameworkFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "onboarding-framework-feature") { variables, prefs in + OnboardingFrameworkFeature(variables, prefs) + } + }() + /// Password Generator Feature + public lazy var passwordGeneratorFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "password-generator-feature") { variables, prefs in + PasswordGeneratorFeature(variables, prefs) + } + }() + /// This feature is for managing the roll out of redux on the search settings + /// screen + /// + public lazy var reduxSearchSettingsFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "redux-search-settings-feature") { variables, prefs in + ReduxSearchSettingsFeature(variables, prefs) + } + }() + /// Features that let users manage tabs on other devices that are connected to + /// the same Mozilla account. + /// + public lazy var remoteTabManagement: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "remote-tab-management") { variables, prefs in + RemoteTabManagement(variables, prefs) + } + }() + /// Configuring the functionality to do with search. This will be separated into + /// smaller sub-features in later releases. + /// + public lazy var search: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "search") { variables, prefs in + Search(variables, prefs) + } + }() + /// The configuration setting for the status of the Fakespot feature + /// + public lazy var shopping2023: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "shopping2023") { variables, prefs in + Shopping2023(variables, prefs) + } + }() + /// A feature that extends splash screen duration, allowing additional data + /// fetching time for the app's initial run. + /// + public lazy var splashScreen: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "splash-screen") { variables, prefs in + SplashScreen(variables, prefs) + } + }() + /// Add pages as items findable with Spotlight. + public lazy var spotlightSearch: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "spotlight-search") { variables, prefs in + SpotlightSearch(variables, prefs) + } + }() + /// This feature is for managing the roll out of the Tab Tray refactor feature + /// + public lazy var tabTrayRefactorFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "tab-tray-refactor-feature") { variables, prefs in + TabTrayRefactorFeature(variables, prefs) + } + }() + /// The tab tray screen that the user goes to when they open the tab tray. + public lazy var tabTrayFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "tabTrayFeature") { variables, prefs in + TabTrayFeature(variables, prefs) + } + }() + /// This feature is for managing the roll out of the Toolbar refactor feature + /// + public lazy var toolbarRefactorFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "toolbar-refactor-feature") { variables, prefs in + ToolbarRefactorFeature(variables, prefs) + } + }() + /// The Enhanced Tracking Protection refactor + /// + public lazy var trackingProtectionRefactor: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "tracking-protection-refactor") { variables, prefs in + TrackingProtectionRefactor(variables, prefs) + } + }() + /// The configuration for the status of the zoom feature + /// + public lazy var zoomFeature: FeatureHolder = { + FeatureHolder(FxNimbus.shared.getSdk, featureId: "zoom-feature") { variables, prefs in + ZoomFeature(variables, prefs) + } + }() +} + +// Public interface members begin here. + +/// This feature is for managing the roll out of the Account Settings Redux + /// implementation + /// +public class AccountSettingsReduxFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// Enables the feature + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension AccountSettingsReduxFeature: FMLFeatureInterface {} +/// This property defines if the address editing is enabled in Settings +public class AddressAutofillEdit: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let status: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + status: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + status: status)) + } + + enum CodingKeys: String, CodingKey { + case status = "status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(status, forKey: .status) + } + + /// If true, we will allow user to edit the address + public lazy var status: Bool = { + self._variables.getBool("status") ?? _defaults.status + }() + +} +extension AddressAutofillEdit: FMLFeatureInterface {} +/// The Feature for managing the roll out of the Bookmark refactor feature + /// +public class BookmarkRefactorFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// Enables the bookmark refactor feature + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension BookmarkRefactorFeature: FMLFeatureInterface {} +/// This set holds all features pertaining to contextual hints. +public class ContextualHintFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let featuresEnabled: [ContextualHint: Bool] + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + featuresEnabled: [ContextualHint: Bool] = [.toolbarHint: true] + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + featuresEnabled: featuresEnabled)) + } + + enum CodingKeys: String, CodingKey { + case featuresEnabled = "features-enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(featuresEnabled.mapKeysNotNull { $0.rawValue }, forKey: .featuresEnabled) + } + + /// This property provides a lookup table of whether specific contextual hints + /// are enabled. + /// + public lazy var featuresEnabled: [ContextualHint: Bool] = { + self._variables.getBoolMap("features-enabled")?.mapKeysNotNull(ContextualHint.enumValue).mergeWith(_defaults.featuresEnabled) ?? _defaults.featuresEnabled + }() + +} +extension ContextualHintFeature: FMLFeatureInterface {} +/// This property defines the credit card autofill feature +public class CreditCardAutofill: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let creditCardAutofillStatus: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + creditCardAutofillStatus: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + creditCardAutofillStatus: creditCardAutofillStatus)) + } + + enum CodingKeys: String, CodingKey { + case creditCardAutofillStatus = "credit-card-autofill-status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(creditCardAutofillStatus, forKey: .creditCardAutofillStatus) + } + + /// If true, we will allow user to use the credit autofill feature + public lazy var creditCardAutofillStatus: Bool = { + self._variables.getBool("credit-card-autofill-status") ?? _defaults.creditCardAutofillStatus + }() + +} +extension CreditCardAutofill: FMLFeatureInterface {} +/// The feature that enhances private browsing mode +public class FeltPrivacyFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let feltDeletionEnabled: Bool + let simplifiedUiEnabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + feltDeletionEnabled: Bool = true, + simplifiedUiEnabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + feltDeletionEnabled: feltDeletionEnabled, + simplifiedUiEnabled: simplifiedUiEnabled)) + } + + enum CodingKeys: String, CodingKey { + case feltDeletionEnabled = "felt-deletion-enabled" + case simplifiedUiEnabled = "simplified-ui-enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(feltDeletionEnabled, forKey: .feltDeletionEnabled) + try container.encode(simplifiedUiEnabled, forKey: .simplifiedUiEnabled) + } + + /// If true, enable Felt Deletion part of Felt Privacy + public lazy var feltDeletionEnabled: Bool = { + self._variables.getBool("felt-deletion-enabled") ?? _defaults.feltDeletionEnabled + }() + + /// If true, enable simplified UI part of Felt Privacy + public lazy var simplifiedUiEnabled: Bool = { + self._variables.getBool("simplified-ui-enabled") ?? _defaults.simplifiedUiEnabled + }() + +} +extension FeltPrivacyFeature: FMLFeatureInterface {} +/// Configuration for the Firefox Suggest feature. +public class FirefoxSuggestFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let availableSuggestionsTypes: [SuggestionType: Bool] + let status: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + availableSuggestionsTypes: [SuggestionType: Bool] = [.amp: true, .ampMobile: false, .wikipedia: true], + status: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + availableSuggestionsTypes: availableSuggestionsTypes, + status: status)) + } + + enum CodingKeys: String, CodingKey { + case availableSuggestionsTypes = "available-suggestions-types" + case status = "status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(availableSuggestionsTypes.mapKeysNotNull { $0.rawValue }, forKey: .availableSuggestionsTypes) + try container.encode(status, forKey: .status) + } + + /// A map of suggestion types to booleans that indicate whether or not the + /// provider should return suggestions of those types. + /// + public lazy var availableSuggestionsTypes: [SuggestionType: Bool] = { + self._variables.getBoolMap("available-suggestions-types")?.mapKeysNotNull(SuggestionType.enumValue).mergeWith(_defaults.availableSuggestionsTypes) ?? _defaults.availableSuggestionsTypes + }() + + /// Whether the feature is enabled. When Firefox Suggest is enabled, Firefox + /// will download and store new search suggestions in the background, and + /// show additional Search settings to control which suggestions appear + /// in the awesomebar. When Firefox Suggest is disabled, Firefox will not + /// download new suggestions, and hide the additional Search settings. + /// + public lazy var status: Bool = { + self._variables.getBool("status") ?? _defaults.status + }() + +} +extension FirefoxSuggestFeature: FMLFeatureInterface {} +/// The feature that contains feature flags for the entire application +public class GeneralAppFeatures: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let reportSiteIssue: GeneralFeature + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + reportSiteIssue: GeneralFeature = GeneralFeature(status: true) + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + reportSiteIssue: reportSiteIssue)) + } + + enum CodingKeys: String, CodingKey { + case reportSiteIssue = "report-site-issue" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(reportSiteIssue, forKey: .reportSiteIssue) + } + + /// This property defines whether or not the feature is enabled + public lazy var reportSiteIssue: GeneralFeature = { + self._variables.getVariables("report-site-issue")?.map(GeneralFeature.create)._mergeWith(_defaults.reportSiteIssue) ?? _defaults.reportSiteIssue + }() + +} +extension GeneralAppFeatures: FMLFeatureInterface {} +/// A feature that provides server-side configurations for Glean metrics (aka + /// Server Knobs). +public class GleanServerKnobs: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let metricsEnabled: [String: Bool] + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + metricsEnabled: [String: Bool] = [:] + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + metricsEnabled: metricsEnabled)) + } + + enum CodingKeys: String, CodingKey { + case metricsEnabled = "metrics-enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(metricsEnabled, forKey: .metricsEnabled) + } + + /// A map of metric base-identifiers to booleans representing the state of the + /// 'enabled' flag for that metric. + public lazy var metricsEnabled: [String: Bool] = { + self._variables.getBoolMap("metrics-enabled")?.mergeWith(_defaults.metricsEnabled) ?? _defaults.metricsEnabled + }() + +} +extension GleanServerKnobs: FMLFeatureInterface {} +/// This feature is for managing the roll out of the Homepage rebuild feature + /// +public class HomepageRebuildFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// If true, enables the feature + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension HomepageRebuildFeature: FMLFeatureInterface {} +/// The homescreen that the user goes to when they press home or new tab. +public class HomescreenFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let preferSwitchToOpenTab: Bool + let sectionsEnabled: [HomeScreenSection: Bool] + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + preferSwitchToOpenTab: Bool = true, + sectionsEnabled: [HomeScreenSection: Bool] = [.jumpBackIn: true, .recentExplorations: false] + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + preferSwitchToOpenTab: preferSwitchToOpenTab, + sectionsEnabled: sectionsEnabled)) + } + + enum CodingKeys: String, CodingKey { + case preferSwitchToOpenTab = "prefer-switch-to-open-tab" + case sectionsEnabled = "sections-enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(preferSwitchToOpenTab, forKey: .preferSwitchToOpenTab) + try container.encode(sectionsEnabled.mapKeysNotNull { $0.rawValue }, forKey: .sectionsEnabled) + } + + /// Enables the feature to automatically switch to an existing tab with the same + /// content instead of opening a new one. + /// + public lazy var preferSwitchToOpenTab: Bool = { + self._variables.getBool("prefer-switch-to-open-tab") ?? _defaults.preferSwitchToOpenTab + }() + + /// This property provides a lookup table of whether or not the given section + /// should be enabled. If the section is enabled, it should be toggleable + /// in the settings screen, and on by default. + /// + public lazy var sectionsEnabled: [HomeScreenSection: Bool] = { + self._variables.getBoolMap("sections-enabled")?.mapKeysNotNull(HomeScreenSection.enumValue).mergeWith(_defaults.sectionsEnabled) ?? _defaults.sectionsEnabled + }() + +} +extension HomescreenFeature: FMLFeatureInterface {} +/// This property defines the login autofill feature for automatically filling + /// in usernames and passwords. +public class LoginAutofill: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let loginAutofillStatus: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + loginAutofillStatus: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + loginAutofillStatus: loginAutofillStatus)) + } + + enum CodingKeys: String, CodingKey { + case loginAutofillStatus = "login-autofill-status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(loginAutofillStatus, forKey: .loginAutofillStatus) + } + + /// If true, allows the user to use the login autofill feature for usernames + /// and passwords. + public lazy var loginAutofillStatus: Bool = { + self._variables.getBool("login-autofill-status") ?? _defaults.loginAutofillStatus + }() + +} +extension LoginAutofill: FMLFeatureInterface {} +/// Controls the menu refactor feature + /// +public class MenuRefactorFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + let menuHint: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false, + menuHint: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled, + menuHint: menuHint)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + case menuHint = "menu-hint" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + try container.encode(menuHint, forKey: .menuHint) + } + + /// Controls which menu users will see + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + + /// If true, enables the menu contextual hint. + /// + public lazy var menuHint: Bool = { + self._variables.getBool("menu-hint") ?? _defaults.menuHint + }() + +} +extension MenuRefactorFeature: FMLFeatureInterface {} +/// A feature that shows the microsurvey for users to interact with and submit + /// responses. + /// +public class MicrosurveyFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// If true, the feature is active. + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension MicrosurveyFeature: FMLFeatureInterface {} +/// This feature is for managing the roll out of the native error page feature + /// +public class NativeErrorPageFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// If true, the feature is active. + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension NativeErrorPageFeature: FMLFeatureInterface {} +/// Describes the night mode feature's configuration + /// +public class NightModeFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// Whether night mode is available for users or not + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension NightModeFeature: FMLFeatureInterface {} +/// The new onboarding framework feature that will allow onboarding to be + /// experimentable through initial experiments. + /// +public class OnboardingFrameworkFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let cards: [String: NimbusOnboardingCardData] + let conditions: [String: String] + let dismissable: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + cards: [String: NimbusOnboardingCardData] = ["welcome": NimbusOnboardingCardData(body: "Onboarding/Onboarding.Welcome.Description.TreatementA.v120", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .openInstructionsPopup, title: "Onboarding/Onboarding.Welcome.ActionTreatementA.v114"), secondary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.Welcome.Skip.v114")), cardType: .basic, image: .welcomeGlobe, instructionsPopup: NimbusOnboardingInstructionPopup(buttonAction: .openIosFxSettings, buttonTitle: "Onboarding/DefaultBrowserPopup.ButtonTitle.v114", instructions: ["Onboarding/DefaultBrowserPopup.FirstLabel.v114", "Onboarding/DefaultBrowserPopup.SecondLabel.v114", "Onboarding/DefaultBrowserPopup.ThirdLabel.v114"], title: "Onboarding/DefaultBrowserPopup.Title.v114"), link: NimbusOnboardingLink(title: "Onboarding/Onboarding.Welcome.Link.Action.v114", url: "https://www.mozilla.org/privacy/firefox/"), onboardingType: .freshInstall, order: 10, prerequisites: ["ALWAYS"], title: "Onboarding/Onboarding.Welcome.Title.TreatementA.v120"), "notification-permissions": NimbusOnboardingCardData(body: "Onboarding/Onboarding.Notification.Description.v120", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .requestNotifications, title: "Onboarding/Onboarding.Notification.TurnOnNotifications.Action.v114"), secondary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.Notification.Skip.Action.v115")), cardType: .basic, image: .notifications, onboardingType: .freshInstall, order: 30, prerequisites: ["ALWAYS"], title: "Onboarding/Onboarding.Notification.Title.v120"), "sign-to-sync": NimbusOnboardingCardData(body: "Onboarding/Onboarding.Sync.Description.v123", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .syncSignIn, title: "Onboarding/Onboarding.Sync.SignIn.Action.v114"), secondary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.Sync.Skip.Action.v114")), cardType: .basic, image: .syncDevices, onboardingType: .freshInstall, order: 20, prerequisites: ["ALWAYS"], title: "Onboarding/Onboarding.Sync.Title.v120"), "customization-theme": NimbusOnboardingCardData(body: "Onboarding/Onboarding.Customization.Theme.Description.v123", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.Customization.Theme.Continue.Action.v123")), cardType: .multipleChoice, image: .themeing, multipleChoiceButtons: [NimbusOnboardingMultipleChoiceButton(action: .themeSystemDefault, image: .themeSystem, title: "Onboarding/Onboarding.Customization.Theme.System.Action.v123"), NimbusOnboardingMultipleChoiceButton(action: .themeLight, image: .themeLight, title: "Onboarding/Onboarding.Customization.Theme.Light.Action.v123"), NimbusOnboardingMultipleChoiceButton(action: .themeDark, image: .themeDark, title: "Onboarding/Onboarding.Customization.Theme.Dark.Action.v123")], onboardingType: .freshInstall, order: 40, prerequisites: ["ALWAYS"], title: "Onboarding/Onboarding.Customization.Theme.Title.v123"), "customization-toolbar": NimbusOnboardingCardData(body: "Onboarding/Onboarding.Customization.Toolbar.Description.v123", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.Customization.Toolbar.Continue.Action.v123")), cardType: .multipleChoice, image: .toolbar, multipleChoiceButtons: [NimbusOnboardingMultipleChoiceButton(action: .toolbarTop, image: .toolbarTop, title: "Onboarding/Onboarding.Customization.Toolbar.Top.Action.v123"), NimbusOnboardingMultipleChoiceButton(action: .toolbarBottom, image: .toolbarBottom, title: "Onboarding/Onboarding.Customization.Toolbar.Bottom.Action.v123")], onboardingType: .freshInstall, order: 41, prerequisites: ["ALWAYS"], title: "Onboarding/Onboarding.Customization.Toolbar.Title.v123"), "update-welcome": NimbusOnboardingCardData(body: "Upgrade/Upgrade.Welcome.Description.v114", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .forwardOneCard, title: "Upgrade/Upgrade.Welcome.Action.v114")), cardType: .basic, image: .welcomeGlobe, onboardingType: .upgrade, order: 10, prerequisites: ["NEVER"], title: "Upgrade/Upgrade.Welcome.Title.v114"), "update-sign-to-sync": NimbusOnboardingCardData(body: "Upgrade/Upgrade.SyncSign.Description.v114", buttons: NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .syncSignIn, title: "Upgrade/Upgrade.SyncSign.Action.v114"), secondary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.LaterAction.v114")), cardType: .basic, image: .syncDevices, onboardingType: .upgrade, order: 20, prerequisites: ["NEVER"], title: "Upgrade/Upgrade.SyncSign.Title.v114")], + conditions: [String: String] = ["ALWAYS": "true", "NEVER": "false"], + dismissable: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + cards: cards, + conditions: conditions, + dismissable: dismissable)) + } + + enum CodingKeys: String, CodingKey { + case cards = "cards" + case conditions = "conditions" + case dismissable = "dismissable" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(cards, forKey: .cards) + try container.encode(conditions, forKey: .conditions) + try container.encode(dismissable, forKey: .dismissable) + } + + /// The list of available cards for onboarding. + /// + public lazy var cards: [String: NimbusOnboardingCardData] = { + self._variables.getVariablesMap("cards")?.mapValuesNotNull(NimbusOnboardingCardData.create).mergeWith(_defaults.cards, NimbusOnboardingCardData.mergeWith) ?? _defaults.cards + }() + + /// A collection of out the box conditional expressions to be used in + /// determining whether a card should show or not. Each entry maps to a + /// valid JEXL expression. + /// + public lazy var conditions: [String: String] = { + self._variables.getStringMap("conditions")?.mergeWith(_defaults.conditions) ?? _defaults.conditions + }() + + /// Whether or not the entire onboarding is dismissable by pressing an X at the + /// top right corner of the screen. + /// + public lazy var dismissable: Bool = { + self._variables.getBool("dismissable") ?? _defaults.dismissable + }() + +} +extension OnboardingFrameworkFeature: FMLFeatureInterface {} +/// Password Generator Feature +public class PasswordGeneratorFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// If true, the password generator feature is enabled + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension PasswordGeneratorFeature: FMLFeatureInterface {} +/// This feature is for managing the roll out of redux on the search settings + /// screen + /// +public class ReduxSearchSettingsFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// Enables the feature + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension ReduxSearchSettingsFeature: FMLFeatureInterface {} +/// Features that let users manage tabs on other devices that are connected to + /// the same Mozilla account. + /// +public class RemoteTabManagement: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let closeTabsEnabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + closeTabsEnabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + closeTabsEnabled: closeTabsEnabled)) + } + + enum CodingKeys: String, CodingKey { + case closeTabsEnabled = "close-tabs-enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(closeTabsEnabled, forKey: .closeTabsEnabled) + } + + /// Whether the feature to close synced tabs is enabled. When enabled, this + /// device will allow other devices to close tabs that are open on this + /// device, and show a "close" button for tabs that are currently open on + /// other supported devices in the synced tabs tray. + /// + public lazy var closeTabsEnabled: Bool = { + self._variables.getBool("close-tabs-enabled") ?? _defaults.closeTabsEnabled + }() + +} +extension RemoteTabManagement: FMLFeatureInterface {} +/// Configuring the functionality to do with search. This will be separated into + /// smaller sub-features in later releases. + /// +public class Search: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let awesomeBar: AwesomeBar + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + awesomeBar: AwesomeBar = AwesomeBar(minSearchTerm: 3, position: SearchBarPositionFeature(isBottom: false, isPositionFeatureEnabled: true, isToolbarCfrOn: false), searchHighlights: false, usePageContent: false) + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + awesomeBar: awesomeBar)) + } + + enum CodingKeys: String, CodingKey { + case awesomeBar = "awesome-bar" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(awesomeBar, forKey: .awesomeBar) + } + + /// Configuring the awesome bar. + public lazy var awesomeBar: AwesomeBar = { + self._variables.getVariables("awesome-bar")?.map(AwesomeBar.create)._mergeWith(_defaults.awesomeBar) ?? _defaults.awesomeBar + }() + +} +extension Search: FMLFeatureInterface {} +/// The configuration setting for the status of the Fakespot feature + /// +public class Shopping2023: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let backInStockReporting: Bool + let config: [String: WebsiteConfig] + let productAds: Bool + let relay: String + let status: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + backInStockReporting: Bool = false, + config: [String: WebsiteConfig] = ["amazon": WebsiteConfig(productIdFromUrlRegex: "(?:[\\/]|$|%2F)(?[A-Z0-9]{10})(?:[\\/]|$|\\#|\\?|%2F)", validTlDs: ["com", "de", "fr"]), "walmart": WebsiteConfig(productIdFromUrlRegex: "\\/ip\\/(?:[A-Za-z0-9-]{1,320}\\/)?(?[0-9]{3,13})", validTlDs: ["com"]), "bestbuy": WebsiteConfig(productIdFromUrlRegex: "\\/(?\\d+\\.p)", validTlDs: ["com"])], + productAds: Bool = false, + relay: String = "https://mozilla-ohttp-fakespot.fastly-edge.com/", + status: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + backInStockReporting: backInStockReporting, + config: config, + productAds: productAds, + relay: relay, + status: status)) + } + + enum CodingKeys: String, CodingKey { + case backInStockReporting = "back_in_stock_reporting" + case config = "config" + case productAds = "product_ads" + case relay = "relay" + case status = "status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(backInStockReporting, forKey: .backInStockReporting) + try container.encode(config, forKey: .config) + try container.encode(productAds, forKey: .productAds) + try container.encode(relay, forKey: .relay) + try container.encode(status, forKey: .status) + } + + /// If true, enables for users the reporting feature for products back in stock. + /// + public lazy var backInStockReporting: Bool = { + self._variables.getBool("back_in_stock_reporting") ?? _defaults.backInStockReporting + }() + + /// A Map of website configurations + /// + public lazy var config: [String: WebsiteConfig] = { + self._variables.getVariablesMap("config")?.mapValuesNotNull(WebsiteConfig.create).mergeWith(_defaults.config, WebsiteConfig.mergeWith) ?? _defaults.config + }() + + /// If true, enables the product advertisement feature, allowing users to see + /// and interact with ads for various products. + /// + public lazy var productAds: Bool = { + self._variables.getBool("product_ads") ?? _defaults.productAds + }() + + /// Configurable relay URL for production environment + /// + public lazy var relay: String = { + self._variables.getString("relay") ?? _defaults.relay + }() + + /// Whether the Fakespot feature is enabled or disabled + /// + public lazy var status: Bool = { + self._variables.getBool("status") ?? _defaults.status + }() + +} +extension Shopping2023: FMLFeatureInterface {} +/// A feature that extends splash screen duration, allowing additional data + /// fetching time for the app's initial run. + /// +public class SplashScreen: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + let maximumDurationMs: Int + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true, + maximumDurationMs: Int = 6000 + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled, + maximumDurationMs: maximumDurationMs)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + case maximumDurationMs = "maximum_duration_ms" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + try container.encode(maximumDurationMs, forKey: .maximumDurationMs) + } + + /// If true, the feature is active. + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + + /// The maximum amount of time in milliseconds the splashscreen will be visible + /// while waiting for initialization calls to complete. + /// + public lazy var maximumDurationMs: Int = { + self._variables.getInt("maximum_duration_ms") ?? _defaults.maximumDurationMs + }() + +} +extension SplashScreen: FMLFeatureInterface {} +/// Add pages as items findable with Spotlight. +public class SpotlightSearch: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + let iconType: IconType? + let keepForDays: Int? + let searchableContent: PageContent? + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true, + iconType: IconType? = .screenshot, + keepForDays: Int? = nil, + searchableContent: PageContent? = .textExcerpt + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled, + iconType: iconType, + keepForDays: keepForDays, + searchableContent: searchableContent)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + case iconType = "icon-type" + case keepForDays = "keep-for-days" + case searchableContent = "searchable-content" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + try container.encode(iconType?.rawValue, forKey: .iconType) + try container.encode(keepForDays, forKey: .keepForDays) + try container.encode(searchableContent?.rawValue, forKey: .searchableContent) + } + + /// If this is true, then on each page load adds a new item to Spotlight. + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + + /// The icon that is displayed next to the item in the search results. If this + /// is `null`, then no icon is displayed. + /// + public lazy var iconType: IconType? = { + self._variables.getString("icon-type")?.map(IconType.enumValue) ?? _defaults.iconType + }() + + /// Number of days to keep the item before automatic deletion. If this is left + /// `null`, then it is left to iOS's default. + /// + public lazy var keepForDays: Int? = { + self._variables.getInt("keep-for-days") ?? _defaults.keepForDays + }() + + /// The text content that is made searchable. If this is `null` then no + /// additional content is used, and only the title and URL will be used. + /// + public lazy var searchableContent: PageContent? = { + self._variables.getString("searchable-content")?.map(PageContent.enumValue) ?? _defaults.searchableContent + }() + +} +extension SpotlightSearch: FMLFeatureInterface {} +/// This feature is for managing the roll out of the Tab Tray refactor feature + /// +public class TabTrayRefactorFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// Enables the feature + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension TabTrayRefactorFeature: FMLFeatureInterface {} +/// The tab tray screen that the user goes to when they open the tab tray. +public class TabTrayFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let sectionsEnabled: [TabTraySection: Bool] + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + sectionsEnabled: [TabTraySection: Bool] = [.inactiveTabs: true] + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + sectionsEnabled: sectionsEnabled)) + } + + enum CodingKeys: String, CodingKey { + case sectionsEnabled = "sections-enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(sectionsEnabled.mapKeysNotNull { $0.rawValue }, forKey: .sectionsEnabled) + } + + /// This property provides a lookup table of whether or not the given section + /// should be enabled. If the section is enabled, it should be toggleable + /// in the settings screen, and on by default. + public lazy var sectionsEnabled: [TabTraySection: Bool] = { + self._variables.getBoolMap("sections-enabled")?.mapKeysNotNull(TabTraySection.enumValue).mergeWith(_defaults.sectionsEnabled) ?? _defaults.sectionsEnabled + }() + +} +extension TabTrayFeature: FMLFeatureInterface {} +/// This feature is for managing the roll out of the Toolbar refactor feature + /// +public class ToolbarRefactorFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + let navigationHint: Bool + let oneTapNewTab: Bool + let unifiedSearch: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = false, + navigationHint: Bool = false, + oneTapNewTab: Bool = false, + unifiedSearch: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled, + navigationHint: navigationHint, + oneTapNewTab: oneTapNewTab, + unifiedSearch: unifiedSearch)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + case navigationHint = "navigation_hint" + case oneTapNewTab = "one_tap_new_tab" + case unifiedSearch = "unified_search" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + try container.encode(navigationHint, forKey: .navigationHint) + try container.encode(oneTapNewTab, forKey: .oneTapNewTab) + try container.encode(unifiedSearch, forKey: .unifiedSearch) + } + + /// Enables the feature + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + + /// If true, enables the navigation contextual hint. + /// + public lazy var navigationHint: Bool = { + self._variables.getBool("navigation_hint") ?? _defaults.navigationHint + }() + + /// If true, enables the one tap new tab feature for users. + /// + public lazy var oneTapNewTab: Bool = { + self._variables.getBool("one_tap_new_tab") ?? _defaults.oneTapNewTab + }() + + /// Enables the unified search feature + /// + public lazy var unifiedSearch: Bool = { + self._variables.getBool("unified_search") ?? _defaults.unifiedSearch + }() + +} +extension ToolbarRefactorFeature: FMLFeatureInterface {} +/// The Enhanced Tracking Protection refactor + /// +public class TrackingProtectionRefactor: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let enabled: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + enabled: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + enabled: enabled)) + } + + enum CodingKeys: String, CodingKey { + case enabled = "enabled" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(enabled, forKey: .enabled) + } + + /// Whether the Enhanced Tracking Protection refactor is enabled or not + /// + public lazy var enabled: Bool = { + self._variables.getBool("enabled") ?? _defaults.enabled + }() + +} +extension TrackingProtectionRefactor: FMLFeatureInterface {} +/// The configuration for the status of the zoom feature + /// +public class ZoomFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let status: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + status: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + status: status)) + } + + enum CodingKeys: String, CodingKey { + case status = "status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(status, forKey: .status) + } + + /// Whether the page zoom feature is enabled or not + /// + public lazy var status: Bool = { + self._variables.getBool("status") ?? _defaults.status + }() + +} +extension ZoomFeature: FMLFeatureInterface {} + + +/// The identifiers for a individual contextual hints. +public enum ContextualHint: String, CaseIterable, Codable { + + /// The contextual hint bubble that appears to provide a hint about the toolbar. + case toolbarHint = "toolbar-hint" + + + public static func enumValue(_ s: String?) -> ContextualHint? { + guard let s = s else { + return nil + } + return ContextualHint(rawValue: s) + } +} + + +/// The identifiers for the sections of the homescreen. +public enum HomeScreenSection: String, CaseIterable, Codable { + + /// The tabs the user was looking immediately before being interrupted. + case jumpBackIn = "jump-back-in" + + /// The tab groups + case recentExplorations = "recent-explorations" + + + public static func enumValue(_ s: String?) -> HomeScreenSection? { + guard let s = s else { + return nil + } + return HomeScreenSection(rawValue: s) + } +} + + +/// The icon that will be added to the item in the device's search engine. +public enum IconType: String, CaseIterable, Codable { + + /// The favicon of the page + case favicon = "favicon" + + /// An icon generated from the first letter of the base domain. + case letter = "letter" + + /// A screenshot of the page at load time. + case screenshot = "screenshot" + + + public static func enumValue(_ s: String?) -> IconType? { + guard let s = s else { + return nil + } + return IconType(rawValue: s) + } +} + + +/// The identifiers for the different images available for cards in onboarding + /// +public enum NimbusOnboardingHeaderImage: String, CaseIterable, Codable { + + /// Corresponding to the fox window image + /// + case customizeFirefox = "customize-firefox" + + /// Corresponding to the notifications image + /// + case notifications = "notifications" + + /// Corresponding to the notifications image for CTD + /// + case notificationsCtd = "notifications-ctd" + + /// Corresponding to the fox search widget image + /// + case searchWidget = "search-widget" + + /// Corresponding to the set to default steps image + /// + case setDefaultSteps = "set-default-steps" + + /// Corresponding to the set to dock image + /// + case setToDock = "set-to-dock" + + /// Corresponding to the sync-devices image + /// + case syncDevices = "sync-devices" + + /// Corresponding to the sync image for CTD + /// + case syncDevicesCtd = "sync-devices-ctd" + + /// Corresponding to the fox world image + /// + case themeing = "themeing" + + /// Corresponding to the fox world image + /// + case toolbar = "toolbar" + + /// Corresponding to the welcome image for CTD + /// + case welcomeCtd = "welcome-ctd" + + /// Corresponding to the fox world image + /// + case welcomeGlobe = "welcome-globe" + + + public static func enumValue(_ s: String?) -> NimbusOnboardingHeaderImage? { + guard let s = s else { + return nil + } + return NimbusOnboardingHeaderImage(rawValue: s) + } +} + + +/// The identifiers for the different images available for multiple choice + /// buttons in onboarding + /// +public enum NimbusOnboardingMultipleChoiceButtonImage: String, CaseIterable, Codable { + + /// Corresponding to the dark theme + /// + case themeDark = "theme-dark" + + /// Corresponding to the light theme + /// + case themeLight = "theme-light" + + /// Corresponding to the system theme + /// + case themeSystem = "theme-system" + + /// Corresponding to the toolbar on the bottom + /// + case toolbarBottom = "toolbar-bottom" + + /// Corresponding to the toolbar on top + /// + case toolbarTop = "toolbar-top" + + + public static func enumValue(_ s: String?) -> NimbusOnboardingMultipleChoiceButtonImage? { + guard let s = s else { + return nil + } + return NimbusOnboardingMultipleChoiceButtonImage(rawValue: s) + } +} + + +/// The identifiers for the different actions available for cards in onboarding + /// +public enum OnboardingActions: String, CaseIterable, Codable { + + /// Will end the onboarding on a set card + /// + case endOnboarding = "end-onboarding" + + /// Will take the user to the next card + /// + case forwardOneCard = "forward-one-card" + + /// Will take the user to the next card + /// + case forwardThreeCard = "forward-three-card" + + /// Will take the user to the next card + /// + case forwardTwoCard = "forward-two-card" + + /// Will open up a popup with instructions for something + /// + case openInstructionsPopup = "open-instructions-popup" + + /// Will take the user to the default browser settings in the iOS system + /// settings + /// + case openIosFxSettings = "open-ios-fx-settings" + + /// Will open a webview where the user can read the privacy policy + /// + case readPrivacyPolicy = "read-privacy-policy" + + /// Will request to allow notifications from the user + /// + case requestNotifications = "request-notifications" + + /// Will send the user to settings to set Firefox as their default browser and + /// advance to next card + /// + case setDefaultBrowser = "set-default-browser" + + /// Will take the user to the sync sign in flow + /// + case syncSignIn = "sync-sign-in" + + + public static func enumValue(_ s: String?) -> OnboardingActions? { + guard let s = s else { + return nil + } + return OnboardingActions(rawValue: s) + } +} + + +/// The identifiers for the different types of onboarding cards. + /// +public enum OnboardingCardType: String, CaseIterable, Codable { + + /// Corresponding to basic info onboarding cards + /// + case basic = "basic" + + /// Corresponding to onboarding cards with multiple choice buttons + /// + case multipleChoice = "multiple-choice" + + + public static func enumValue(_ s: String?) -> OnboardingCardType? { + guard let s = s else { + return nil + } + return OnboardingCardType(rawValue: s) + } +} + + +/// The identifiers for the different actions available for the insturction card + /// in onboarding + /// +public enum OnboardingInstructionsPopupActions: String, CaseIterable, Codable { + + /// Will dismiss the popup + /// + case dismiss = "dismiss" + + /// Will dismiss the popup and move to the next card + /// + case dismissAndNextCard = "dismiss-and-next-card" + + /// Will take the user to the default browser settings in the iOS system + /// settings + /// + case openIosFxSettings = "open-ios-fx-settings" + + + public static func enumValue(_ s: String?) -> OnboardingInstructionsPopupActions? { + guard let s = s else { + return nil + } + return OnboardingInstructionsPopupActions(rawValue: s) + } +} + + +/// The identifiers for the different actions available for cards in onboarding + /// +public enum OnboardingMultipleChoiceAction: String, CaseIterable, Codable { + + /// Will will set the theme to dark mode + /// + case themeDark = "theme-dark" + + /// Will set the theme to light mode + /// + case themeLight = "theme-light" + + /// Will set the theme to use the system theme + /// + case themeSystemDefault = "theme-system-default" + + /// Will set the toolbar on the bottom + /// + case toolbarBottom = "toolbar-bottom" + + /// Will set the toolbar on the top + /// + case toolbarTop = "toolbar-top" + + + public static func enumValue(_ s: String?) -> OnboardingMultipleChoiceAction? { + guard let s = s else { + return nil + } + return OnboardingMultipleChoiceAction(rawValue: s) + } +} + + +/// The identifiers for the different types of onboarding cards. + /// +public enum OnboardingType: String, CaseIterable, Codable { + + /// Corresponding to onboarding cards that are for new users + /// + case freshInstall = "fresh-install" + + /// Corresponding to onboarding cards that are for users who have updated + /// + case upgrade = "upgrade" + + + public static func enumValue(_ s: String?) -> OnboardingType? { + guard let s = s else { + return nil + } + return OnboardingType(rawValue: s) + } +} + + +/// The page content that will be added as an item in the device's search + /// engine. +public enum PageContent: String, CaseIterable, Codable { + + /// Use all the page as HTML + case htmlContent = "html-content" + + /// Use all the page as text + case textContent = "text-content" + + /// Only use the first paragraph + case textExcerpt = "text-excerpt" + + + public static func enumValue(_ s: String?) -> PageContent? { + guard let s = s else { + return nil + } + return PageContent(rawValue: s) + } +} + + +/// The type of a Firefox Suggest search suggestion. +public enum SuggestionType: String, CaseIterable, Codable { + + /// A Firefox Suggestion for desktop from adMarketplace. + case amp = "amp" + + /// A Firefox Suggestion specifically for mobile from adMarketplace. + case ampMobile = "ampMobile" + + /// A Firefox Suggestion for a Wikipedia page. + case wikipedia = "wikipedia" + + + public static func enumValue(_ s: String?) -> SuggestionType? { + guard let s = s else { + return nil + } + return SuggestionType(rawValue: s) + } +} + + +/// The identifiers for the sections of the tab tray. +public enum TabTraySection: String, CaseIterable, Codable { + + /// Tabs that have been automatically closed for the user. + case inactiveTabs = "inactive-tabs" + + + public static func enumValue(_ s: String?) -> TabTraySection? { + guard let s = s else { + return nil + } + return TabTraySection(rawValue: s) + } +} + +/// A configuration option for the awesome bar. Part of the `search` feature. +public class AwesomeBar: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let minSearchTerm: Int + let position: SearchBarPositionFeature + let searchHighlights: Bool + let usePageContent: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + minSearchTerm: Int = 3, + position: SearchBarPositionFeature = SearchBarPositionFeature(isBottom: true, isPositionFeatureEnabled: true, isToolbarCfrOn: true), + searchHighlights: Bool = false, + usePageContent: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + minSearchTerm: minSearchTerm, + position: position, + searchHighlights: searchHighlights, + usePageContent: usePageContent)) + } + + enum CodingKeys: String, CodingKey { + case minSearchTerm = "min-search-term" + case position = "position" + case searchHighlights = "search-highlights" + case usePageContent = "use-page-content" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(minSearchTerm, forKey: .minSearchTerm) + try container.encode(position, forKey: .position) + try container.encode(searchHighlights, forKey: .searchHighlights) + try container.encode(usePageContent, forKey: .usePageContent) + } + + /// The minimum number of characters that the user types before searching in + /// the page. + public lazy var minSearchTerm: Int = { + self._variables.getInt("min-search-term") ?? _defaults.minSearchTerm + }() + + /// This property defines whether or not the feature is enabled, and the + /// position of the search bar + /// + public lazy var position: SearchBarPositionFeature = { + self._variables.getVariables("position")?.map(SearchBarPositionFeature.create)._mergeWith(_defaults.position) ?? _defaults.position + }() + + /// Whether or not search highlights are enabled + public lazy var searchHighlights: Bool = { + self._variables.getBool("search-highlights") ?? _defaults.searchHighlights + }() + + /// Search in the open tab's text content when typing. + public lazy var usePageContent: Bool = { + self._variables.getBool("use-page-content") ?? _defaults.usePageContent + }() + +} + +public extension AwesomeBar { + func _mergeWith(_ defaults: AwesomeBar?) -> AwesomeBar { + guard let defaults = defaults else { + return self + } + return AwesomeBar(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> AwesomeBar { + return AwesomeBar(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: AwesomeBar, _ defaults: AwesomeBar) -> AwesomeBar { + return overrides._mergeWith(defaults) + } +} + +/// The configuration for the a feature that can be enabled or disabled +public class GeneralFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let status: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + status: Bool = false + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + status: status)) + } + + enum CodingKeys: String, CodingKey { + case status = "status" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(status, forKey: .status) + } + + /// Whether or not the feature is enabled + public lazy var status: Bool = { + self._variables.getBool("status") ?? _defaults.status + }() + +} + +public extension GeneralFeature { + func _mergeWith(_ defaults: GeneralFeature?) -> GeneralFeature { + guard let defaults = defaults else { + return self + } + return GeneralFeature(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> GeneralFeature { + return GeneralFeature(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: GeneralFeature, _ defaults: GeneralFeature) -> GeneralFeature { + return overrides._mergeWith(defaults) + } +} + +/// A group of properties describing the attributes of a card. + /// +public class NimbusOnboardingButton: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let action: OnboardingActions + let title: String + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + action: OnboardingActions = .forwardOneCard, + title: String = "" + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + action: action, + title: title)) + } + + enum CodingKeys: String, CodingKey { + case action = "action" + case title = "title" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(action.rawValue, forKey: .action) + try container.encode(title, forKey: .title) + } + + /// The action the button should take. The default for this will be "forward- + /// one-card" + /// + public lazy var action: OnboardingActions = { + self._variables.getString("action")?.map(OnboardingActions.enumValue) ?? _defaults.action + }() + + /// The text of the button title. This should never be defaulted. + /// + public lazy var title: String = { + self._variables.getText("title") ?? self._variables.resourceBundles.getString(named: _defaults.title) ?? _defaults.title + }() + +} + +public extension NimbusOnboardingButton { + func _mergeWith(_ defaults: NimbusOnboardingButton?) -> NimbusOnboardingButton { + guard let defaults = defaults else { + return self + } + return NimbusOnboardingButton(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> NimbusOnboardingButton { + return NimbusOnboardingButton(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: NimbusOnboardingButton, _ defaults: NimbusOnboardingButton) -> NimbusOnboardingButton { + return overrides._mergeWith(defaults) + } +} + +/// A set of buttons for the card. There can be up to two, but there must be at + /// least one. + /// +public class NimbusOnboardingButtons: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let primary: NimbusOnboardingButton + let secondary: NimbusOnboardingButton? + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + primary: NimbusOnboardingButton = NimbusOnboardingButton(action: .forwardOneCard, title: "Primary Button"), + secondary: NimbusOnboardingButton? = nil + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + primary: primary, + secondary: secondary)) + } + + enum CodingKeys: String, CodingKey { + case primary = "primary" + case secondary = "secondary" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(primary, forKey: .primary) + try container.encode(secondary, forKey: .secondary) + } + + /// The primary button for the card. This must exist. + /// + public lazy var primary: NimbusOnboardingButton = { + self._variables.getVariables("primary")?.map(NimbusOnboardingButton.create)._mergeWith(_defaults.primary) ?? _defaults.primary + }() + + /// A secondary, optional, button for the card. + /// + public lazy var secondary: NimbusOnboardingButton? = { + self._variables.getVariables("secondary")?.map(NimbusOnboardingButton.create)._mergeWith(_defaults.secondary) ?? _defaults.secondary + }() + +} + +public extension NimbusOnboardingButtons { + func _mergeWith(_ defaults: NimbusOnboardingButtons?) -> NimbusOnboardingButtons { + guard let defaults = defaults else { + return self + } + return NimbusOnboardingButtons(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> NimbusOnboardingButtons { + return NimbusOnboardingButtons(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: NimbusOnboardingButtons, _ defaults: NimbusOnboardingButtons) -> NimbusOnboardingButtons { + return overrides._mergeWith(defaults) + } +} + +/// A group of properties describing the attributes of a card. + /// +public class NimbusOnboardingCardData: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let body: String + let buttons: NimbusOnboardingButtons + let cardType: OnboardingCardType + let disqualifiers: [String] + let image: NimbusOnboardingHeaderImage + let instructionsPopup: NimbusOnboardingInstructionPopup? + let link: NimbusOnboardingLink? + let multipleChoiceButtons: [NimbusOnboardingMultipleChoiceButton] + let onboardingType: OnboardingType + let order: Int + let prerequisites: [String] + let title: String + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + body: String = "", + buttons: NimbusOnboardingButtons = NimbusOnboardingButtons(primary: NimbusOnboardingButton(action: .forwardOneCard, title: "Onboarding/Onboarding.Sync.Skip.Action.v114"), secondary: nil), + cardType: OnboardingCardType = .basic, + disqualifiers: [String] = [], + image: NimbusOnboardingHeaderImage = .welcomeGlobe, + instructionsPopup: NimbusOnboardingInstructionPopup? = nil, + link: NimbusOnboardingLink? = nil, + multipleChoiceButtons: [NimbusOnboardingMultipleChoiceButton] = [], + onboardingType: OnboardingType = .freshInstall, + order: Int = 10, + prerequisites: [String] = [], + title: String = "" + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + body: body, + buttons: buttons, + cardType: cardType, + disqualifiers: disqualifiers, + image: image, + instructionsPopup: instructionsPopup, + link: link, + multipleChoiceButtons: multipleChoiceButtons, + onboardingType: onboardingType, + order: order, + prerequisites: prerequisites, + title: title)) + } + + enum CodingKeys: String, CodingKey { + case body = "body" + case buttons = "buttons" + case cardType = "card-type" + case disqualifiers = "disqualifiers" + case image = "image" + case instructionsPopup = "instructions-popup" + case link = "link" + case multipleChoiceButtons = "multiple-choice-buttons" + case onboardingType = "onboarding-type" + case order = "order" + case prerequisites = "prerequisites" + case title = "title" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(body, forKey: .body) + try container.encode(buttons, forKey: .buttons) + try container.encode(cardType.rawValue, forKey: .cardType) + try container.encode(disqualifiers, forKey: .disqualifiers) + try container.encode(image.rawValue, forKey: .image) + try container.encode(instructionsPopup, forKey: .instructionsPopup) + try container.encode(link, forKey: .link) + try container.encode(multipleChoiceButtons, forKey: .multipleChoiceButtons) + try container.encode(onboardingType.rawValue, forKey: .onboardingType) + try container.encode(order, forKey: .order) + try container.encode(prerequisites, forKey: .prerequisites) + try container.encode(title, forKey: .title) + } + + /// The body text dispalyed on the card, in less prominent text. This should + /// never be defaulted. + /// + public lazy var body: String = { + self._variables.getText("body") ?? self._variables.resourceBundles.getString(named: _defaults.body) ?? _defaults.body + }() + + /// The set of buttons associated with the card. + /// + public lazy var buttons: NimbusOnboardingButtons = { + self._variables.getVariables("buttons")?.map(NimbusOnboardingButtons.create)._mergeWith(_defaults.buttons) ?? _defaults.buttons + }() + + /// The type of onboarding this card should be shown in, whether it a fresh + /// install or an update. The default is fresh-install. + /// + public lazy var cardType: OnboardingCardType = { + self._variables.getString("card-type")?.map(OnboardingCardType.enumValue) ?? _defaults.cardType + }() + + /// A list of ConditionName strings corresponding to targeting expressions. The + /// card will not be shown if any expression is `true`. + /// + public lazy var disqualifiers: [String] = { + self._variables.getStringList("disqualifiers") ?? _defaults.disqualifiers + }() + + /// The image that should be dispalyed on the card. + /// + public lazy var image: NimbusOnboardingHeaderImage = { + self._variables.getString("image")?.map(NimbusOnboardingHeaderImage.enumValue) ?? _defaults.image + }() + + /// The object describing the specific instruction popup button for a card. If + /// left empty, the card will have no instruction popup information + /// + public lazy var instructionsPopup: NimbusOnboardingInstructionPopup? = { + self._variables.getVariables("instructions-popup")?.map(NimbusOnboardingInstructionPopup.create)._mergeWith(_defaults.instructionsPopup) ?? _defaults.instructionsPopup + }() + + /// The object describing the link button for a card. If left empty, the card + /// will have no link. + /// + public lazy var link: NimbusOnboardingLink? = { + self._variables.getVariables("link")?.map(NimbusOnboardingLink.create)._mergeWith(_defaults.link) ?? _defaults.link + }() + + /// A list of multiple choice buttons that the card will display + /// + public lazy var multipleChoiceButtons: [NimbusOnboardingMultipleChoiceButton] = { + self._variables.getVariablesList("multiple-choice-buttons")?.mapNotNull(NimbusOnboardingMultipleChoiceButton.create) ?? _defaults.multipleChoiceButtons + }() + + /// The type of onboarding this card should be shown in, whether it a fresh + /// install or an update. The default is fresh-install. + /// + public lazy var onboardingType: OnboardingType = { + self._variables.getString("onboarding-type")?.map(OnboardingType.enumValue) ?? _defaults.onboardingType + }() + + /// The place in the order where the card will be found. The feature layer will + /// then sort the cards based on this field. + /// + public lazy var order: Int = { + self._variables.getInt("order") ?? _defaults.order + }() + + /// A list of ConditionName strings corresponding to targeting expressions. The + /// card will be shown if all expressions `true` and if no expressions in + /// the `disqualifiers` table are true, or if the `disqualifiers` table + /// is empty. + /// + public lazy var prerequisites: [String] = { + self._variables.getStringList("prerequisites") ?? _defaults.prerequisites + }() + + /// The title displayed on the card, in prominent, bolded text. This should + /// never be defaulted. + /// + public lazy var title: String = { + self._variables.getText("title") ?? self._variables.resourceBundles.getString(named: _defaults.title) ?? _defaults.title + }() + +} + +public extension NimbusOnboardingCardData { + func _mergeWith(_ defaults: NimbusOnboardingCardData?) -> NimbusOnboardingCardData { + guard let defaults = defaults else { + return self + } + return NimbusOnboardingCardData(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> NimbusOnboardingCardData { + return NimbusOnboardingCardData(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: NimbusOnboardingCardData, _ defaults: NimbusOnboardingCardData) -> NimbusOnboardingCardData { + return overrides._mergeWith(defaults) + } +} + +/// The object outlining the content of the instruction card. + /// +public class NimbusOnboardingInstructionPopup: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let buttonAction: OnboardingInstructionsPopupActions + let buttonTitle: String + let instructions: [String] + let title: String + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + buttonAction: OnboardingInstructionsPopupActions = .dismissAndNextCard, + buttonTitle: String = "", + instructions: [String] = [], + title: String = "" + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + buttonAction: buttonAction, + buttonTitle: buttonTitle, + instructions: instructions, + title: title)) + } + + enum CodingKeys: String, CodingKey { + case buttonAction = "button-action" + case buttonTitle = "button-title" + case instructions = "instructions" + case title = "title" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(buttonAction.rawValue, forKey: .buttonAction) + try container.encode(buttonTitle, forKey: .buttonTitle) + try container.encode(instructions, forKey: .instructions) + try container.encode(title, forKey: .title) + } + + /// The action the button should have. Default is `dismiss-and-next-card` + /// + public lazy var buttonAction: OnboardingInstructionsPopupActions = { + self._variables.getString("button-action")?.map(OnboardingInstructionsPopupActions.enumValue) ?? _defaults.buttonAction + }() + + /// The title the button should have. This should never be defaulted. + /// + public lazy var buttonTitle: String = { + self._variables.getText("button-title") ?? self._variables.resourceBundles.getString(named: _defaults.buttonTitle) ?? _defaults.buttonTitle + }() + + /// A list of instructions, either as free text, or as Text identifiers. + /// + public lazy var instructions: [String] = { + self._variables.getTextList("instructions") ?? _defaults.instructions.map { self._variables.resourceBundles.getString(named: $0) ?? $0 } + }() + + /// The text of the popup. This should never be defaulted. + /// + public lazy var title: String = { + self._variables.getText("title") ?? self._variables.resourceBundles.getString(named: _defaults.title) ?? _defaults.title + }() + +} + +public extension NimbusOnboardingInstructionPopup { + func _mergeWith(_ defaults: NimbusOnboardingInstructionPopup?) -> NimbusOnboardingInstructionPopup { + guard let defaults = defaults else { + return self + } + return NimbusOnboardingInstructionPopup(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> NimbusOnboardingInstructionPopup { + return NimbusOnboardingInstructionPopup(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: NimbusOnboardingInstructionPopup, _ defaults: NimbusOnboardingInstructionPopup) -> NimbusOnboardingInstructionPopup { + return overrides._mergeWith(defaults) + } +} + +/// A group of properties describing the attributes for the active link on + /// a card + /// +public class NimbusOnboardingLink: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let title: String + let url: String + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + title: String = "Onboarding/Onboarding.Welcome.Link.Action.v114", + url: String = "https://www.mozilla.org/privacy/firefox/" + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + title: title, + url: url)) + } + + enum CodingKeys: String, CodingKey { + case title = "title" + case url = "url" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(title, forKey: .title) + try container.encode(url, forKey: .url) + } + + /// The text of the link title. + /// + public lazy var title: String = { + self._variables.getText("title") ?? self._variables.resourceBundles.getString(named: _defaults.title) ?? _defaults.title + }() + + /// The url that the link will lead to. + /// + public lazy var url: String = { + self._variables.getString("url") ?? _defaults.url + }() + +} + +public extension NimbusOnboardingLink { + func _mergeWith(_ defaults: NimbusOnboardingLink?) -> NimbusOnboardingLink { + guard let defaults = defaults else { + return self + } + return NimbusOnboardingLink(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> NimbusOnboardingLink { + return NimbusOnboardingLink(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: NimbusOnboardingLink, _ defaults: NimbusOnboardingLink) -> NimbusOnboardingLink { + return overrides._mergeWith(defaults) + } +} + +/// A group of properties describing the attributes of a multiple choice button + /// on a card + /// +public class NimbusOnboardingMultipleChoiceButton: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let action: OnboardingMultipleChoiceAction + let image: NimbusOnboardingMultipleChoiceButtonImage + let title: String + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + action: OnboardingMultipleChoiceAction = .themeSystemDefault, + image: NimbusOnboardingMultipleChoiceButtonImage = .themeSystem, + title: String = "" + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + action: action, + image: image, + title: title)) + } + + enum CodingKeys: String, CodingKey { + case action = "action" + case image = "image" + case title = "title" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(action.rawValue, forKey: .action) + try container.encode(image.rawValue, forKey: .image) + try container.encode(title, forKey: .title) + } + + /// The action the button should take. The default for this will be "theme- + /// system-default" + /// + public lazy var action: OnboardingMultipleChoiceAction = { + self._variables.getString("action")?.map(OnboardingMultipleChoiceAction.enumValue) ?? _defaults.action + }() + + /// The text of the button title. This should never be defaulted. + /// + public lazy var image: NimbusOnboardingMultipleChoiceButtonImage = { + self._variables.getString("image")?.map(NimbusOnboardingMultipleChoiceButtonImage.enumValue) ?? _defaults.image + }() + + /// The text of the button title. This should never be defaulted. + /// + public lazy var title: String = { + self._variables.getText("title") ?? self._variables.resourceBundles.getString(named: _defaults.title) ?? _defaults.title + }() + +} + +public extension NimbusOnboardingMultipleChoiceButton { + func _mergeWith(_ defaults: NimbusOnboardingMultipleChoiceButton?) -> NimbusOnboardingMultipleChoiceButton { + guard let defaults = defaults else { + return self + } + return NimbusOnboardingMultipleChoiceButton(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> NimbusOnboardingMultipleChoiceButton { + return NimbusOnboardingMultipleChoiceButton(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: NimbusOnboardingMultipleChoiceButton, _ defaults: NimbusOnboardingMultipleChoiceButton) -> NimbusOnboardingMultipleChoiceButton { + return overrides._mergeWith(defaults) + } +} + +/// The configuration for the bottom search bar on the homescreen +public class SearchBarPositionFeature: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let isBottom: Bool + let isPositionFeatureEnabled: Bool + let isToolbarCfrOn: Bool + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + isBottom: Bool = true, + isPositionFeatureEnabled: Bool = true, + isToolbarCfrOn: Bool = true + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + isBottom: isBottom, + isPositionFeatureEnabled: isPositionFeatureEnabled, + isToolbarCfrOn: isToolbarCfrOn)) + } + + enum CodingKeys: String, CodingKey { + case isBottom = "is-bottom" + case isPositionFeatureEnabled = "is-position-feature-enabled" + case isToolbarCfrOn = "is-toolbar-cfr-on" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(isBottom, forKey: .isBottom) + try container.encode(isPositionFeatureEnabled, forKey: .isPositionFeatureEnabled) + try container.encode(isToolbarCfrOn, forKey: .isToolbarCfrOn) + } + + /// Whether or not the default position is at the bottom + public lazy var isBottom: Bool = { + self._variables.getBool("is-bottom") ?? _defaults.isBottom + }() + + /// Whether or not the feature is enabled + public lazy var isPositionFeatureEnabled: Bool = { + self._variables.getBool("is-position-feature-enabled") ?? _defaults.isPositionFeatureEnabled + }() + + /// Whether or not the toolbar CFR shows. This is a temporary hack for Nimbus + public lazy var isToolbarCfrOn: Bool = { + self._variables.getBool("is-toolbar-cfr-on") ?? _defaults.isToolbarCfrOn + }() + +} + +public extension SearchBarPositionFeature { + func _mergeWith(_ defaults: SearchBarPositionFeature?) -> SearchBarPositionFeature { + guard let defaults = defaults else { + return self + } + return SearchBarPositionFeature(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> SearchBarPositionFeature { + return SearchBarPositionFeature(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: SearchBarPositionFeature, _ defaults: SearchBarPositionFeature) -> SearchBarPositionFeature { + return overrides._mergeWith(defaults) + } +} + +/// It represents a configuration for different e-commerce websites and includes + /// regular expressions for extracting product IDs from their respective URLs + /// +public class WebsiteConfig: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let productIdFromUrlRegex: String + let validTlDs: [String] + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + productIdFromUrlRegex: String = "", + validTlDs: [String] = [] + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + productIdFromUrlRegex: productIdFromUrlRegex, + validTlDs: validTlDs)) + } + + enum CodingKeys: String, CodingKey { + case productIdFromUrlRegex = "productIdFromURLRegex" + case validTlDs = "validTLDs" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(productIdFromUrlRegex, forKey: .productIdFromUrlRegex) + try container.encode(validTlDs, forKey: .validTlDs) + } + + /// It represents the product ID extracted from a regex query + /// + public lazy var productIdFromUrlRegex: String = { + self._variables.getString("productIdFromURLRegex") ?? _defaults.productIdFromUrlRegex + }() + + /// Valid Top Level Domains + /// + public lazy var validTlDs: [String] = { + self._variables.getStringList("validTLDs") ?? _defaults.validTlDs + }() + +} + +public extension WebsiteConfig { + func _mergeWith(_ defaults: WebsiteConfig?) -> WebsiteConfig { + guard let defaults = defaults else { + return self + } + return WebsiteConfig(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> WebsiteConfig { + return WebsiteConfig(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: WebsiteConfig, _ defaults: WebsiteConfig) -> WebsiteConfig { + return overrides._mergeWith(defaults) + } +} + diff --git a/firefox-ios/Client/Ecosia/PersistedGenerated/FxNimbusMessaging.swift b/firefox-ios/Client/Ecosia/PersistedGenerated/FxNimbusMessaging.swift new file mode 100644 index 0000000000000..32ef5cb8799c8 --- /dev/null +++ b/firefox-ios/Client/Ecosia/PersistedGenerated/FxNimbusMessaging.swift @@ -0,0 +1,590 @@ +// This file was autogenerated by the `nimbus-fml` crate. +// Trust me, you don't want to mess with it! +#if canImport(Foundation) + import Foundation +#endif +#if canImport(MozillaAppServices) + import MozillaAppServices +#endif +#if canImport(UIKit) + import UIKit +#endif + +/// +/// An object for safely accessing feature configuration from Nimbus. +/// +/// This is generated. +public class FxNimbusMessaging : FeatureManifestInterface { + public typealias Features = FxNimbusMessagingFeatures + + /// + /// This should be populated at app launch; this method of initializing features + /// will be removed in favor of the `initialize` function. + /// + public var api: FeaturesInterface? + + /// + /// This method should be called as early in the startup sequence of the app as possible. + /// This is to connect the Nimbus SDK (and thus server) with the `FxNimbusMessaging` + /// class. + /// + /// The lambda MUST be threadsafe in its own right. + public func initialize(with getSdk: @escaping () -> FeaturesInterface?) { + self.getSdk = getSdk + self.features.messaging.with(sdk: getSdk) + self.reinitialize() + } + + fileprivate lazy var getSdk: GetSdk = { [self] in self.api } + + /// + /// Represents all the features supported by Nimbus + /// + public let features = Features() + + public func getCoenrollingFeatureIds() -> [String] { + ["messaging"] + } + + /// Introspection utility method. + public func getFeature(featureId: String) -> FeatureHolderAny? { + switch featureId { + case "messaging": return FeatureHolderAny(wrapping: features.messaging) + default: return nil + } + } + + /// + /// All generated initialization code. Clients shouldn't need to override or call + /// this. + /// We put it in a separate method because we have to be quite careful about what order + /// the initialization happens in— e.g. when importing other FML files. + /// + private func reinitialize() { + // Nothing left to do. + } + + /// + /// Refresh the cache of configuration objects. + /// + /// For performance reasons, the feature configurations are constructed once then cached. + /// This method is to clear that cache for all features configured with Nimbus. + /// + /// It must be called whenever the Nimbus SDK finishes the `applyPendingExperiments()` method. + /// + public func invalidateCachedValues() { + features.messaging.with(cachedValue: nil) + } + + /// + /// A singleton instance of FxNimbusMessaging + /// + public static let shared = FxNimbusMessaging() +} + +public class FxNimbusMessagingFeatures { + /// The in-app messaging system + /// + public lazy var messaging: FeatureHolder = { + FeatureHolder(FxNimbusMessaging.shared.getSdk, featureId: "messaging") { variables, prefs in + Messaging(variables, prefs) + } + }() +} + +// Public interface members begin here. + +/// The in-app messaging system + /// +public class Messaging: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let actions: [String: String] + let messageUnderExperiment: String? + let messages: [String: MessageData] + let onControl: ControlMessageBehavior + let styles: [String: StyleData] + let triggers: [String: String] + let experiment: String + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + actions: [String: String] = [:], + messageUnderExperiment: String? = nil, + messages: [String: MessageData] = [:], + onControl: ControlMessageBehavior = .showNextMessage, + styles: [String: StyleData] = [:], + triggers: [String: String] = ["ALWAYS": "true", "NEVER": "false"], + experiment: String = "{experiment}" + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + actions: actions, + messageUnderExperiment: messageUnderExperiment, + messages: messages, + onControl: onControl, + styles: styles, + triggers: triggers, + experiment: experiment)) + } + + enum CodingKeys: String, CodingKey { + case actions = "actions" + case messageUnderExperiment = "message-under-experiment" + case messages = "messages" + case onControl = "on-control" + case styles = "styles" + case triggers = "triggers" + case experiment = "~~experiment" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(actions, forKey: .actions) + try container.encode(messageUnderExperiment, forKey: .messageUnderExperiment) + try container.encode(messages, forKey: .messages) + try container.encode(onControl.rawValue, forKey: .onControl) + try container.encode(styles, forKey: .styles) + try container.encode(triggers, forKey: .triggers) + try container.encode(experiment, forKey: .experiment) + } + + /// A growable map of action URLs. + public lazy var actions: [String: String] = { + self._variables.getStringMap("actions")?.mergeWith(_defaults.actions) ?? _defaults.actions + }() + + /// Deprecated. Please use "experiment": "{experiment}" instead. + public lazy var messageUnderExperiment: String? = { + self._variables.getString("message-under-experiment") ?? _defaults.messageUnderExperiment + }() + + /// A growable collection of messages, where the Key is the message identifier + /// and the value is its associated MessageData. + /// + public lazy var messages: [String: MessageData] = { + self._variables.getVariablesMap("messages")?.mapValuesNotNull(MessageData.create).mergeWith(_defaults.messages, MessageData.mergeWith) ?? _defaults.messages + }() + + /// What should be displayed when a control message is selected. + public lazy var onControl: ControlMessageBehavior = { + self._variables.getString("on-control")?.map(ControlMessageBehavior.enumValue) ?? _defaults.onControl + }() + + /// A map of styles to configure message appearance. + /// + public lazy var styles: [String: StyleData] = { + self._variables.getVariablesMap("styles")?.mapValuesNotNull(StyleData.create).mergeWith(_defaults.styles, StyleData.mergeWith) ?? _defaults.styles + }() + + /// A collection of out the box trigger expressions. Each entry maps to a valid + /// JEXL expression. + /// + public lazy var triggers: [String: String] = { + self._variables.getStringMap("triggers")?.mergeWith(_defaults.triggers) ?? _defaults.triggers + }() + + /// Not to be set by experiment. + public lazy var experiment: String = { + self._variables.getString("~~experiment") ?? _defaults.experiment + }() + +} +extension Messaging: FMLFeatureInterface {} + + +/// An enum to influence what should be displayed when a control message is + /// selected. +public enum ControlMessageBehavior: String, CaseIterable, Codable { + + /// The next eligible message should be shown. + case showNextMessage = "show-next-message" + + /// The surface should show no message. + case showNone = "show-none" + + + public static func enumValue(_ s: String?) -> ControlMessageBehavior? { + guard let s = s else { + return nil + } + return ControlMessageBehavior(rawValue: s) + } +} + + +/// For messaging, we would like to have a message tell us which surface its + /// associated with. This is a label that matches across both Android and iOS. + /// +public enum MessageSurfaceId: String, CaseIterable, Codable { + + /// A message has NOT declared its target surface. + case unknown = "Unknown" + + /// This is a microsurvey that appears on top of the bottom toolbar to the user. + case microsurvey = "microsurvey" + + /// This is the card that appears at the top on the Firefox Home Page. + case newTabCard = "new-tab-card" + + /// This is a local notification send to the user periodically with tips and + /// updates. + case notification = "notification" + + /// This is a full-page that appears providing a survey to the user. + case survey = "survey" + + + public static func enumValue(_ s: String?) -> MessageSurfaceId? { + guard let s = s else { + return nil + } + return MessageSurfaceId(rawValue: s) + } +} + +/// An object to describe a message. It uses human readable strings to describe + /// the triggers, action and style of the message as well as the text of the + /// message and call to action. + /// +public class MessageData: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let action: String? + let actionParams: [String: String] + let buttonLabel: String? + let exceptIfAny: [String] + let experiment: String? + let isControl: Bool + let microsurveyConfig: MicrosurveyConfig? + let style: String + let surface: MessageSurfaceId + let text: String + let title: String? + let triggerIfAll: [String] + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + action: String? = nil, + actionParams: [String: String] = [:], + buttonLabel: String? = nil, + exceptIfAny: [String] = [], + experiment: String? = nil, + isControl: Bool = false, + microsurveyConfig: MicrosurveyConfig? = nil, + style: String = "DEFAULT", + surface: MessageSurfaceId = .unknown, + text: String = "", + title: String? = nil, + triggerIfAll: [String] = ["ALWAYS"] + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + action: action, + actionParams: actionParams, + buttonLabel: buttonLabel, + exceptIfAny: exceptIfAny, + experiment: experiment, + isControl: isControl, + microsurveyConfig: microsurveyConfig, + style: style, + surface: surface, + text: text, + title: title, + triggerIfAll: triggerIfAll)) + } + + enum CodingKeys: String, CodingKey { + case action = "action" + case actionParams = "action-params" + case buttonLabel = "button-label" + case exceptIfAny = "except-if-any" + case experiment = "experiment" + case isControl = "is-control" + case microsurveyConfig = "microsurveyConfig" + case style = "style" + case surface = "surface" + case text = "text" + case title = "title" + case triggerIfAll = "trigger-if-all" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(action, forKey: .action) + try container.encode(actionParams, forKey: .actionParams) + try container.encode(buttonLabel, forKey: .buttonLabel) + try container.encode(exceptIfAny, forKey: .exceptIfAny) + try container.encode(experiment, forKey: .experiment) + try container.encode(isControl, forKey: .isControl) + try container.encode(microsurveyConfig, forKey: .microsurveyConfig) + try container.encode(style, forKey: .style) + try container.encode(surface.rawValue, forKey: .surface) + try container.encode(text, forKey: .text) + try container.encode(title, forKey: .title) + try container.encode(triggerIfAll, forKey: .triggerIfAll) + } + + /// The name of a deeplink URL to be opened if the button is clicked. + /// + public lazy var action: String? = { + self._variables.getString("action") ?? _defaults.action + }() + + /// Query parameters appended to the deeplink action URL + public lazy var actionParams: [String: String] = { + self._variables.getStringMap("action-params")?.mergeWith(_defaults.actionParams) ?? _defaults.actionParams + }() + + /// The text on the button. If no text is present, the whole message is + /// clickable. + /// + public lazy var buttonLabel: String? = { + self._variables.getText("button-label") ?? _defaults.buttonLabel.map { self._variables.resourceBundles.getString(named: $0) ?? $0 } + }() + + /// A list of strings corresponding to targeting expressions. If any of these + /// expressions evaluate to `true`, the message will not be eligible. + /// + public lazy var exceptIfAny: [String] = { + self._variables.getStringList("except-if-any") ?? _defaults.exceptIfAny + }() + + /// The experiment slug that this message is involved in. + public lazy var experiment: String? = { + self._variables.getString("experiment") ?? _defaults.experiment + }() + + /// Indicates if this message is the control message, if true shouldn't be + /// displayed + public lazy var isControl: Bool = { + self._variables.getBool("is-control") ?? _defaults.isControl + }() + + /// Optional configuration data for a microsurvey. + public lazy var microsurveyConfig: MicrosurveyConfig? = { + self._variables.getVariables("microsurveyConfig")?.map(MicrosurveyConfig.create)._mergeWith(_defaults.microsurveyConfig) ?? _defaults.microsurveyConfig + }() + + /// The style as described in a `StyleData` from the styles table. + /// + public lazy var style: String = { + self._variables.getString("style") ?? _defaults.style + }() + + /// Each message will tell us the surface it is targeting with this. + public lazy var surface: MessageSurfaceId = { + self._variables.getString("surface")?.map(MessageSurfaceId.enumValue) ?? _defaults.surface + }() + + /// The message text displayed to the user + public lazy var text: String = { + self._variables.getText("text") ?? self._variables.resourceBundles.getString(named: _defaults.text) ?? _defaults.text + }() + + /// The title text displayed to the user + public lazy var title: String? = { + self._variables.getText("title") ?? _defaults.title.map { self._variables.resourceBundles.getString(named: $0) ?? $0 } + }() + + /// A list of strings corresponding to targeting expressions. All named + /// expressions must evaluate to true if the message is to be eligible to + /// be shown. + /// + public lazy var triggerIfAll: [String] = { + self._variables.getStringList("trigger-if-all") ?? _defaults.triggerIfAll + }() + +} + +public extension MessageData { + func _mergeWith(_ defaults: MessageData?) -> MessageData { + guard let defaults = defaults else { + return self + } + return MessageData(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> MessageData { + return MessageData(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: MessageData, _ defaults: MessageData) -> MessageData { + return overrides._mergeWith(defaults) + } +} + +/// Attributes relating to microsurvey messaging. + /// +public class MicrosurveyConfig: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let icon: String + let options: [String] + let utmContent: String? + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + icon: String = "lightbulbLarge", + options: [String] = [], + utmContent: String? = nil + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + icon: icon, + options: options, + utmContent: utmContent)) + } + + enum CodingKeys: String, CodingKey { + case icon = "icon" + case options = "options" + case utmContent = "utm-content" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(icon.encodableImageName, forKey: .icon) + try container.encode(options, forKey: .options) + try container.encode(utmContent, forKey: .utmContent) + } + + /// The asset name in our bundle used as the icon shown in the survey. + public lazy var icon: UIImage = { + self._variables.getImage("icon") ?? self._variables.resourceBundles.getImageNotNull(named: _defaults.icon) + }() + + /// The list of survey options to present to the user. + public lazy var options: [String] = { + self._variables.getTextList("options") ?? _defaults.options.map { self._variables.resourceBundles.getString(named: $0) ?? $0 } + }() + + /// The name used to provide as the utm_content parameter for the privacy + /// notice. + public lazy var utmContent: String? = { + self._variables.getString("utm-content") ?? _defaults.utmContent + }() + +} + +public extension MicrosurveyConfig { + func _mergeWith(_ defaults: MicrosurveyConfig?) -> MicrosurveyConfig { + guard let defaults = defaults else { + return self + } + return MicrosurveyConfig(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> MicrosurveyConfig { + return MicrosurveyConfig(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: MicrosurveyConfig, _ defaults: MicrosurveyConfig) -> MicrosurveyConfig { + return overrides._mergeWith(defaults) + } +} + +/// A group of properities (predominantly visual) to the describe style of the + /// message. + /// +public class StyleData: FMLObjectInterface { + private let _variables: Variables + private let _defaults: Defaults + private let _prefs: UserDefaults? + + private init(variables: Variables = NilVariables.instance, prefs: UserDefaults? = nil, defaults: Defaults) { + self._variables = variables + self._defaults = defaults + self._prefs = prefs + } + + struct Defaults { + let maxDisplayCount: Int + let priority: Int + } + + public convenience init( + _ _variables: Variables = NilVariables.instance, + _ _prefs: UserDefaults? = nil, + maxDisplayCount: Int = 5, + priority: Int = 50 + ) { + self.init(variables: _variables, prefs: _prefs, defaults: Defaults( + maxDisplayCount: maxDisplayCount, + priority: priority)) + } + + enum CodingKeys: String, CodingKey { + case maxDisplayCount = "max-display-count" + case priority = "priority" + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(maxDisplayCount, forKey: .maxDisplayCount) + try container.encode(priority, forKey: .priority) + } + + /// How many sessions will this message be shown to the user before it is + /// expired. + /// + public lazy var maxDisplayCount: Int = { + self._variables.getInt("max-display-count") ?? _defaults.maxDisplayCount + }() + + /// The importance of this message. 0 is not very important, 100 is very + /// important. + /// + public lazy var priority: Int = { + self._variables.getInt("priority") ?? _defaults.priority + }() + +} + +public extension StyleData { + func _mergeWith(_ defaults: StyleData?) -> StyleData { + guard let defaults = defaults else { + return self + } + return StyleData(variables: self._variables, prefs: self._prefs, defaults: defaults._defaults) + } + + static func create(_ variables: Variables?) -> StyleData { + return StyleData(variables ?? NilVariables.instance) + } + + static func mergeWith(_ overrides: StyleData, _ defaults: StyleData) -> StyleData { + return overrides._mergeWith(defaults) + } +} + diff --git a/firefox-ios/Client/Ecosia/PersistedGenerated/Metrics/Metrics.swift b/firefox-ios/Client/Ecosia/PersistedGenerated/Metrics/Metrics.swift new file mode 100644 index 0000000000000..75bf9c4098338 --- /dev/null +++ b/firefox-ios/Client/Ecosia/PersistedGenerated/Metrics/Metrics.swift @@ -0,0 +1,6597 @@ +// -*- mode: Swift -*- + +// AUTOGENERATED BY glean_parser v15.2.1. DO NOT EDIT. DO NOT COMMIT. + +#if canImport(Foundation) + import Foundation +#endif + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import Glean + +// swiftlint:disable superfluous_disable_command +// swiftlint:disable nesting +// swiftlint:disable line_length +// swiftlint:disable identifier_name +// swiftlint:disable force_try + +extension GleanMetrics { + class GleanBuild { + private init() { + // Intentionally left private, no external user can instantiate a new global object. + } + + public static let info = BuildInfo(buildDate: DateComponents(calendar: Calendar.current, timeZone: TimeZone(abbreviation: "UTC"), year: 2025, month: 1, day: 14, hour: 15, minute: 34, second: 5)) + } + + enum Accessibility { + struct DynamicTextExtra: EventExtras { + var isAccessibilitySizeEnabled: String? + var preferredSize: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isAccessibilitySizeEnabled = self.isAccessibilitySizeEnabled { + record["is_accessibility_size_enabled"] = String(isAccessibilitySizeEnabled) + } + if let preferredSize = self.preferredSize { + record["preferred_size"] = String(preferredSize) + } + + return record + } + } + + struct InvertColorsExtra: EventExtras { + var isEnabled: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + struct ReduceMotionExtra: EventExtras { + var isEnabled: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + struct ReduceTransparencyExtra: EventExtras { + var isEnabled: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + struct SwitchControlExtra: EventExtras { + var isRunning: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isRunning = self.isRunning { + record["is_running"] = String(isRunning) + } + + return record + } + } + + struct VoiceOverExtra: EventExtras { + var isRunning: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isRunning = self.isRunning { + record["is_running"] = String(isRunning) + } + + return record + } + } + + /// Records the Dynamic Text feature + static let dynamicText = EventMetricType( // generated from accessibility.dynamic_text + CommonMetricData( + category: "accessibility", + name: "dynamic_text", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_accessibility_size_enabled", "preferred_size"] + ) + + /// Records the Invert Colors feature + static let invertColors = EventMetricType( // generated from accessibility.invert_colors + CommonMetricData( + category: "accessibility", + name: "invert_colors", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + /// Records the Reduce Motion feature + static let reduceMotion = EventMetricType( // generated from accessibility.reduce_motion + CommonMetricData( + category: "accessibility", + name: "reduce_motion", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + /// Records the Reduce Transparency feature + static let reduceTransparency = EventMetricType( // generated from accessibility.reduce_transparency + CommonMetricData( + category: "accessibility", + name: "reduce_transparency", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + /// Records the Switch Control feature + static let switchControl = EventMetricType( // generated from accessibility.switch_control + CommonMetricData( + category: "accessibility", + name: "switch_control", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_running"] + ) + + /// Records the Voice Over feature + static let voiceOver = EventMetricType( // generated from accessibility.voice_over + CommonMetricData( + category: "accessibility", + name: "voice_over", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_running"] + ) + + } + + enum Addresses { + /// Recorded when the autofill prompt was dismissed. + static let autofillPromptDismissed = EventMetricType( // generated from addresses.autofill_prompt_dismissed + CommonMetricData( + category: "addresses", + name: "autofill_prompt_dismissed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when the autofill prompt was expanded. + static let autofillPromptExpanded = EventMetricType( // generated from addresses.autofill_prompt_expanded + CommonMetricData( + category: "addresses", + name: "autofill_prompt_expanded", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when autofill popup is shown, indicating which field triggered this + /// event. + static let autofillPromptShown = EventMetricType( // generated from addresses.autofill_prompt_shown + CommonMetricData( + category: "addresses", + name: "autofill_prompt_shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when a form is autofilled. + static let autofilled = EventMetricType( // generated from addresses.autofilled + CommonMetricData( + category: "addresses", + name: "autofilled", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when a form is recognized as an address form. + static let formDetected = EventMetricType( // generated from addresses.form_detected + CommonMetricData( + category: "addresses", + name: "form_detected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when a field is autofilled and then modified by the user. + static let modified = EventMetricType( // generated from addresses.modified + CommonMetricData( + category: "addresses", + name: "modified", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A counter of the number of all addresses that are currently saved by the user. + static let savedAll = QuantityMetricType( // generated from addresses.saved_all + CommonMetricData( + category: "addresses", + name: "saved_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when the user has tapped Autofill in the settings menu. + static let settingsAutofill = EventMetricType( // generated from addresses.settings_autofill + CommonMetricData( + category: "addresses", + name: "settings_autofill", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum Adjust { + struct DeeplinkReceivedExtra: EventExtras { + var receivedUrl: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let receivedUrl = self.receivedUrl { + record["received_url"] = String(receivedUrl) + } + + return record + } + } + + /// A string containing the Adjust ad group ID from which the user installed + /// Firefox-iOS. + static let adGroup = StringMetricType( // generated from adjust.ad_group + CommonMetricData( + category: "adjust", + name: "ad_group", + sendInPings: ["first-session", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A string containing the Adjust campaign ID from which the user installed + /// Firefox-iOS. + static let campaign = StringMetricType( // generated from adjust.campaign + CommonMetricData( + category: "adjust", + name: "campaign", + sendInPings: ["first-session", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A string containing the Adjust creative ID from which the user installed + /// Firefox-iOS. + static let creative = StringMetricType( // generated from adjust.creative + CommonMetricData( + category: "adjust", + name: "creative", + sendInPings: ["first-session", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Send for Adjust callback for deeplink. + static let deeplinkReceived = EventMetricType( // generated from adjust.deeplink_received + CommonMetricData( + category: "adjust", + name: "deeplink_received", + sendInPings: ["events", "first-session", "metrics"], + lifetime: .ping, + disabled: false + ) + , ["received_url"] + ) + + /// A string containing the Adjust network ID from which the user installed + /// Firefox-iOS. + static let network = StringMetricType( // generated from adjust.network + CommonMetricData( + category: "adjust", + name: "network", + sendInPings: ["first-session", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum App { + struct NotificationPermissionExtra: EventExtras { + var alertSetting: String? + var status: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let alertSetting = self.alertSetting { + record["alert_setting"] = String(alertSetting) + } + if let status = self.status { + record["status"] = String(status) + } + + return record + } + } + + /// The user installed the app via the browser choice screen + static let choiceScreenAcquisition = BooleanMetricType( // generated from app.choice_screen_acquisition + CommonMetricData( + category: "app", + name: "choice_screen_acquisition", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Is Firefox the default browser + static let defaultBrowser = BooleanMetricType( // generated from app.default_browser + CommonMetricData( + category: "app", + name: "default_browser", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records the status of the users notification permission. + static let notificationPermission = EventMetricType( // generated from app.notification_permission + CommonMetricData( + category: "app", + name: "notification_permission", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["alert_setting", "status"] + ) + + /// Counts the number of times the app is opened from an external + /// link, implying the client has Firefox set as a default browser. + /// + /// Currently this is our most accurate way of measuring how + /// often Firefox is set as the default browser. + static let openedAsDefaultBrowser = CounterMetricType( // generated from app.opened_as_default_browser + CommonMetricData( + category: "app", + name: "opened_as_default_browser", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum AppCycle { + /// Records when the app goes to background + static let background = EventMetricType( // generated from app_cycle.background + CommonMetricData( + category: "app_cycle", + name: "background", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the app comes to foreground + static let foreground = EventMetricType( // generated from app_cycle.foreground + CommonMetricData( + category: "app_cycle", + name: "foreground", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum AppErrors { + struct CpuExceptionExtra: EventExtras { + var size: Int32? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let size = self.size { + record["size"] = String(size) + } + + return record + } + } + + struct HangExceptionExtra: EventExtras { + var size: Int32? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let size = self.size { + record["size"] = String(size) + } + + return record + } + } + + struct LargeFileWriteExtra: EventExtras { + var size: Int32? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let size = self.size { + record["size"] = String(size) + } + + return record + } + } + + /// Recorded when a cpu exception is triggered + static let cpuException = EventMetricType( // generated from app_errors.cpu_exception + CommonMetricData( + category: "app_errors", + name: "cpu_exception", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["size"] + ) + + /// Recorded when the previous session ended as a result of a crash + static let crashedLastLaunch = EventMetricType( // generated from app_errors.crashed_last_launch + CommonMetricData( + category: "app_errors", + name: "crashed_last_launch", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when the main thread hangs + static let hangException = EventMetricType( // generated from app_errors.hang_exception + CommonMetricData( + category: "app_errors", + name: "hang_exception", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["size"] + ) + + /// Recorded when a very large file is written to disk + static let largeFileWrite = EventMetricType( // generated from app_errors.large_file_write + CommonMetricData( + category: "app_errors", + name: "large_file_write", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["size"] + ) + + /// Recorded when we detect potential tab loss + static let tabLossDetected = EventMetricType( // generated from app_errors.tab_loss_detected + CommonMetricData( + category: "app_errors", + name: "tab_loss_detected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum AppIcon { + /// A user opened a new private tab from long pressing the app icon + static let newPrivateTabTapped = EventMetricType( // generated from app_icon.new_private_tab_tapped + CommonMetricData( + category: "app_icon", + name: "new_private_tab_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum AppMenu { + struct CloseButtonExtra: EventExtras { + var isHomepage: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isHomepage = self.isHomepage { + record["is_homepage"] = String(isHomepage) + } + + return record + } + } + + struct MainMenuOptionSelectedExtra: EventExtras { + var isHomepage: Bool? + var option: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isHomepage = self.isHomepage { + record["is_homepage"] = String(isHomepage) + } + if let option = self.option { + record["option"] = String(option) + } + + return record + } + } + + struct MenuDismissedExtra: EventExtras { + var isHomepage: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isHomepage = self.isHomepage { + record["is_homepage"] = String(isHomepage) + } + + return record + } + } + + struct SaveMenuOptionSelectedExtra: EventExtras { + var isHomepage: Bool? + var option: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isHomepage = self.isHomepage { + record["is_homepage"] = String(isHomepage) + } + if let option = self.option { + record["option"] = String(option) + } + + return record + } + } + + struct ToolsMenuOptionSelectedExtra: EventExtras { + var isHomepage: Bool? + var option: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isHomepage = self.isHomepage { + record["is_homepage"] = String(isHomepage) + } + if let option = self.option { + record["option"] = String(option) + } + + return record + } + } + + /// Counts the number of times a user disables Block Images + /// in the app menu + static let blockImagesDisabled = CounterMetricType( // generated from app_menu.block_images_disabled + CommonMetricData( + category: "app_menu", + name: "block_images_disabled", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user enables Block Images + /// in the app menu + static let blockImagesEnabled = CounterMetricType( // generated from app_menu.block_images_enabled + CommonMetricData( + category: "app_menu", + name: "block_images_enabled", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record event when user has tapped the close menu button. + static let closeButton = EventMetricType( // generated from app_menu.close_button + CommonMetricData( + category: "app_menu", + name: "close_button", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_homepage"] + ) + + /// Counts the number of times a user taps Customize Homepage + /// in the app menu + static let customizeHomepage = CounterMetricType( // generated from app_menu.customize_homepage + CommonMetricData( + category: "app_menu", + name: "customize_homepage", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Help in the app menu + static let help = CounterMetricType( // generated from app_menu.help + CommonMetricData( + category: "app_menu", + name: "help", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Home in the app menu + static let home = CounterMetricType( // generated from app_menu.home + CommonMetricData( + category: "app_menu", + name: "home", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps in the menu toolbar + /// on the homepage + static let homepageMenu = CounterMetricType( // generated from app_menu.homepage_menu + CommonMetricData( + category: "app_menu", + name: "homepage_menu", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Logins & Passwords + /// in the app menu + static let logins = CounterMetricType( // generated from app_menu.logins + CommonMetricData( + category: "app_menu", + name: "logins", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record event when user has tapped on the main menu option. + static let mainMenuOptionSelected = EventMetricType( // generated from app_menu.main_menu_option_selected + CommonMetricData( + category: "app_menu", + name: "main_menu_option_selected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_homepage", "option"] + ) + + /// Record event when user has dismissed the menu because of tapping outside or + /// drag the menu. + static let menuDismissed = EventMetricType( // generated from app_menu.menu_dismissed + CommonMetricData( + category: "app_menu", + name: "menu_dismissed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_homepage"] + ) + + /// Counts the number of times a user taps Disable Night Mode + /// in the app menu + static let nightModeDisabled = CounterMetricType( // generated from app_menu.night_mode_disabled + CommonMetricData( + category: "app_menu", + name: "night_mode_disabled", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Enable Night Mode + /// in the app menu + static let nightModeEnabled = CounterMetricType( // generated from app_menu.night_mode_enabled + CommonMetricData( + category: "app_menu", + name: "night_mode_enabled", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records when the user taps Passwords in the app menu + static let passwords = EventMetricType( // generated from app_menu.passwords + CommonMetricData( + category: "app_menu", + name: "passwords", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Record event when user has tapped on a save sub menu option. + static let saveMenuOptionSelected = EventMetricType( // generated from app_menu.save_menu_option_selected + CommonMetricData( + category: "app_menu", + name: "save_menu_option_selected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_homepage", "option"] + ) + + /// Counts the number of times a user taps Settings in the app menu + static let settings = CounterMetricType( // generated from app_menu.settings + CommonMetricData( + category: "app_menu", + name: "settings", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Sign Into Sync + /// in the app menu + static let signIntoSync = CounterMetricType( // generated from app_menu.sign_into_sync + CommonMetricData( + category: "app_menu", + name: "sign_into_sync", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps in the menu toolbar + /// from a website + static let siteMenu = CounterMetricType( // generated from app_menu.site_menu + CommonMetricData( + category: "app_menu", + name: "site_menu", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record event when user has tapped on a tools sub menu option. + static let toolsMenuOptionSelected = EventMetricType( // generated from app_menu.tools_menu_option_selected + CommonMetricData( + category: "app_menu", + name: "tools_menu_option_selected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_homepage", "option"] + ) + + /// Counts the number of times a user taps What's New in the app menu + static let whatsNew = CounterMetricType( // generated from app_menu.whats_new + CommonMetricData( + category: "app_menu", + name: "whats_new", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Awesomebar { + struct LocationExtra: EventExtras { + var location: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let location = self.location { + record["location"] = String(location) + } + + return record + } + } + + struct SearchResultImpressionExtra: EventExtras { + var type: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let type = self.type { + record["type"] = String(type) + } + + return record + } + } + + struct SearchResultTapExtra: EventExtras { + var type: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let type = self.type { + record["type"] = String(type) + } + + return record + } + } + + /// Records when dragging the location bar + static let dragLocationBar = EventMetricType( // generated from awesomebar.drag_location_bar + CommonMetricData( + category: "awesomebar", + name: "drag_location_bar", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records location of awesome bar when + /// user opens the app + static let location = EventMetricType( // generated from awesomebar.location + CommonMetricData( + category: "awesomebar", + name: "location", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["location"] + ) + + /// The time a query against awesomebar took. This helps us understand the + /// performance of the awesomebar in querying history and bookmarks. The query time + /// will also help us verify that we are **not** introducing any performance + /// regressions. + static let queryTime = TimingDistributionMetricType( // generated from awesomebar.query_time + CommonMetricData( + category: "awesomebar", + name: "query_time", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + , .millisecond + ) + + /// Recorded for an item that was visible in the list of search results + /// when the user finished interacting with the awesomebar. + static let searchResultImpression = EventMetricType( // generated from awesomebar.search_result_impression + CommonMetricData( + category: "awesomebar", + name: "search_result_impression", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["type"] + ) + + /// Record type of search item tap from the + /// list of results of awesomebar search. + static let searchResultTap = EventMetricType( // generated from awesomebar.search_result_tap + CommonMetricData( + category: "awesomebar", + name: "search_result_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["type"] + ) + + /// Counts the number of times a user taps share + /// button on the awesomebar + static let shareButtonTapped = EventMetricType( // generated from awesomebar.share_button_tapped + CommonMetricData( + category: "awesomebar", + name: "share_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum Bookmarks { + private static let addLabel = CounterMetricType( // generated from bookmarks.add + CommonMetricData( + category: "bookmarks", + name: "add", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a bookmark is added from the + /// following: + /// * Page Action Menu + /// * Share Menu + static let add = try! LabeledMetricType( // generated from bookmarks.add + category: "bookmarks", + name: "add", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: addLabel, + labels: ["activity-stream", "page-action-menu", "share-menu"] + ) + + private static let deleteLabel = CounterMetricType( // generated from bookmarks.delete + CommonMetricData( + category: "bookmarks", + name: "delete", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a bookmark is deleted from + /// the following: + /// * Page Action Menu + /// * Activity Stream + /// * Bookmarks Panel + static let delete = try! LabeledMetricType( // generated from bookmarks.delete + category: "bookmarks", + name: "delete", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: deleteLabel, + labels: ["activity-stream", "bookmarks-panel", "page-action-menu"] + ) + + private static let editLabel = CounterMetricType( // generated from bookmarks.edit + CommonMetricData( + category: "bookmarks", + name: "edit", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a bookmark is tapped to + /// be edited from: + /// * Add bookmark toast Edit button + /// * Bookmarks panel edit bookmarks view + static let edit = try! LabeledMetricType( // generated from bookmarks.edit + category: "bookmarks", + name: "edit", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: editLabel, + labels: ["add-bookmark-toast", "bookmarks-panel"] + ) + + /// A user added a new bookmark folder. + static let folderAdd = EventMetricType( // generated from bookmarks.folder_add + CommonMetricData( + category: "bookmarks", + name: "folder_add", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A boolean that indicates if the user has bookmarks + /// in the mobile folder. + static let hasMobileBookmarks = BooleanMetricType( // generated from bookmarks.has_mobile_bookmarks + CommonMetricData( + category: "bookmarks", + name: "has_mobile_bookmarks", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A quantity that indicates how many bookmarks a user + /// has in the mobile folder. + static let mobileBookmarksCount = QuantityMetricType( // generated from bookmarks.mobile_bookmarks_count + CommonMetricData( + category: "bookmarks", + name: "mobile_bookmarks_count", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let openLabel = CounterMetricType( // generated from bookmarks.open + CommonMetricData( + category: "bookmarks", + name: "open", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a bookmark is opened from + /// the following: + /// * Awesomebar results + /// * Bookmarks Panel + /// * Top sites + static let open = try! LabeledMetricType( // generated from bookmarks.open + category: "bookmarks", + name: "open", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: openLabel, + labels: ["awesomebar-results", "bookmarks-panel"] + ) + + private static let viewListLabel = CounterMetricType( // generated from bookmarks.view_list + CommonMetricData( + category: "bookmarks", + name: "view_list", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times the bookmarks list is opened + /// from either the Home Panel tab button or the App Menu. + static let viewList = try! LabeledMetricType( // generated from bookmarks.view_list + category: "bookmarks", + name: "view_list", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: viewListLabel, + labels: ["app-menu"] + ) + + } + + enum BrowserSearch { + private static let adClicksLabel = CounterMetricType( // generated from browser_search.ad_clicks + CommonMetricData( + category: "browser_search", + name: "ad_clicks", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records clicks of adverts on SERP pages. + /// The key format is ``. + static let adClicks = try! LabeledMetricType( // generated from browser_search.ad_clicks + category: "browser_search", + name: "ad_clicks", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false, + subMetric: adClicksLabel, + labels: nil + ) + + private static let withAdsLabel = CounterMetricType( // generated from browser_search.with_ads + CommonMetricData( + category: "browser_search", + name: "with_ads", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records counts of SERP pages with adverts displayed. + /// The key format is ``. + static let withAds = try! LabeledMetricType( // generated from browser_search.with_ads + category: "browser_search", + name: "with_ads", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false, + subMetric: withAdsLabel, + labels: nil + ) + + } + + enum CfrAnalytics { + struct DismissCfrFromButtonExtra: EventExtras { + var hintType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let hintType = self.hintType { + record["hint_type"] = String(hintType) + } + + return record + } + } + + struct DismissCfrFromOutsideTapExtra: EventExtras { + var hintType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let hintType = self.hintType { + record["hint_type"] = String(hintType) + } + + return record + } + } + + struct PressCfrActionButtonExtra: EventExtras { + var hintType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let hintType = self.hintType { + record["hint_type"] = String(hintType) + } + + return record + } + } + + /// Recorded when the user dismisses a CFR by tapping + /// the close button in the CFR. Hint type is sent + /// in the extra keys. + static let dismissCfrFromButton = EventMetricType( // generated from cfr_analytics.dismiss_cfr_from_button + CommonMetricData( + category: "cfr_analytics", + name: "dismiss_cfr_from_button", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["hint_type"] + ) + + /// Recorded when the user dismisses a CFR by tapping + /// outside the CFR. Hint type is sent in the extra keys. + static let dismissCfrFromOutsideTap = EventMetricType( // generated from cfr_analytics.dismiss_cfr_from_outside_tap + CommonMetricData( + category: "cfr_analytics", + name: "dismiss_cfr_from_outside_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["hint_type"] + ) + + /// Recorded when the user taps CFR's action button. + /// Hint type is sent in the extra keys. + static let pressCfrActionButton = EventMetricType( // generated from cfr_analytics.press_cfr_action_button + CommonMetricData( + category: "cfr_analytics", + name: "press_cfr_action_button", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["hint_type"] + ) + + } + + enum CreditCard { + struct AutofillToggleExtra: EventExtras { + var isEnabled: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + struct SyncToggleExtra: EventExtras { + var isEnabled: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + /// Recorded on startup to check if credit card + /// autofill settings are enabled + static let autofillEnabled = BooleanMetricType( // generated from credit_card.autofill_enabled + CommonMetricData( + category: "credit_card", + name: "autofill_enabled", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + /// Recorded when a user taps a card from bottom sheet + /// select a card flow and the credit card does not + /// get autofilled on the webpage + static let autofillFailed = EventMetricType( // generated from credit_card.autofill_failed + CommonMetricData( + category: "credit_card", + name: "autofill_failed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the credit card autofill prompt was dismissed. + static let autofillPromptDismissed = EventMetricType( // generated from credit_card.autofill_prompt_dismissed + CommonMetricData( + category: "credit_card", + name: "autofill_prompt_dismissed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the credit card autofill prompt was expanded. + static let autofillPromptExpanded = EventMetricType( // generated from credit_card.autofill_prompt_expanded + CommonMetricData( + category: "credit_card", + name: "autofill_prompt_expanded", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the credit card autofill prompt was shown. + static let autofillPromptShown = EventMetricType( // generated from credit_card.autofill_prompt_shown + CommonMetricData( + category: "credit_card", + name: "autofill_prompt_shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when the user taps on credit card autofill + /// settings item in settings screen. + static let autofillSettingsTapped = EventMetricType( // generated from credit_card.autofill_settings_tapped + CommonMetricData( + category: "credit_card", + name: "autofill_settings_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when a user toggles to enable save and autofill + /// cards in autofill settings + static let autofillToggle = EventMetricType( // generated from credit_card.autofill_toggle + CommonMetricData( + category: "credit_card", + name: "autofill_toggle", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + /// Recorded when a user taps a card from bottom sheet + /// select a card flow and the credit card gets + /// autofilled on the webpage + static let autofilled = EventMetricType( // generated from credit_card.autofilled + CommonMetricData( + category: "credit_card", + name: "autofilled", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A counter of the number of credit cards that have been deleted by the user. + static let deleted = CounterMetricType( // generated from credit_card.deleted + CommonMetricData( + category: "credit_card", + name: "deleted", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when the user taps on credit card form input + /// and we detect it. + static let formDetected = EventMetricType( // generated from credit_card.form_detected + CommonMetricData( + category: "credit_card", + name: "form_detected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the user has tapped the add button through credit card management + /// settings. + static let managementAddTapped = EventMetricType( // generated from credit_card.management_add_tapped + CommonMetricData( + category: "credit_card", + name: "management_add_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the user has tapped on a saved card through credit card management + /// settings. + static let managementCardTapped = EventMetricType( // generated from credit_card.management_card_tapped + CommonMetricData( + category: "credit_card", + name: "management_card_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A counter of the number of credit cards that have been modified by the user. + static let modified = CounterMetricType( // generated from credit_card.modified + CommonMetricData( + category: "credit_card", + name: "modified", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when a user saved a credit card using + /// the autofill save prompt. + static let savePromptCreate = EventMetricType( // generated from credit_card.save_prompt_create + CommonMetricData( + category: "credit_card", + name: "save_prompt_create", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the credit card autofill save prompt is shown. + static let savePromptShown = EventMetricType( // generated from credit_card.save_prompt_shown + CommonMetricData( + category: "credit_card", + name: "save_prompt_shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the user updated a credit card using the autofill save prompt. + static let savePromptUpdate = EventMetricType( // generated from credit_card.save_prompt_update + CommonMetricData( + category: "credit_card", + name: "save_prompt_update", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A counter of the number of credit cards that have been saved by the user. + static let saved = CounterMetricType( // generated from credit_card.saved + CommonMetricData( + category: "credit_card", + name: "saved", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record the number of ALL the credit cards that have been currently stored by + /// the user. + static let savedAll = QuantityMetricType( // generated from credit_card.saved_all + CommonMetricData( + category: "credit_card", + name: "saved_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded on startup to check if credit card + /// sync settings are enabled + static let syncEnabled = BooleanMetricType( // generated from credit_card.sync_enabled + CommonMetricData( + category: "credit_card", + name: "sync_enabled", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + /// Recorded when a user toggles to enable save and autofill + /// cards in sync settings + static let syncToggle = EventMetricType( // generated from credit_card.sync_toggle + CommonMetricData( + category: "credit_card", + name: "sync_toggle", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + } + + enum DefaultBrowserCard { + /// Counts the number of times default browser card is dismissed. + static let dismissPressed = CounterMetricType( // generated from default_browser_card.dismiss_pressed + CommonMetricData( + category: "default_browser_card", + name: "dismiss_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Reports events of Home Tab Banner evergreen impressions. + static let evergreenImpression = EventMetricType( // generated from default_browser_card.evergreen_impression + CommonMetricData( + category: "default_browser_card", + name: "evergreen_impression", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times the Go To Settings button on + /// default browser card is clicked. + static let goToSettingsPressed = CounterMetricType( // generated from default_browser_card.go_to_settings_pressed + CommonMetricData( + category: "default_browser_card", + name: "go_to_settings_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum DefaultBrowserOnboarding { + /// Counts the number of times default browser onboarding is dismissed. + static let dismissPressed = CounterMetricType( // generated from default_browser_onboarding.dismiss_pressed + CommonMetricData( + category: "default_browser_onboarding", + name: "dismiss_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times the Go To Settings button on + /// default browser onboarding is clicked. + static let goToSettingsPressed = CounterMetricType( // generated from default_browser_onboarding.go_to_settings_pressed + CommonMetricData( + category: "default_browser_onboarding", + name: "go_to_settings_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Deletion { + /// The FxA device id. + static let syncDeviceId = StringMetricType( // generated from deletion.sync_device_id + CommonMetricData( + category: "deletion", + name: "sync_device_id", + sendInPings: ["deletion-request"], + lifetime: .user, + disabled: false + ) + ) + + } + + enum Device { + /// True if the device support device owner authentication + /// with either biometrics or a passcode. + static let authentication = BooleanMetricType( // generated from device.authentication + CommonMetricData( + category: "device", + name: "authentication", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + } + + enum Downloads { + /// Records when the download now button is pressed + static let downloadNowButtonTapped = EventMetricType( // generated from downloads.download_now_button_tapped + CommonMetricData( + category: "downloads", + name: "download_now_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when a row is pressed in the downloads panel + static let downloadsPanelRowTapped = EventMetricType( // generated from downloads.downloads_panel_row_tapped + CommonMetricData( + category: "downloads", + name: "downloads_panel_row_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the toast message is tapped after a download is completed + static let viewDownloadCompleteToast = EventMetricType( // generated from downloads.view_download_complete_toast + CommonMetricData( + category: "downloads", + name: "view_download_complete_toast", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum FirefoxHomePage { + struct HistoryHighlightsContextExtra: EventExtras { + var type: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let type = self.type { + record["type"] = String(type) + } + + return record + } + } + + struct ReadingListViewExtra: EventExtras { + var readingListCount: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let readingListCount = self.readingListCount { + record["reading_list_count"] = String(readingListCount) + } + + return record + } + } + + struct RecentlySavedBookmarkViewExtra: EventExtras { + var bookmarkCount: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let bookmarkCount = self.bookmarkCount { + record["bookmark_count"] = String(bookmarkCount) + } + + return record + } + } + + /// Counts the number of times a user taps to open the + /// settings menu to customize the Firefox Homepage + static let customizeHomepageButton = CounterMetricType( // generated from firefox_home_page.customize_homepage_button + CommonMetricData( + category: "firefox_home_page", + name: "customize_homepage_button", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let firefoxHomepageOriginLabel = CounterMetricType( // generated from firefox_home_page.firefox_homepage_origin + CommonMetricData( + category: "firefox_home_page", + name: "firefox_homepage_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The number of times a user sees the Firefox Homepage, + /// with a home page origin. + static let firefoxHomepageOrigin = try! LabeledMetricType( // generated from firefox_home_page.firefox_homepage_origin + category: "firefox_home_page", + name: "firefox_homepage_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: firefoxHomepageOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// The number of times a user sees the Firefox Homepage + static let firefoxHomepageView = CounterMetricType( // generated from firefox_home_page.firefox_homepage_view + CommonMetricData( + category: "firefox_home_page", + name: "firefox_homepage_view", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records when a user clicks on a contextual + /// menu option on a history highlight + static let historyHighlightsContext = EventMetricType( // generated from firefox_home_page.history_highlights_context + CommonMetricData( + category: "firefox_home_page", + name: "history_highlights_context", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["type"] + ) + + /// Records the event where a user taps on a group item + /// within the History Highlights (Recently Visited) section. + static let historyHighlightsGroupOpen = EventMetricType( // generated from firefox_home_page.history_highlights_group_open + CommonMetricData( + category: "firefox_home_page", + name: "history_highlights_group_open", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user taps to open an + /// existing tab from the History Highlights section + static let historyHighlightsItemOpened = EventMetricType( // generated from firefox_home_page.history_highlights_item_opened + CommonMetricData( + category: "firefox_home_page", + name: "history_highlights_item_opened", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user taps the + /// Show All button in the History section + static let historyHighlightsShowAll = CounterMetricType( // generated from firefox_home_page.history_highlights_show_all + CommonMetricData( + category: "firefox_home_page", + name: "history_highlights_show_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records the number of times a user views + /// history highlights section + static let historyImpressions = EventMetricType( // generated from firefox_home_page.history_impressions + CommonMetricData( + category: "firefox_home_page", + name: "history_impressions", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + private static let jumpBackInGroupOpenOriginLabel = CounterMetricType( // generated from firefox_home_page.jump_back_in_group_open_origin + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_group_open_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps to open an + /// existing group from the Jump Back In section, + /// with a home page origin. + static let jumpBackInGroupOpenOrigin = try! LabeledMetricType( // generated from firefox_home_page.jump_back_in_group_open_origin + category: "firefox_home_page", + name: "jump_back_in_group_open_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: jumpBackInGroupOpenOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a user taps to open an + /// existing group from the Jump Back In section + static let jumpBackInGroupOpened = CounterMetricType( // generated from firefox_home_page.jump_back_in_group_opened + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_group_opened", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps the + /// Show All button in the Jump Back In section. + static let jumpBackInShowAll = CounterMetricType( // generated from firefox_home_page.jump_back_in_show_all + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_show_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let jumpBackInShowAllOriginLabel = CounterMetricType( // generated from firefox_home_page.jump_back_in_show_all_origin + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_show_all_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps the + /// Show All button in the Jump Back In section, + /// with a home page origin. + static let jumpBackInShowAllOrigin = try! LabeledMetricType( // generated from firefox_home_page.jump_back_in_show_all_origin + category: "firefox_home_page", + name: "jump_back_in_show_all_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: jumpBackInShowAllOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a user taps to open an + /// existing tab from the Jump Back In section + static let jumpBackInTabOpened = CounterMetricType( // generated from firefox_home_page.jump_back_in_tab_opened + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_tab_opened", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let jumpBackInTabOpenedOriginLabel = CounterMetricType( // generated from firefox_home_page.jump_back_in_tab_opened_origin + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_tab_opened_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps to open an + /// existing tab from the Jump Back In section, + /// with a home page origin. + static let jumpBackInTabOpenedOrigin = try! LabeledMetricType( // generated from firefox_home_page.jump_back_in_tab_opened_origin + category: "firefox_home_page", + name: "jump_back_in_tab_opened_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: jumpBackInTabOpenedOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a user sees a Jump Back In tile + /// in the Jump Back In section on the homepage + static let jumpBackInTileView = CounterMetricType( // generated from firefox_home_page.jump_back_in_tile_view + CommonMetricData( + category: "firefox_home_page", + name: "jump_back_in_tile_view", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts when a user opens Firefox Home + /// from awesomebar. + static let openFromAwesomebar = CounterMetricType( // generated from firefox_home_page.open_from_awesomebar + CommonMetricData( + category: "firefox_home_page", + name: "open_from_awesomebar", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts when user opens Firefox Home from + /// bottom right hamburger menu Home button + /// and New Tab button. + static let openFromMenuHomeButton = CounterMetricType( // generated from firefox_home_page.open_from_menu_home_button + CommonMetricData( + category: "firefox_home_page", + name: "open_from_menu_home_button", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Measures the state of the show Pocket stories preference. + static let pocketStoriesVisible = BooleanMetricType( // generated from firefox_home_page.pocket_stories_visible + CommonMetricData( + category: "firefox_home_page", + name: "pocket_stories_visible", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of reading list items + /// appearing in the Recently Saved section + /// on the Firefox home page. + static let readingListView = EventMetricType( // generated from firefox_home_page.reading_list_view + CommonMetricData( + category: "firefox_home_page", + name: "reading_list_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["reading_list_count"] + ) + + /// Counts the number of times a bookmarked item in + /// the Recently Saved section is tapped + static let recentlySavedBookmarkItem = CounterMetricType( // generated from firefox_home_page.recently_saved_bookmark_item + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_bookmark_item", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let recentlySavedBookmarkOriginLabel = CounterMetricType( // generated from firefox_home_page.recently_saved_bookmark_origin + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_bookmark_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a bookmarked item in + /// the Recently Saved section is tapped, + /// with a home page origin. + static let recentlySavedBookmarkOrigin = try! LabeledMetricType( // generated from firefox_home_page.recently_saved_bookmark_origin + category: "firefox_home_page", + name: "recently_saved_bookmark_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: recentlySavedBookmarkOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of bookmarked items appearing + /// in the Recently Saved section on the home page. + static let recentlySavedBookmarkView = EventMetricType( // generated from firefox_home_page.recently_saved_bookmark_view + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_bookmark_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["bookmark_count"] + ) + + private static let recentlySavedReadOriginLabel = CounterMetricType( // generated from firefox_home_page.recently_saved_read_origin + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_read_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a reading list + /// item in the Recently Saved section is tapped, + /// with a home page origin. + static let recentlySavedReadOrigin = try! LabeledMetricType( // generated from firefox_home_page.recently_saved_read_origin + category: "firefox_home_page", + name: "recently_saved_read_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: recentlySavedReadOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a reading list + /// item in the Recently Saved section is tapped. + static let recentlySavedReadingItem = CounterMetricType( // generated from firefox_home_page.recently_saved_reading_item + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_reading_item", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps the Show All + /// button of the Recently Saved Section + static let recentlySavedShowAll = CounterMetricType( // generated from firefox_home_page.recently_saved_show_all + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_show_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let recentlySavedShowAllOriginLabel = CounterMetricType( // generated from firefox_home_page.recently_saved_show_all_origin + CommonMetricData( + category: "firefox_home_page", + name: "recently_saved_show_all_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps the Show All + /// button of the Recently Saved Section, + /// with a home page origin. + static let recentlySavedShowAllOrigin = try! LabeledMetricType( // generated from firefox_home_page.recently_saved_show_all_origin + category: "firefox_home_page", + name: "recently_saved_show_all_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: recentlySavedShowAllOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a user taps to open a + /// synced tab from the Jump Back In section + static let syncedTabOpened = CounterMetricType( // generated from firefox_home_page.synced_tab_opened + CommonMetricData( + category: "firefox_home_page", + name: "synced_tab_opened", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let syncedTabOpenedOriginLabel = CounterMetricType( // generated from firefox_home_page.synced_tab_opened_origin + CommonMetricData( + category: "firefox_home_page", + name: "synced_tab_opened_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps to open a + /// synced tab from the Jump Back In section, + /// with a home page origin. + static let syncedTabOpenedOrigin = try! LabeledMetricType( // generated from firefox_home_page.synced_tab_opened_origin + category: "firefox_home_page", + name: "synced_tab_opened_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: syncedTabOpenedOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a user taps the + /// Show All button of Synced Tab in the Jump Back In section. + static let syncedTabShowAll = CounterMetricType( // generated from firefox_home_page.synced_tab_show_all + CommonMetricData( + category: "firefox_home_page", + name: "synced_tab_show_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let syncedTabShowAllOriginLabel = CounterMetricType( // generated from firefox_home_page.synced_tab_show_all_origin + CommonMetricData( + category: "firefox_home_page", + name: "synced_tab_show_all_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps the + /// Show All button of Synced Tab in the Jump Back In section, + /// with a home page origin. + static let syncedTabShowAllOrigin = try! LabeledMetricType( // generated from firefox_home_page.synced_tab_show_all_origin + category: "firefox_home_page", + name: "synced_tab_show_all_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: syncedTabShowAllOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Counts the number of times a user sees the + /// Synced Tab tile on the homepage + static let syncedTabTileView = CounterMetricType( // generated from firefox_home_page.synced_tab_tile_view + CommonMetricData( + category: "firefox_home_page", + name: "synced_tab_tile_view", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum FxSuggest { + /// The name of the advertiser providing the sponsored suggestion + static let advertiser = StringMetricType( // generated from fx_suggest.advertiser + CommonMetricData( + category: "fx_suggest", + name: "advertiser", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// A unique identifier for a sponsored suggestion. Not set for non-sponsored + /// suggestions. + static let blockId = QuantityMetricType( // generated from fx_suggest.block_id + CommonMetricData( + category: "fx_suggest", + name: "block_id", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// An identifier to identify users for Contextual Services user interaction pings. + static let contextId = UuidMetricType( // generated from fx_suggest.context_id + CommonMetricData( + category: "fx_suggest", + name: "context_id", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// The suggestion's advertising category. "22 - Shopping" for sponsored + /// suggestions. + /// Not set for non-sponsored suggestions. + static let iabCategory = StringMetricType( // generated from fx_suggest.iab_category + CommonMetricData( + category: "fx_suggest", + name: "iab_category", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// If `ping_type` is "fxsuggest-impression", indicates whether this impression is + /// for a clicked suggestion. If `ping_type` is "fxsuggest-click", always `true`. + static let isClicked = BooleanMetricType( // generated from fx_suggest.is_clicked + CommonMetricData( + category: "fx_suggest", + name: "is_clicked", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// The ping's type. Either "fxsuggest-click" or "fxsuggest-impression". + static let pingType = StringMetricType( // generated from fx_suggest.ping_type + CommonMetricData( + category: "fx_suggest", + name: "ping_type", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// The position (1-based) of this suggestion in the full list of suggestions, + /// relative to the top of the awesomebar. + static let position = QuantityMetricType( // generated from fx_suggest.position + CommonMetricData( + category: "fx_suggest", + name: "position", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + /// If this ping is for a sponsored suggestion, the partner URL for reporting this + /// interaction. + /// Not set for non-sponsored suggestions. + static let reportingUrl = UrlMetricType( // generated from fx_suggest.reporting_url + CommonMetricData( + category: "fx_suggest", + name: "reporting_url", + sendInPings: ["fx-suggest"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum History { + /// Recorded when a user taps on trash icon in history panel + static let deleteTap = EventMetricType( // generated from history.delete_tap + CommonMetricData( + category: "history", + name: "delete_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user has selected a + /// search term group inside the history panel. Selecting + /// this item will navigate them to a view containing a list + /// of all Site items that appear in that group. + static let groupList = CounterMetricType( // generated from history.group_list + CommonMetricData( + category: "history", + name: "group_list", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user opened the history screen. + static let opened = EventMetricType( // generated from history.opened + CommonMetricData( + category: "history", + name: "opened", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when a user opened a history item + static let openedItem = EventMetricType( // generated from history.opened_item + CommonMetricData( + category: "history", + name: "opened_item", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A user removed a history item. + static let removed = EventMetricType( // generated from history.removed + CommonMetricData( + category: "history", + name: "removed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A user removed all history items. + static let removedAll = EventMetricType( // generated from history.removed_all + CommonMetricData( + category: "history", + name: "removed_all", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A user removed history items opened that day. + static let removedToday = EventMetricType( // generated from history.removed_today + CommonMetricData( + category: "history", + name: "removed_today", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A user removed history items opened that + /// day and the day before. + static let removedTodayAndYesterday = EventMetricType( // generated from history.removed_today_and_yesterday + CommonMetricData( + category: "history", + name: "removed_today_and_yesterday", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when a user taps on search icon in history panel + static let searchTap = EventMetricType( // generated from history.search_tap + CommonMetricData( + category: "history", + name: "search_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + private static let selectedItemLabel = CounterMetricType( // generated from history.selected_item + CommonMetricData( + category: "history", + name: "selected_item", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user selected an item from + /// the history panel. This labeled counter will tell apart + /// items that appear inside a group, and those that do not. + static let selectedItem = try! LabeledMetricType( // generated from history.selected_item + category: "history", + name: "selected_item", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: selectedItemLabel, + labels: ["group-item", "non-grouped-item"] + ) + + } + + enum Homepage { + struct PrivateModeToggleExtra: EventExtras { + var isPrivateMode: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivateMode = self.isPrivateMode { + record["is_private_mode"] = String(isPrivateMode) + } + + return record + } + } + + /// Records when the private browsing mode icon is toggled on homepage. + static let privateModeToggle = EventMetricType( // generated from homepage.private_mode_toggle + CommonMetricData( + category: "homepage", + name: "private_mode_toggle", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private_mode"] + ) + + } + + enum InactiveTabsTray { + struct ToggleInactiveTabTrayExtra: EventExtras { + var toggleType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let toggleType = self.toggleType { + record["toggle_type"] = String(toggleType) + } + + return record + } + } + + /// Recorded when a user is shown inactive tab view + static let inactiveTabShown = CounterMetricType( // generated from inactive_tabs_tray.inactive_tab_shown + CommonMetricData( + category: "inactive_tabs_tray", + name: "inactive_tab_shown", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when a user swipes to close an inactive tab + static let inactiveTabSwipeClose = CounterMetricType( // generated from inactive_tabs_tray.inactive_tab_swipe_close + CommonMetricData( + category: "inactive_tabs_tray", + name: "inactive_tab_swipe_close", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when a user taps on close all inactive tabs button + static let inactiveTabsCloseAllBtn = CounterMetricType( // generated from inactive_tabs_tray.inactive_tabs_close_all_btn + CommonMetricData( + category: "inactive_tabs_tray", + name: "inactive_tabs_close_all_btn", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when a user taps on an inactive tab + static let openInactiveTab = CounterMetricType( // generated from inactive_tabs_tray.open_inactive_tab + CommonMetricData( + category: "inactive_tabs_tray", + name: "open_inactive_tab", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times user toggle (expand or collapse) + /// inactive tab section. + static let toggleInactiveTabTray = EventMetricType( // generated from inactive_tabs_tray.toggle_inactive_tab_tray + CommonMetricData( + category: "inactive_tabs_tray", + name: "toggle_inactive_tab_tray", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["toggle_type"] + ) + + } + + enum InstalledMozillaProducts { + /// If Focus is installed on the users's device. + static let focus = BooleanMetricType( // generated from installed_mozilla_products.focus + CommonMetricData( + category: "installed_mozilla_products", + name: "focus", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + /// If Klar is installed on the users's device. + static let klar = BooleanMetricType( // generated from installed_mozilla_products.klar + CommonMetricData( + category: "installed_mozilla_products", + name: "klar", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + } + + enum KeyCommands { + struct PressKeyCommandActionExtra: EventExtras { + var action: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let action = self.action { + record["action"] = String(action) + } + + return record + } + } + + /// Records when a key command action is triggered + static let pressKeyCommandAction = EventMetricType( // generated from key_commands.press_key_command_action + CommonMetricData( + category: "key_commands", + name: "press_key_command_action", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["action"] + ) + + } + + enum LegacyIds { + /// The client id from legacy telemetry. + static let clientId = UuidMetricType( // generated from legacy.ids.client_id + CommonMetricData( + category: "legacy.ids", + name: "client_id", + sendInPings: ["deletion-request", "metrics"], + lifetime: .user, + disabled: false + ) + ) + + } + + enum Library { + private static let panelPressedLabel = CounterMetricType( // generated from library.panel_pressed + CommonMetricData( + category: "library", + name: "panel_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a specific library panel + /// button is tapped for Bookmarks, History, Reading List, + /// Downloads and Synced tabs + static let panelPressed = try! LabeledMetricType( // generated from library.panel_pressed + category: "library", + name: "panel_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: panelPressedLabel, + labels: ["bookmarks-panel", "downloads-panel", "history-panel", "reading-panel", "sync-panel"] + ) + + } + + enum Logins { + struct SyncEnabledExtra: EventExtras { + var isEnabled: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + /// Records when the user has failed to autofill a password. + static let autofillFailed = EventMetricType( // generated from logins.autofill_failed + CommonMetricData( + category: "logins", + name: "autofill_failed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Password autofill prompt was dismissed. + static let autofillPromptDismissed = EventMetricType( // generated from logins.autofill_prompt_dismissed + CommonMetricData( + category: "logins", + name: "autofill_prompt_dismissed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Password autofill prompt was expanded. + static let autofillPromptExpanded = EventMetricType( // generated from logins.autofill_prompt_expanded + CommonMetricData( + category: "logins", + name: "autofill_prompt_expanded", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Password autofill prompt was shown. + static let autofillPromptShown = EventMetricType( // generated from logins.autofill_prompt_shown + CommonMetricData( + category: "logins", + name: "autofill_prompt_shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the user has autofilled a login. + static let autofilled = EventMetricType( // generated from logins.autofilled + CommonMetricData( + category: "logins", + name: "autofilled", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A counter of the number of passwords that have been deleted by the user. + static let deleted = CounterMetricType( // generated from logins.deleted + CommonMetricData( + category: "logins", + name: "deleted", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records when the user has tapped the add button through password management in + /// the settings menu + static let managementAddTapped = EventMetricType( // generated from logins.management_add_tapped + CommonMetricData( + category: "logins", + name: "management_add_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the user has tapped on a saved login through password management + /// in the setting menu. + static let managementLoginsTapped = EventMetricType( // generated from logins.management_logins_tapped + CommonMetricData( + category: "logins", + name: "management_logins_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A counter of the number of passwords that have been modified by the user. + static let modified = CounterMetricType( // generated from logins.modified + CommonMetricData( + category: "logins", + name: "modified", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A counter of the number of passwords that have been saved by the user. + static let saved = CounterMetricType( // generated from logins.saved + CommonMetricData( + category: "logins", + name: "saved", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record the number of ALL the passwords that have been currently stored by the + /// user. + static let savedAll = QuantityMetricType( // generated from logins.saved_all + CommonMetricData( + category: "logins", + name: "saved_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Whether the user has logins enabled in sync option + static let syncEnabled = EventMetricType( // generated from logins.sync_enabled + CommonMetricData( + category: "logins", + name: "sync_enabled", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + } + + enum Messaging { + struct ClickedExtra: EventExtras { + var actionUuid: String? + var messageKey: String? + var messageSurface: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let actionUuid = self.actionUuid { + record["action_uuid"] = String(actionUuid) + } + if let messageKey = self.messageKey { + record["message_key"] = String(messageKey) + } + if let messageSurface = self.messageSurface { + record["message_surface"] = String(messageSurface) + } + + return record + } + } + + struct DismissedExtra: EventExtras { + var messageKey: String? + var messageSurface: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let messageKey = self.messageKey { + record["message_key"] = String(messageKey) + } + if let messageSurface = self.messageSurface { + record["message_surface"] = String(messageSurface) + } + + return record + } + } + + struct ExpiredExtra: EventExtras { + var messageKey: String? + var messageSurface: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let messageKey = self.messageKey { + record["message_key"] = String(messageKey) + } + if let messageSurface = self.messageSurface { + record["message_surface"] = String(messageSurface) + } + + return record + } + } + + struct MalformedExtra: EventExtras { + var messageKey: String? + var messageSurface: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let messageKey = self.messageKey { + record["message_key"] = String(messageKey) + } + if let messageSurface = self.messageSurface { + record["message_surface"] = String(messageSurface) + } + + return record + } + } + + struct ShownExtra: EventExtras { + var messageKey: String? + var messageSurface: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let messageKey = self.messageKey { + record["message_key"] = String(messageKey) + } + if let messageSurface = self.messageSurface { + record["message_surface"] = String(messageSurface) + } + + return record + } + } + + /// A message was clicked by the user. + static let clicked = EventMetricType( // generated from messaging.clicked + CommonMetricData( + category: "messaging", + name: "clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["action_uuid", "message_key", "message_surface"] + ) + + /// A message was dismissed by the user. + static let dismissed = EventMetricType( // generated from messaging.dismissed + CommonMetricData( + category: "messaging", + name: "dismissed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["message_key", "message_surface"] + ) + + /// A message's max display count has been reached. + static let expired = EventMetricType( // generated from messaging.expired + CommonMetricData( + category: "messaging", + name: "expired", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["message_key", "message_surface"] + ) + + /// A message was malformed. + static let malformed = EventMetricType( // generated from messaging.malformed + CommonMetricData( + category: "messaging", + name: "malformed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["message_key", "message_surface"] + ) + + /// A message was shown to the user. + static let shown = EventMetricType( // generated from messaging.shown + CommonMetricData( + category: "messaging", + name: "shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["message_key", "message_surface"] + ) + + } + + enum Microsurvey { + struct ConfirmationShownExtra: EventExtras { + var surveyId: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let surveyId = self.surveyId { + record["survey_id"] = String(surveyId) + } + + return record + } + } + + struct DismissButtonTappedExtra: EventExtras { + var surveyId: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let surveyId = self.surveyId { + record["survey_id"] = String(surveyId) + } + + return record + } + } + + struct PrivacyNoticeTappedExtra: EventExtras { + var surveyId: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let surveyId = self.surveyId { + record["survey_id"] = String(surveyId) + } + + return record + } + } + + struct ShownExtra: EventExtras { + var surveyId: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let surveyId = self.surveyId { + record["survey_id"] = String(surveyId) + } + + return record + } + } + + struct SubmitButtonTappedExtra: EventExtras { + var surveyId: String? + var userSelection: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let surveyId = self.surveyId { + record["survey_id"] = String(surveyId) + } + if let userSelection = self.userSelection { + record["user_selection"] = String(userSelection) + } + + return record + } + } + + /// Records that the confirmation message in the survey has been viewed by the + /// user. + static let confirmationShown = EventMetricType( // generated from microsurvey.confirmation_shown + CommonMetricData( + category: "microsurvey", + name: "confirmation_shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["survey_id"] + ) + + /// Records that the user tapped on the close button to dismiss the survey. + static let dismissButtonTapped = EventMetricType( // generated from microsurvey.dismiss_button_tapped + CommonMetricData( + category: "microsurvey", + name: "dismiss_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["survey_id"] + ) + + /// Records that the user tapped on the privacy notice. + static let privacyNoticeTapped = EventMetricType( // generated from microsurvey.privacy_notice_tapped + CommonMetricData( + category: "microsurvey", + name: "privacy_notice_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["survey_id"] + ) + + /// The survey surface (bottom sheet) was shown and visible. + static let shown = EventMetricType( // generated from microsurvey.shown + CommonMetricData( + category: "microsurvey", + name: "shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["survey_id"] + ) + + /// Records that the user tapped on the submit button to respond to the survey. + static let submitButtonTapped = EventMetricType( // generated from microsurvey.submit_button_tapped + CommonMetricData( + category: "microsurvey", + name: "submit_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["survey_id", "user_selection"] + ) + + } + + enum Migration { + /// Counts the number of times a user runs the + /// sd web image library cache cleanup + static let imageSdCacheCleanup = CounterMetricType( // generated from migration.image_sd_cache_cleanup + CommonMetricData( + category: "migration", + name: "image_sd_cache_cleanup", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Onboarding { + struct CardViewExtra: EventExtras { + var cardType: String? + var flowType: String? + var sequenceId: String? + var sequencePosition: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let cardType = self.cardType { + record["card_type"] = String(cardType) + } + if let flowType = self.flowType { + record["flow_type"] = String(flowType) + } + if let sequenceId = self.sequenceId { + record["sequence_id"] = String(sequenceId) + } + if let sequencePosition = self.sequencePosition { + record["sequence_position"] = String(sequencePosition) + } + + return record + } + } + + struct CloseTapExtra: EventExtras { + var cardType: String? + var flowType: String? + var sequenceId: String? + var sequencePosition: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let cardType = self.cardType { + record["card_type"] = String(cardType) + } + if let flowType = self.flowType { + record["flow_type"] = String(flowType) + } + if let sequenceId = self.sequenceId { + record["sequence_id"] = String(sequenceId) + } + if let sequencePosition = self.sequencePosition { + record["sequence_position"] = String(sequencePosition) + } + + return record + } + } + + struct MultipleChoiceButtonTapExtra: EventExtras { + var buttonAction: String? + var cardType: String? + var flowType: String? + var sequenceId: String? + var sequencePosition: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let buttonAction = self.buttonAction { + record["button_action"] = String(buttonAction) + } + if let cardType = self.cardType { + record["card_type"] = String(cardType) + } + if let flowType = self.flowType { + record["flow_type"] = String(flowType) + } + if let sequenceId = self.sequenceId { + record["sequence_id"] = String(sequenceId) + } + if let sequencePosition = self.sequencePosition { + record["sequence_position"] = String(sequencePosition) + } + + return record + } + } + + struct NotificationPermissionPromptExtra: EventExtras { + var granted: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let granted = self.granted { + record["granted"] = String(granted) + } + + return record + } + } + + struct PrimaryButtonTapExtra: EventExtras { + var buttonAction: String? + var cardType: String? + var flowType: String? + var sequenceId: String? + var sequencePosition: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let buttonAction = self.buttonAction { + record["button_action"] = String(buttonAction) + } + if let cardType = self.cardType { + record["card_type"] = String(cardType) + } + if let flowType = self.flowType { + record["flow_type"] = String(flowType) + } + if let sequenceId = self.sequenceId { + record["sequence_id"] = String(sequenceId) + } + if let sequencePosition = self.sequencePosition { + record["sequence_position"] = String(sequencePosition) + } + + return record + } + } + + struct SecondaryButtonTapExtra: EventExtras { + var buttonAction: String? + var cardType: String? + var flowType: String? + var sequenceId: String? + var sequencePosition: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let buttonAction = self.buttonAction { + record["button_action"] = String(buttonAction) + } + if let cardType = self.cardType { + record["card_type"] = String(cardType) + } + if let flowType = self.flowType { + record["flow_type"] = String(flowType) + } + if let sequenceId = self.sequenceId { + record["sequence_id"] = String(sequenceId) + } + if let sequencePosition = self.sequencePosition { + record["sequence_position"] = String(sequencePosition) + } + + return record + } + } + + struct WallpaperSelectedExtra: EventExtras { + var wallpaperName: String? + var wallpaperType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let wallpaperName = self.wallpaperName { + record["wallpaper_name"] = String(wallpaperName) + } + if let wallpaperType = self.wallpaperType { + record["wallpaper_type"] = String(wallpaperType) + } + + return record + } + } + + struct WallpaperSelectorSelectedExtra: EventExtras { + var wallpaperName: String? + var wallpaperType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let wallpaperName = self.wallpaperName { + record["wallpaper_name"] = String(wallpaperName) + } + if let wallpaperType = self.wallpaperType { + record["wallpaper_type"] = String(wallpaperType) + } + + return record + } + } + + /// Records when an onboarding card is shown + static let cardView = EventMetricType( // generated from onboarding.card_view + CommonMetricData( + category: "onboarding", + name: "card_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["card_type", "flow_type", "sequence_id", "sequence_position"] + ) + + /// Records when the user closes the onboarding. + static let closeTap = EventMetricType( // generated from onboarding.close_tap + CommonMetricData( + category: "onboarding", + name: "close_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["card_type", "flow_type", "sequence_id", "sequence_position"] + ) + + /// Records the cancelation of a scheduled engagement notification. + static let engagementNotificationCancel = EventMetricType( // generated from onboarding.engagement_notification_cancel + CommonMetricData( + category: "onboarding", + name: "engagement_notification_cancel", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records the users interaction with the engagement notification. + static let engagementNotificationTapped = EventMetricType( // generated from onboarding.engagement_notification_tapped + CommonMetricData( + category: "onboarding", + name: "engagement_notification_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when a multiple choice button is tapped during the onboarding + static let multipleChoiceButtonTap = EventMetricType( // generated from onboarding.multiple_choice_button_tap + CommonMetricData( + category: "onboarding", + name: "multiple_choice_button_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["button_action", "card_type", "flow_type", "sequence_id", "sequence_position"] + ) + + /// Records the users decision to enable/disable notifications. + static let notificationPermissionPrompt = EventMetricType( // generated from onboarding.notification_permission_prompt + CommonMetricData( + category: "onboarding", + name: "notification_permission_prompt", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["granted"] + ) + + /// Records when the primary button is tap during the onboarding + static let primaryButtonTap = EventMetricType( // generated from onboarding.primary_button_tap + CommonMetricData( + category: "onboarding", + name: "primary_button_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["button_action", "card_type", "flow_type", "sequence_id", "sequence_position"] + ) + + /// Records when the secondary button is tap during the onboarding + static let secondaryButtonTap = EventMetricType( // generated from onboarding.secondary_button_tap + CommonMetricData( + category: "onboarding", + name: "secondary_button_tap", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["button_action", "card_type", "flow_type", "sequence_id", "sequence_position"] + ) + + /// Recorded when the user selects a wallpaper from + /// the wallpaper onboarding screen. + static let wallpaperSelected = EventMetricType( // generated from onboarding.wallpaper_selected + CommonMetricData( + category: "onboarding", + name: "wallpaper_selected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["wallpaper_name", "wallpaper_type"] + ) + + /// Recorded when the wallpaper onboarding bottom sheet + /// is dismissed by the user. + static let wallpaperSelectorClose = EventMetricType( // generated from onboarding.wallpaper_selector_close + CommonMetricData( + category: "onboarding", + name: "wallpaper_selector_close", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when the user selects a wallpaper from + /// the wallpaper onboarding bottom sheet. Wallpaper information + /// is sent in the extra keys. + static let wallpaperSelectorSelected = EventMetricType( // generated from onboarding.wallpaper_selector_selected + CommonMetricData( + category: "onboarding", + name: "wallpaper_selector_selected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["wallpaper_name", "wallpaper_type"] + ) + + /// Recorded when the wallpaper onboarding bottom sheet is shown to the user. + static let wallpaperSelectorView = EventMetricType( // generated from onboarding.wallpaper_selector_view + CommonMetricData( + category: "onboarding", + name: "wallpaper_selector_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum PageActionMenu { + /// Counts the number of times a user taps Copy Address + /// in the page action menu + static let copyAddress = CounterMetricType( // generated from page_action_menu.copy_address + CommonMetricData( + category: "page_action_menu", + name: "copy_address", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user creates a new tab from the + /// page action menu. + static let createNewTab = CounterMetricType( // generated from page_action_menu.create_new_tab + CommonMetricData( + category: "page_action_menu", + name: "create_new_tab", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Find in Page + /// in the page action menu + static let findInPage = CounterMetricType( // generated from page_action_menu.find_in_page + CommonMetricData( + category: "page_action_menu", + name: "find_in_page", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Pin to Top Sites + /// in the page action menu + static let pinToTopSites = CounterMetricType( // generated from page_action_menu.pin_to_top_sites + CommonMetricData( + category: "page_action_menu", + name: "pin_to_top_sites", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Pin to Top Sites + /// in the page action menu + static let removePinnedSite = CounterMetricType( // generated from page_action_menu.remove_pinned_site + CommonMetricData( + category: "page_action_menu", + name: "remove_pinned_site", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Report Site Issue + /// in the page action menu + static let reportSiteIssue = CounterMetricType( // generated from page_action_menu.report_site_issue + CommonMetricData( + category: "page_action_menu", + name: "report_site_issue", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Request Desktop Site + /// in the page action menu + static let requestDesktopSite = CounterMetricType( // generated from page_action_menu.request_desktop_site + CommonMetricData( + category: "page_action_menu", + name: "request_desktop_site", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Request Mobile Site + /// in the page action menu + static let requestMobileSite = CounterMetricType( // generated from page_action_menu.request_mobile_site + CommonMetricData( + category: "page_action_menu", + name: "request_mobile_site", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Send to Device + /// in the page action menu + static let sendToDevice = CounterMetricType( // generated from page_action_menu.send_to_device + CommonMetricData( + category: "page_action_menu", + name: "send_to_device", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user taps Share Page With in the + /// page action menu + static let sharePageWith = CounterMetricType( // generated from page_action_menu.share_page_with + CommonMetricData( + category: "page_action_menu", + name: "share_page_with", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user navigates to the downloads panel + /// from the page action menu. + static let viewDownloadsPanel = CounterMetricType( // generated from page_action_menu.view_downloads_panel + CommonMetricData( + category: "page_action_menu", + name: "view_downloads_panel", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times a user navigates to the history panel + /// from the page action menu. + static let viewHistoryPanel = CounterMetricType( // generated from page_action_menu.view_history_panel + CommonMetricData( + category: "page_action_menu", + name: "view_history_panel", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum PasswordGenerator { + /// The "use password button" of the password generator bottom sheet was clicked. + static let filled = CounterMetricType( // generated from password_generator.filled + CommonMetricData( + category: "password_generator", + name: "filled", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The password generator bottom sheet was shown and is visible + static let shown = CounterMetricType( // generated from password_generator.shown + CommonMetricData( + category: "password_generator", + name: "shown", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum PlacesHistoryMigration { + /// A time distribution of how long it took for the migration to occur + static let duration = TimingDistributionMetricType( // generated from places_history_migration.duration + CommonMetricData( + category: "places_history_migration", + name: "duration", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + , .millisecond + ) + + /// A Rate of how often migrations end + static let migrationEndedRate = RateMetricType( // generated from places_history_migration.migration_ended_rate + CommonMetricData( + category: "places_history_migration", + name: "migration_ended_rate", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A Rate of how often migrations error out + static let migrationErrorRate = RateMetricType( // generated from places_history_migration.migration_error_rate + CommonMetricData( + category: "places_history_migration", + name: "migration_error_rate", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The number of visits migrated to the places.db + static let numMigrated = QuantityMetricType( // generated from places_history_migration.num_migrated + CommonMetricData( + category: "places_history_migration", + name: "num_migrated", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The number of visits expected to migrate to the places.db + static let numToMigrate = QuantityMetricType( // generated from places_history_migration.num_to_migrate + CommonMetricData( + category: "places_history_migration", + name: "num_to_migrate", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Pocket { + /// A user opens a new private tab based on a pocket story item + static let openInPrivateTab = EventMetricType( // generated from pocket.open_in_private_tab + CommonMetricData( + category: "pocket", + name: "open_in_private_tab", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + private static let openStoryOriginLabel = CounterMetricType( // generated from pocket.open_story_origin + CommonMetricData( + category: "pocket", + name: "open_story_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records when a user opens Pocket article from + /// Firefox Home Pocket feed, with a home page origin. + static let openStoryOrigin = try! LabeledMetricType( // generated from pocket.open_story_origin + category: "pocket", + name: "open_story_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: openStoryOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + private static let openStoryPositionLabel = CounterMetricType( // generated from pocket.open_story_position + CommonMetricData( + category: "pocket", + name: "open_story_position", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts when a user opens Pocket article from + /// Firefox Home Pocket feed. + /// The label is position of tile i.e. 0,1,2... + static let openStoryPosition = try! LabeledMetricType( // generated from pocket.open_story_position + category: "pocket", + name: "open_story_position", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: openStoryPositionLabel, + labels: nil + ) + + /// Counts when a user gets to pocket section + /// on Firefox Home + static let sectionImpressions = CounterMetricType( // generated from pocket.section_impressions + CommonMetricData( + category: "pocket", + name: "section_impressions", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Preferences { + struct ChangedExtra: EventExtras { + var changedTo: String? + var preference: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let changedTo = self.changedTo { + record["changed_to"] = String(changedTo) + } + if let preference = self.preference { + record["preference"] = String(preference) + } + + return record + } + } + + struct PrivateBrowsingButtonTappedExtra: EventExtras { + var isPrivate: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + /// Recorded when a preference is changed and includes the + /// preference that changed as well as the value changed to + /// recorded in the extra keys. + static let changed = EventMetricType( // generated from preferences.changed + CommonMetricData( + category: "preferences", + name: "changed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["changed_to", "preference"] + ) + + /// Measures the state of the "Close Private Tabs" preference. + static let closePrivateTabs = BooleanMetricType( // generated from preferences.close_private_tabs + CommonMetricData( + category: "preferences", + name: "close_private_tabs", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Measures the state of the fxa login. + static let fxaLoggedIn = BooleanMetricType( // generated from preferences.fxa_logged_in + CommonMetricData( + category: "preferences", + name: "fxa_logged_in", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The setting a user chooses as their Home page. By default, + /// it is initially set to Firefox Home. + static let homePageSetting = StringMetricType( // generated from preferences.home_page_setting + CommonMetricData( + category: "preferences", + name: "home_page_setting", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// An indication of whether the Jump back in section + /// is enabled to be displayed. + static let jumpBackIn = BooleanMetricType( // generated from preferences.jump_back_in + CommonMetricData( + category: "preferences", + name: "jump_back_in", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The name of the view that the user wants to see on new tabs. + /// For example History, Homepage or Blank. It is used to measure + /// usage of this feature, to see how effective feature promotion + /// campaigns are and to establish a baseline number for when we + /// introduce the new Activity Stream features. + static let newTabExperience = StringMetricType( // generated from preferences.new_tab_experience + CommonMetricData( + category: "preferences", + name: "new_tab_experience", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// What opening screen preference the user has selected under + /// "Customize Home". "homepage," "last tab," or + /// "homepage after 4 hours" default: "homepage after 4 hours". + static let openingScreen = StringMetricType( // generated from preferences.opening_screen + CommonMetricData( + category: "preferences", + name: "opening_screen", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// An indication of whether Pocket is enabled to be displayed. + static let pocket = BooleanMetricType( // generated from preferences.pocket + CommonMetricData( + category: "preferences", + name: "pocket", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records when the private browsing button is tapped + static let privateBrowsingButtonTapped = EventMetricType( // generated from preferences.private_browsing_button_tapped + CommonMetricData( + category: "preferences", + name: "private_browsing_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// An indication of whether the recently saved section + /// is enabled to be displayed. + static let recentlySaved = BooleanMetricType( // generated from preferences.recently_saved + CommonMetricData( + category: "preferences", + name: "recently_saved", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// An indication of whether the Recently visited section + /// is enabled to be displayed. + static let recentlyVisited = BooleanMetricType( // generated from preferences.recently_visited + CommonMetricData( + category: "preferences", + name: "recently_visited", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Measures the state of the "Save Logins" preference. + static let saveLogins = BooleanMetricType( // generated from preferences.save_logins + CommonMetricData( + category: "preferences", + name: "save_logins", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Measures the state of the "Show Clipboard Bar" preference. + static let showClipboardBar = BooleanMetricType( // generated from preferences.show_clipboard_bar + CommonMetricData( + category: "preferences", + name: "show_clipboard_bar", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// True if notifications for sync are allowed, otherwise false. + static let syncNotifs = BooleanMetricType( // generated from preferences.sync_notifs + CommonMetricData( + category: "preferences", + name: "sync_notifs", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// True if notifications for tips and features are allowed, otherwise false. + static let tipsAndFeaturesNotifs = BooleanMetricType( // generated from preferences.tips_and_features_notifs + CommonMetricData( + category: "preferences", + name: "tips_and_features_notifs", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum PrivateBrowsing { + struct DataClearanceIconTappedExtra: EventExtras { + var didConfirm: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let didConfirm = self.didConfirm { + record["did_confirm"] = String(didConfirm) + } + + return record + } + } + + /// Records when the private browsing button is tapped. + static let dataClearanceIconTapped = EventMetricType( // generated from private_browsing.data_clearance_icon_tapped + CommonMetricData( + category: "private_browsing", + name: "data_clearance_icon_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["did_confirm"] + ) + + } + + enum QrCode { + /// Counts the number of times a QR code is scanned. + static let scanned = CounterMetricType( // generated from qr_code.scanned + CommonMetricData( + category: "qr_code", + name: "scanned", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum ReaderMode { + /// Counts how many times the reader mode is closed. + static let close = CounterMetricType( // generated from reader_mode.close + CommonMetricData( + category: "reader_mode", + name: "close", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times the reader mode is opened. + static let open = CounterMetricType( // generated from reader_mode.open + CommonMetricData( + category: "reader_mode", + name: "open", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum ReadingList { + private static let addLabel = CounterMetricType( // generated from reading_list.add + CommonMetricData( + category: "reading_list", + name: "add", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times an item is added to the reading + /// list from the following: + /// * Reader Mode Toolbar + /// * Share Extension + /// * Page Action Menu + static let add = try! LabeledMetricType( // generated from reading_list.add + category: "reading_list", + name: "add", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: addLabel, + labels: ["page-action-menu", "reader-mode-toolbar", "share-extension"] + ) + + private static let deleteLabel = CounterMetricType( // generated from reading_list.delete + CommonMetricData( + category: "reading_list", + name: "delete", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times an item is added to the + /// reading list from the following: + /// * Reader Mode Toolbar + /// * Reading List Panel + static let delete = try! LabeledMetricType( // generated from reading_list.delete + category: "reading_list", + name: "delete", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: deleteLabel, + labels: ["page-action-menu", "reader-mode-toolbar", "reading-list-panel"] + ) + + /// Counts the number of times an item is opened from the + /// Reading List + static let open = CounterMetricType( // generated from reading_list.open + CommonMetricData( + category: "reading_list", + name: "open", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Search { + private static let countsLabel = CounterMetricType( // generated from search.counts + CommonMetricData( + category: "search", + name: "counts", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The labels for this counter are `{search-engine-name}.{source}` + /// + /// If the search engine is bundled with Firefox-iOS, then + /// `search-engine-name` will be the name of the search engine. If + /// it is a custom search engine, the value will be `custom`. + /// + /// The value of `source` will reflect the source from which the + /// search started. One of: + /// * quicksearch + /// * suggestion + /// * actionbar + static let counts = try! LabeledMetricType( // generated from search.counts + category: "search", + name: "counts", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false, + subMetric: countsLabel, + labels: nil + ) + + /// The default search engine identifier if the search engine is + /// pre-loaded with Firefox-iOS. If it's a custom search engine, + /// then the value will be 'custom'. + static let defaultEngine = StringMetricType( // generated from search.default_engine + CommonMetricData( + category: "search", + name: "default_engine", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .application, + disabled: false + ) + ) + + private static let googleTopsitePressedLabel = CounterMetricType( // generated from search.google_topsite_pressed + CommonMetricData( + category: "search", + name: "google_topsite_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times the google top site button + /// is pressed + static let googleTopsitePressed = try! LabeledMetricType( // generated from search.google_topsite_pressed + category: "search", + name: "google_topsite_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: googleTopsitePressedLabel, + labels: nil + ) + + private static let inContentLabel = CounterMetricType( // generated from search.in_content + CommonMetricData( + category: "search", + name: "in_content", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records the type of interaction a user has on SERP pages. + static let inContent = try! LabeledMetricType( // generated from search.in_content + category: "search", + name: "in_content", + sendInPings: ["baseline", "dau-reporting", "metrics"], + lifetime: .ping, + disabled: false, + subMetric: inContentLabel, + labels: nil + ) + + /// Counts the number of times the start search button is + /// pressed + static let startSearchPressed = CounterMetricType( // generated from search.start_search_pressed + CommonMetricData( + category: "search", + name: "start_search_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum ServerKnobs { + /// Temporary metric recorded at the same time as + /// "app_cycle.foreground" to validate that the Glean Server Knobs + /// functionality is working correctly. + static let validation = EventMetricType( // generated from server_knobs.validation + CommonMetricData( + category: "server_knobs", + name: "validation", + sendInPings: ["events"], + lifetime: .ping, + disabled: true + ) + , [] + ) + + } + + enum SettingsMenu { + /// Records when the user taps Passwords in the settings menu + static let passwords = EventMetricType( // generated from settings_menu.passwords + CommonMetricData( + category: "settings_menu", + name: "passwords", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times setting as default + /// browser menu option is tapped. + static let setAsDefaultBrowserPressed = CounterMetricType( // generated from settings_menu.set_as_default_browser_pressed + CommonMetricData( + category: "settings_menu", + name: "set_as_default_browser_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Metric recorded when a user taps on the Show Tour option + /// from the app settings menu. + static let showTourPressed = EventMetricType( // generated from settings_menu.show_tour_pressed + CommonMetricData( + category: "settings_menu", + name: "show_tour_pressed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum ShareSheet { + /// Counts the number of times a user taps copy + /// button from Share Sheet actions + static let copyLinkTapped = EventMetricType( // generated from share_sheet.copy_link_tapped + CommonMetricData( + category: "share_sheet", + name: "copy_link_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user taps Pocket icon + /// from Share Sheet apps available + static let pocketActionTapped = EventMetricType( // generated from share_sheet.pocket_action_tapped + CommonMetricData( + category: "share_sheet", + name: "pocket_action_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user taps Save to Pocket + /// from Share Sheet actions + static let saveToPocketTapped = EventMetricType( // generated from share_sheet.save_to_pocket_tapped + CommonMetricData( + category: "share_sheet", + name: "save_to_pocket_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user taps send to device + /// button from Share Sheet actions + static let sendDeviceTapped = EventMetricType( // generated from share_sheet.send_device_tapped + CommonMetricData( + category: "share_sheet", + name: "send_device_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum Shopping { + struct SurfaceAdsSettingToggledExtra: EventExtras { + var isEnabled: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + struct SurfaceClosedExtra: EventExtras { + var action: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let action = self.action { + record["action"] = String(action) + } + + return record + } + } + + struct SurfaceDisplayedExtra: EventExtras { + var size: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let size = self.size { + record["size"] = String(size) + } + + return record + } + } + + /// Records an event everytime a shopping CFR is shown on the screen + static let addressBarFeatureCalloutDisplayed = EventMetricType( // generated from shopping.address_bar_feature_callout_displayed + CommonMetricData( + category: "shopping", + name: "address_bar_feature_callout_displayed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the shopping icon from the address bar is tapped + static let addressBarIconClicked = EventMetricType( // generated from shopping.address_bar_icon_clicked + CommonMetricData( + category: "shopping", + name: "address_bar_icon_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the shopping icon from the address bar is displayed + static let addressBarIconDisplayed = EventMetricType( // generated from shopping.address_bar_icon_displayed + CommonMetricData( + category: "shopping", + name: "address_bar_icon_displayed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// On a supported product page, the review checker showed analysis, and + /// the ads exposure pref was enabled, or review checker ads were enabled, + /// and when we tried to fetch an ad from the ad server, an ad was available. + /// Does not indicate whether the ad was actually shown. + static let adsExposure = EventMetricType( // generated from shopping.ads_exposure + CommonMetricData( + category: "shopping", + name: "ads_exposure", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A count of the number of eligible product pages the user has visited. + static let productPageVisits = CounterMetricType( // generated from shopping.product_page_visits + CommonMetricData( + category: "shopping", + name: "product_page_visits", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// An ad shown in the fakespot bar was clicked. + static let surfaceAdsClicked = EventMetricType( // generated from shopping.surface_ads_clicked + CommonMetricData( + category: "shopping", + name: "surface_ads_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// An ad was shown and visible in the bottom sheet for 1.5 seconds. + static let surfaceAdsImpression = EventMetricType( // generated from shopping.surface_ads_impression + CommonMetricData( + category: "shopping", + name: "surface_ads_impression", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Whether the user has opted in for ads + static let surfaceAdsSettingToggled = EventMetricType( // generated from shopping.surface_ads_setting_toggled + CommonMetricData( + category: "shopping", + name: "surface_ads_setting_toggled", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + /// Records an event when the user taps on + /// analyzer button from the 'No Analysis Card'. + static let surfaceAnalyzeReviewsNoneAvailableClicked = EventMetricType( // generated from shopping.surface_analyze_reviews_none_available_clicked + CommonMetricData( + category: "shopping", + name: "surface_analyze_reviews_none_available_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the surface (bottom sheet) is dismissed + static let surfaceClosed = EventMetricType( // generated from shopping.surface_closed + CommonMetricData( + category: "shopping", + name: "surface_closed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["action"] + ) + + /// Records when the surface (bottom sheet) is displayed + static let surfaceDisplayed = EventMetricType( // generated from shopping.surface_displayed + CommonMetricData( + category: "shopping", + name: "surface_displayed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["size"] + ) + + /// Records when the learn more button in the shopping surface opt in is displayed + static let surfaceLearnMoreClicked = EventMetricType( // generated from shopping.surface_learn_more_clicked + CommonMetricData( + category: "shopping", + name: "surface_learn_more_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// On a supported product page, the review checker + /// showed analysis, and review checker ads were enabled, + /// but when we tried to fetch an ad from the ad server, + /// no ad was available. + static let surfaceNoAdsAvailable = EventMetricType( // generated from shopping.surface_no_ads_available + CommonMetricData( + category: "shopping", + name: "surface_no_ads_available", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records an event when the Fakespot API doesn't return a grade. + static let surfaceNoReviewReliabilityAvailable = EventMetricType( // generated from shopping.surface_no_review_reliability_available + CommonMetricData( + category: "shopping", + name: "surface_no_review_reliability_available", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the shopping surface opt in is dismissed from the not now button + static let surfaceNotNowClicked = EventMetricType( // generated from shopping.surface_not_now_clicked + CommonMetricData( + category: "shopping", + name: "surface_not_now_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the shopping surface onboarding is displayed + static let surfaceOnboardingDisplayed = EventMetricType( // generated from shopping.surface_onboarding_displayed + CommonMetricData( + category: "shopping", + name: "surface_onboarding_displayed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the shopping surface opt in is accepted + static let surfaceOptInAccepted = EventMetricType( // generated from shopping.surface_opt_in_accepted + CommonMetricData( + category: "shopping", + name: "surface_opt_in_accepted", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records an event when the user taps on + /// 'Review checker is powered by Fakespot by mozilla' link. + static let surfacePoweredByFakespotLinkClicked = EventMetricType( // generated from shopping.surface_powered_by_fakespot_link_clicked + CommonMetricData( + category: "shopping", + name: "surface_powered_by_fakespot_link_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records an event when a user reports a product as back in stock. + static let surfaceReactivatedButtonClicked = EventMetricType( // generated from shopping.surface_reactivated_button_clicked + CommonMetricData( + category: "shopping", + name: "surface_reactivated_button_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records an event when the user taps on + /// primary button from the 'Needs Analysis Card'. + static let surfaceReanalyzeClicked = EventMetricType( // generated from shopping.surface_reanalyze_clicked + CommonMetricData( + category: "shopping", + name: "surface_reanalyze_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the Settings Card's header or chevron button is tapped and the + /// card expands + static let surfaceSettingsExpandClicked = EventMetricType( // generated from shopping.surface_settings_expand_clicked + CommonMetricData( + category: "shopping", + name: "surface_settings_expand_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the show more button of the recent reviews card is clicked + static let surfaceShowMoreRecentReviewsClicked = EventMetricType( // generated from shopping.surface_show_more_recent_reviews_clicked + CommonMetricData( + category: "shopping", + name: "surface_show_more_recent_reviews_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the privacy policy is clicked in the shopping surface opt in + static let surfaceShowPrivacyPolicyClicked = EventMetricType( // generated from shopping.surface_show_privacy_policy_clicked + CommonMetricData( + category: "shopping", + name: "surface_show_privacy_policy_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the Learn more about how Fakespot determines review quality link + /// is tapped + static let surfaceShowQualityExplainerClicked = EventMetricType( // generated from shopping.surface_show_quality_explainer_clicked + CommonMetricData( + category: "shopping", + name: "surface_show_quality_explainer_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when the terms of use is clicked in the shopping surface opt in + static let surfaceShowTermsClicked = EventMetricType( // generated from shopping.surface_show_terms_clicked + CommonMetricData( + category: "shopping", + name: "surface_show_terms_clicked", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records an event when the "New info to check" card is shown. + static let surfaceStaleAnalysisShown = EventMetricType( // generated from shopping.surface_stale_analysis_shown + CommonMetricData( + category: "shopping", + name: "surface_stale_analysis_shown", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum ShoppingSettings { + /// Indicates if the user has opted out of using the shopping component. + /// Set in BrowserViewController and updated when changed by user. + static let componentOptedOut = BooleanMetricType( // generated from shopping.settings.component_opted_out + CommonMetricData( + category: "shopping.settings", + name: "component_opted_out", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + /// Indicates if the user has opted out of being shown ads in the Fakespot screen + /// experience. Set in BrowserViewController and updated when changed by user. + static let disabledAds = BooleanMetricType( // generated from shopping.settings.disabled_ads + CommonMetricData( + category: "shopping.settings", + name: "disabled_ads", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + /// Indicates if Nimbus has disabled the use the shopping component. + static let nimbusDisabledShopping = BooleanMetricType( // generated from shopping.settings.nimbus_disabled_shopping + CommonMetricData( + category: "shopping.settings", + name: "nimbus_disabled_shopping", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + /// Indicates if the user has completed the Shopping product Onboarding + /// experience. Set in BrowserViewController and updated when changed by user. + static let userHasOnboarded = BooleanMetricType( // generated from shopping.settings.user_has_onboarded + CommonMetricData( + category: "shopping.settings", + name: "user_has_onboarded", + sendInPings: ["metrics"], + lifetime: .application, + disabled: false + ) + ) + + } + + enum Sync { + /// Counts the number of times a user taps + /// on create account button in sync library view + static let createAccountPressed = CounterMetricType( // generated from sync.create_account_pressed + CommonMetricData( + category: "sync", + name: "create_account_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user tapped disconnect sync in fxa page. + static let disconnect = EventMetricType( // generated from sync.disconnect + CommonMetricData( + category: "sync", + name: "disconnect", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when user successfully logs in + static let loginCompletedView = EventMetricType( // generated from sync.login_completed_view + CommonMetricData( + category: "sync", + name: "login_completed_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when user is on fxa webpage to confirm signin token + static let loginTokenView = EventMetricType( // generated from sync.login_token_view + CommonMetricData( + category: "sync", + name: "login_token_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when user opens login fxa page + static let loginView = EventMetricType( // generated from sync.login_view + CommonMetricData( + category: "sync", + name: "login_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user opens + /// synced tab + static let openTab = CounterMetricType( // generated from sync.open_tab + CommonMetricData( + category: "sync", + name: "open_tab", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user scanned QR code to attempt to sign in. + static let paired = EventMetricType( // generated from sync.paired + CommonMetricData( + category: "sync", + name: "paired", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when user is on fxa webpage to confirm signup code + static let registrationCodeView = EventMetricType( // generated from sync.registration_code_view + CommonMetricData( + category: "sync", + name: "registration_code_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when user opens successfully completes registration + static let registrationCompletedView = EventMetricType( // generated from sync.registration_completed_view + CommonMetricData( + category: "sync", + name: "registration_completed_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when user opens a registration fxa page + static let registrationView = EventMetricType( // generated from sync.registration_view + CommonMetricData( + category: "sync", + name: "registration_view", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts the number of times a user taps + /// on sign in to sync button in sync + /// library view + static let signInSyncPressed = CounterMetricType( // generated from sync.sign_in_sync_pressed + CommonMetricData( + category: "sync", + name: "sign_in_sync_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user chose to use their email to attempt a sign in instead + /// of scanning a QR code, counterpart to "scan_pairing". + static let useEmail = EventMetricType( // generated from sync.use_email + CommonMetricData( + category: "sync", + name: "use_email", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + } + + enum Tabs { + struct GroupedTabExtra: EventExtras { + var averageTabsInAllGroups: Int32? + var groupsTwoTabsOnly: Int32? + var groupsWithMoreThanTwoTab: Int32? + var totalNumOfGroups: Int32? + var totalTabsInAllGroups: Int32? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let averageTabsInAllGroups = self.averageTabsInAllGroups { + record["average_tabs_in_all_groups"] = String(averageTabsInAllGroups) + } + if let groupsTwoTabsOnly = self.groupsTwoTabsOnly { + record["groups_two_tabs_only"] = String(groupsTwoTabsOnly) + } + if let groupsWithMoreThanTwoTab = self.groupsWithMoreThanTwoTab { + record["groups_with_more_than_two_tab"] = String(groupsWithMoreThanTwoTab) + } + if let totalNumOfGroups = self.totalNumOfGroups { + record["total_num_of_groups"] = String(totalNumOfGroups) + } + if let totalTabsInAllGroups = self.totalTabsInAllGroups { + record["total_tabs_in_all_groups"] = String(totalTabsInAllGroups) + } + + return record + } + } + + struct RestoreTabsAlertExtra: EventExtras { + var isEnabled: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isEnabled = self.isEnabled { + record["is_enabled"] = String(isEnabled) + } + + return record + } + } + + /// Recorded when a user clicks an open tab + static let clickTab = EventMetricType( // generated from tabs.click_tab + CommonMetricData( + category: "tabs", + name: "click_tab", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + private static let closeLabel = CounterMetricType( // generated from tabs.close + CommonMetricData( + category: "tabs", + name: "close", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The count of tabs closed by the user. Increments the + /// appropriate label when either a normal or private tab + /// is closed. + static let close = try! LabeledMetricType( // generated from tabs.close + category: "tabs", + name: "close", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: closeLabel, + labels: ["normal-tab", "private-tab"] + ) + + private static let closeAllLabel = CounterMetricType( // generated from tabs.close_all + CommonMetricData( + category: "tabs", + name: "close_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The count of times the user closes all tabs. Increments the + /// appropriate label when either a normal or private tab + /// is closed. + static let closeAll = try! LabeledMetricType( // generated from tabs.close_all + category: "tabs", + name: "close_all", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: closeAllLabel, + labels: ["normal-tab", "private-tab"] + ) + + /// Recorded when a user closes the tab tray + static let closeTabTray = EventMetricType( // generated from tabs.close_tab_tray + CommonMetricData( + category: "tabs", + name: "close_tab_tray", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Measures the current open tab count as the application + /// goes to background. Each background event adds to this + /// metric, making it the cumulative sum of all open tabs + /// when the app goes to background during the period of + /// time covered by a single metrics ping. This can be + /// divided by the number of baseline pings with + /// `ping_info.reason="inactive"` from the `start_time` to + /// the `end_time` of the metrics ping to determine the + /// average open tabs per foreground "session". + static let cumulativeCount = CounterMetricType( // generated from tabs.cumulative_count + CommonMetricData( + category: "tabs", + name: "cumulative_count", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when a user opens the tab tray with grouped tab + static let groupedTab = EventMetricType( // generated from tabs.grouped_tab + CommonMetricData( + category: "tabs", + name: "grouped_tab", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["average_tabs_in_all_groups", "groups_two_tabs_only", "groups_with_more_than_two_tab", "total_num_of_groups", "total_tabs_in_all_groups"] + ) + + /// Recorded when a user closes the tab in a group + static let groupedTabClosed = CounterMetricType( // generated from tabs.grouped_tab_closed + CommonMetricData( + category: "tabs", + name: "grouped_tab_closed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// This counts the number of times a user has tapped the search + /// icon for grouped tabs in the tab tray. + static let groupedTabSearch = CounterMetricType( // generated from tabs.grouped_tab_search + CommonMetricData( + category: "tabs", + name: "grouped_tab_search", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A quantity that indicates how many INACTIVE tabs a user + /// currently has open. This is reported when the user backgrounds + /// the app. + static let inactiveTabsCount = QuantityMetricType( // generated from tabs.inactive_tabs_count + CommonMetricData( + category: "tabs", + name: "inactive_tabs_count", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// This counts the number of times a user navigates back in tab + /// history by swiping from the left edge of the device to the right. + static let navigateTabBackSwipe = CounterMetricType( // generated from tabs.navigate_tab_back_swipe + CommonMetricData( + category: "tabs", + name: "navigate_tab_back_swipe", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// This counts the number of times a user taps the back + /// button on a tab's toolbar. + static let navigateTabHistoryBack = CounterMetricType( // generated from tabs.navigate_tab_history_back + CommonMetricData( + category: "tabs", + name: "navigate_tab_history_back", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// This counts the number of times a user taps the forward + /// button on a tab's toolbar. + static let navigateTabHistoryForward = CounterMetricType( // generated from tabs.navigate_tab_history_forward + CommonMetricData( + category: "tabs", + name: "navigate_tab_history_forward", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts the number of times the add new tab button is + /// pressed + static let newTabPressed = CounterMetricType( // generated from tabs.new_tab_pressed + CommonMetricData( + category: "tabs", + name: "new_tab_pressed", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record the number of URI's visited by the user. + static let normalAndPrivateUriCount = CounterMetricType( // generated from tabs.normal_and_private_uri_count + CommonMetricData( + category: "tabs", + name: "normal_and_private_uri_count", + sendInPings: ["baseline", "metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A snapshot of how many normal tabs a user has opened when he + /// closes the application. + static let normalTabsQuantity = QuantityMetricType( // generated from tabs.normal_tabs_quantity + CommonMetricData( + category: "tabs", + name: "normal_tabs_quantity", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + private static let openLabel = CounterMetricType( // generated from tabs.open + CommonMetricData( + category: "tabs", + name: "open", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// The count of tabs opened by the user. Increments the + /// appropriate label when either a normal or private tab + /// is opened. + static let open = try! LabeledMetricType( // generated from tabs.open + category: "tabs", + name: "open", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: openLabel, + labels: ["normal-tab", "private-tab"] + ) + + /// Recorded when a user opens the tab tray + static let openTabTray = EventMetricType( // generated from tabs.open_tab_tray + CommonMetricData( + category: "tabs", + name: "open_tab_tray", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when tab view button is pressed + static let pressTabToolbar = EventMetricType( // generated from tabs.press_tab_toolbar + CommonMetricData( + category: "tabs", + name: "press_tab_toolbar", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Records when a top tab is pressed + static let pressTopTab = EventMetricType( // generated from tabs.press_top_tab + CommonMetricData( + category: "tabs", + name: "press_top_tab", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A snapshot of how many private tabs a user has opened when he + /// closes the application. + static let privateTabsQuantity = QuantityMetricType( // generated from tabs.private_tabs_quantity + CommonMetricData( + category: "tabs", + name: "private_tabs_quantity", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record the number of times a user pulls down + /// on a page to reload. + static let pullToRefresh = CounterMetricType( // generated from tabs.pull_to_refresh + CommonMetricData( + category: "tabs", + name: "pull_to_refresh", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Record event when user has discovered the Easter egg. + static let pullToRefreshEasterEgg = EventMetricType( // generated from tabs.pull_to_refresh_easter_egg + CommonMetricData( + category: "tabs", + name: "pull_to_refresh_easter_egg", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// This counts the number of times a user taps the reload + /// button in the URL bar. + static let reloadFromUrlBar = CounterMetricType( // generated from tabs.reload_from_url_bar + CommonMetricData( + category: "tabs", + name: "reload_from_url_bar", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when a users choose an option on the restore tabs alert + static let restoreTabsAlert = EventMetricType( // generated from tabs.restore_tabs_alert + CommonMetricData( + category: "tabs", + name: "restore_tabs_alert", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_enabled"] + ) + + /// Counts how long it takes to switch to another tab + static let tabSwitch = TimingDistributionMetricType( // generated from tabs.tab_switch + CommonMetricData( + category: "tabs", + name: "tab_switch", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + , .millisecond + ) + + } + + enum TabsTray { + struct PrivateBrowsingIconTappedExtra: EventExtras { + var action: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let action = self.action { + record["action"] = String(action) + } + + return record + } + } + + /// A user opened a new private tab from the tab tray + static let newPrivateTabTapped = EventMetricType( // generated from tabs_tray.new_private_tab_tapped + CommonMetricData( + category: "tabs_tray", + name: "new_private_tab_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// A user has tapped on the private browsing icon in tabs tray. + static let privateBrowsingIconTapped = EventMetricType( // generated from tabs_tray.private_browsing_icon_tapped + CommonMetricData( + category: "tabs_tray", + name: "private_browsing_icon_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["action"] + ) + + } + + enum Toolbar { + struct AppMenuButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct BackButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct BackLongPressExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct ClearSearchButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct DataClearanceButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct ForwardButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct ForwardLongPressExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct HomeButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct OneTapNewTabButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct OneTapNewTabLongPressExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct QrScanButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct ReaderModeButtonTappedExtra: EventExtras { + var enabled: Bool? + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let enabled = self.enabled { + record["enabled"] = String(enabled) + } + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct RefreshButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct SearchButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct ShareButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct SiteInfoButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct TabTrayButtonTappedExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + struct TabTrayLongPressExtra: EventExtras { + var isPrivate: Bool? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let isPrivate = self.isPrivate { + record["is_private"] = String(isPrivate) + } + + return record + } + } + + /// Counts the number of times a user taps the menu button + /// in the address or navigation toolbar + static let appMenuButtonTapped = EventMetricType( // generated from toolbar.app_menu_button_tapped + CommonMetricData( + category: "toolbar", + name: "app_menu_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the back button + /// in the address or navigation toolbar + static let backButtonTapped = EventMetricType( // generated from toolbar.back_button_tapped + CommonMetricData( + category: "toolbar", + name: "back_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user long presses the back button + /// in the address or navigation toolbar + static let backLongPress = EventMetricType( // generated from toolbar.back_long_press + CommonMetricData( + category: "toolbar", + name: "back_long_press", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the clear button + /// in the address toolbar + static let clearSearchButtonTapped = EventMetricType( // generated from toolbar.clear_search_button_tapped + CommonMetricData( + category: "toolbar", + name: "clear_search_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the data clearance + /// button in the address or navigation toolbar + static let dataClearanceButtonTapped = EventMetricType( // generated from toolbar.data_clearance_button_tapped + CommonMetricData( + category: "toolbar", + name: "data_clearance_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the forward button + /// in the address or navigation toolbar + static let forwardButtonTapped = EventMetricType( // generated from toolbar.forward_button_tapped + CommonMetricData( + category: "toolbar", + name: "forward_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user long presses the forward button + /// in the address or navigation toolbar + static let forwardLongPress = EventMetricType( // generated from toolbar.forward_long_press + CommonMetricData( + category: "toolbar", + name: "forward_long_press", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the home button + /// in the address or navigation toolbar + static let homeButtonTapped = EventMetricType( // generated from toolbar.home_button_tapped + CommonMetricData( + category: "toolbar", + name: "home_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the one tap new tab + /// button in the address or navigation toolbar + static let oneTapNewTabButtonTapped = EventMetricType( // generated from toolbar.one_tap_new_tab_button_tapped + CommonMetricData( + category: "toolbar", + name: "one_tap_new_tab_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user long presses the one + /// tap new tab button in the address or navigation toolbar + static let oneTapNewTabLongPress = EventMetricType( // generated from toolbar.one_tap_new_tab_long_press + CommonMetricData( + category: "toolbar", + name: "one_tap_new_tab_long_press", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the qr code scan button + /// in the address toolbar + static let qrScanButtonTapped = EventMetricType( // generated from toolbar.qr_scan_button_tapped + CommonMetricData( + category: "toolbar", + name: "qr_scan_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the reader mode button + /// in the address toolbar + static let readerModeButtonTapped = EventMetricType( // generated from toolbar.reader_mode_button_tapped + CommonMetricData( + category: "toolbar", + name: "reader_mode_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["enabled", "is_private"] + ) + + /// Counts the number of times a user taps the refresh button + /// in the address toolbar + static let refreshButtonTapped = EventMetricType( // generated from toolbar.refresh_button_tapped + CommonMetricData( + category: "toolbar", + name: "refresh_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the search button + /// in the address or navigation toolbar + static let searchButtonTapped = EventMetricType( // generated from toolbar.search_button_tapped + CommonMetricData( + category: "toolbar", + name: "search_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the share button + /// in the address toolbar + static let shareButtonTapped = EventMetricType( // generated from toolbar.share_button_tapped + CommonMetricData( + category: "toolbar", + name: "share_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the site info button + /// in the address toolbar + static let siteInfoButtonTapped = EventMetricType( // generated from toolbar.site_info_button_tapped + CommonMetricData( + category: "toolbar", + name: "site_info_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user taps the tab tray button + /// in the address or navigation toolbar + static let tabTrayButtonTapped = EventMetricType( // generated from toolbar.tab_tray_button_tapped + CommonMetricData( + category: "toolbar", + name: "tab_tray_button_tapped", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + /// Counts the number of times a user long presses the tab tray + /// button in the address or navigation toolbar + static let tabTrayLongPress = EventMetricType( // generated from toolbar.tab_tray_long_press + CommonMetricData( + category: "toolbar", + name: "tab_tray_long_press", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["is_private"] + ) + + } + + enum TopSites { + struct ContextualMenuExtra: EventExtras { + var type: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let type = self.type { + record["type"] = String(type) + } + + return record + } + } + + struct ContileClickExtra: EventExtras { + var position: Int32? + var source: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let position = self.position { + record["position"] = String(position) + } + if let source = self.source { + record["source"] = String(source) + } + + return record + } + } + + struct ContileImpressionExtra: EventExtras { + var position: Int32? + var source: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let position = self.position { + record["position"] = String(position) + } + if let source = self.source { + record["source"] = String(source) + } + + return record + } + } + + struct TilePressedExtra: EventExtras { + var position: String? + var tileType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let position = self.position { + record["position"] = String(position) + } + if let tileType = self.tileType { + record["tile_type"] = String(tileType) + } + + return record + } + } + + /// A UUID that is unjoinable with other browser metrics. This ID will not be + /// shared with AdM, only for internal uses. This ID is shared across all + /// contextual services features. + static let contextId = UuidMetricType( // generated from top_sites.context_id + CommonMetricData( + category: "top_sites", + name: "context_id", + sendInPings: ["topsites-impression"], + lifetime: .application, + disabled: false + ) + ) + + /// Records when a user clicks on a contextual + /// menu option on top site + static let contextualMenu = EventMetricType( // generated from top_sites.contextual_menu + CommonMetricData( + category: "top_sites", + name: "contextual_menu", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["type"] + ) + + /// Advertiser brand for the sponsored TopSites tile + static let contileAdvertiser = StringMetricType( // generated from top_sites.contile_advertiser + CommonMetricData( + category: "top_sites", + name: "contile_advertiser", + sendInPings: ["topsites-impression"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user clicked a Contile top site + static let contileClick = EventMetricType( // generated from top_sites.contile_click + CommonMetricData( + category: "top_sites", + name: "contile_click", + sendInPings: ["events", "topsites-impression"], + lifetime: .ping, + disabled: false + ) + , ["position", "source"] + ) + + /// A user saw a Contile top site + static let contileImpression = EventMetricType( // generated from top_sites.contile_impression + CommonMetricData( + category: "top_sites", + name: "contile_impression", + sendInPings: ["events", "topsites-impression"], + lifetime: .ping, + disabled: false + ) + , ["position", "source"] + ) + + /// The AdM reporting endpoint (impression_url for “impression” event, + /// click_url for “click” event). + static let contileReportingUrl = UrlMetricType( // generated from top_sites.contile_reporting_url + CommonMetricData( + category: "top_sites", + name: "contile_reporting_url", + sendInPings: ["topsites-impression"], + lifetime: .ping, + disabled: false + ) + ) + + /// A unique identifier provided by the AdM for the sponsored TopSites tile + static let contileTileId = QuantityMetricType( // generated from top_sites.contile_tile_id + CommonMetricData( + category: "top_sites", + name: "contile_tile_id", + sendInPings: ["topsites-impression"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user opens a new private tab based on a top site item + static let openInPrivateTab = EventMetricType( // generated from top_sites.open_in_private_tab + CommonMetricData( + category: "top_sites", + name: "open_in_private_tab", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + private static let pressedTileOriginLabel = CounterMetricType( // generated from top_sites.pressed_tile_origin + CommonMetricData( + category: "top_sites", + name: "pressed_tile_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records an event when user taps on top site tile, + /// with a home page origin. + static let pressedTileOrigin = try! LabeledMetricType( // generated from top_sites.pressed_tile_origin + category: "top_sites", + name: "pressed_tile_origin", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: pressedTileOriginLabel, + labels: ["origin-other", "zero-search"] + ) + + /// Tracks if the user has enabled sponsored shortcuts + static let sponsoredShortcuts = BooleanMetricType( // generated from top_sites.sponsored_shortcuts + CommonMetricData( + category: "top_sites", + name: "sponsored_shortcuts", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Records an event when user taps on top site tile. + static let tilePressed = EventMetricType( // generated from top_sites.tile_pressed + CommonMetricData( + category: "top_sites", + name: "tile_pressed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["position", "tile_type"] + ) + + } + + enum TrackingProtection { + struct EtpSettingChangedExtra: EventExtras { + var etpEnabled: Bool? + var etpSetting: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let etpEnabled = self.etpEnabled { + record["etp_enabled"] = String(etpEnabled) + } + if let etpSetting = self.etpSetting { + record["etp_setting"] = String(etpSetting) + } + + return record + } + } + + /// Measures the state of the tracking-protection enabled + /// preference. + static let enabled = BooleanMetricType( // generated from tracking_protection.enabled + CommonMetricData( + category: "tracking_protection", + name: "enabled", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// A user changed their tracking protection + /// level setting to either strict or standard + static let etpSettingChanged = EventMetricType( // generated from tracking_protection.etp_setting_changed + CommonMetricData( + category: "tracking_protection", + name: "etp_setting_changed", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["etp_enabled", "etp_setting"] + ) + + /// A string representing the selected strength of the + /// tracking-protection that is enabled. One of: + /// * basic + /// * strict + static let strength = StringMetricType( // generated from tracking_protection.strength + CommonMetricData( + category: "tracking_protection", + name: "strength", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Urlbar { + struct AbandonmentExtra: EventExtras { + var groups: String? + var interaction: String? + var nChars: Int32? + var nResults: Int32? + var nWords: Int32? + var results: String? + var sap: String? + var searchMode: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let groups = self.groups { + record["groups"] = String(groups) + } + if let interaction = self.interaction { + record["interaction"] = String(interaction) + } + if let nChars = self.nChars { + record["n_chars"] = String(nChars) + } + if let nResults = self.nResults { + record["n_results"] = String(nResults) + } + if let nWords = self.nWords { + record["n_words"] = String(nWords) + } + if let results = self.results { + record["results"] = String(results) + } + if let sap = self.sap { + record["sap"] = String(sap) + } + if let searchMode = self.searchMode { + record["search_mode"] = String(searchMode) + } + + return record + } + } + + struct EngagementExtra: EventExtras { + var engagementType: String? + var groups: String? + var interaction: String? + var nChars: Int32? + var nResults: Int32? + var nWords: Int32? + var provider: String? + var results: String? + var sap: String? + var searchMode: String? + var selectedResult: String? + var selectedResultSubtype: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let engagementType = self.engagementType { + record["engagement_type"] = String(engagementType) + } + if let groups = self.groups { + record["groups"] = String(groups) + } + if let interaction = self.interaction { + record["interaction"] = String(interaction) + } + if let nChars = self.nChars { + record["n_chars"] = String(nChars) + } + if let nResults = self.nResults { + record["n_results"] = String(nResults) + } + if let nWords = self.nWords { + record["n_words"] = String(nWords) + } + if let provider = self.provider { + record["provider"] = String(provider) + } + if let results = self.results { + record["results"] = String(results) + } + if let sap = self.sap { + record["sap"] = String(sap) + } + if let searchMode = self.searchMode { + record["search_mode"] = String(searchMode) + } + if let selectedResult = self.selectedResult { + record["selected_result"] = String(selectedResult) + } + if let selectedResultSubtype = self.selectedResultSubtype { + record["selected_result_subtype"] = String(selectedResultSubtype) + } + + return record + } + } + + struct ImpressionExtra: EventExtras { + var groups: String? + var interaction: String? + var nChars: Int32? + var nResults: Int32? + var nWords: Int32? + var reason: String? + var results: String? + var sap: String? + var searchMode: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let groups = self.groups { + record["groups"] = String(groups) + } + if let interaction = self.interaction { + record["interaction"] = String(interaction) + } + if let nChars = self.nChars { + record["n_chars"] = String(nChars) + } + if let nResults = self.nResults { + record["n_results"] = String(nResults) + } + if let nWords = self.nWords { + record["n_words"] = String(nWords) + } + if let reason = self.reason { + record["reason"] = String(reason) + } + if let results = self.results { + record["results"] = String(results) + } + if let sap = self.sap { + record["sap"] = String(sap) + } + if let searchMode = self.searchMode { + record["search_mode"] = String(searchMode) + } + + return record + } + } + + /// Recorded when urlbar results are shown to the user + static let abandonment = EventMetricType( // generated from urlbar.abandonment + CommonMetricData( + category: "urlbar", + name: "abandonment", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["groups", "interaction", "n_chars", "n_results", "n_words", "results", "sap", "search_mode"] + ) + + /// Recorded when the user executes an action on a result + static let engagement = EventMetricType( // generated from urlbar.engagement + CommonMetricData( + category: "urlbar", + name: "engagement", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["engagement_type", "groups", "interaction", "n_chars", "n_results", "n_words", "provider", "results", "sap", "search_mode", "selected_result", "selected_result_subtype"] + ) + + /// Recorded when urlbar results are shown to the user + static let impression = EventMetricType( // generated from urlbar.impression + CommonMetricData( + category: "urlbar", + name: "impression", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["groups", "interaction", "n_chars", "n_results", "n_words", "reason", "results", "sap", "search_mode"] + ) + + } + + enum WallpaperAnalytics { + struct WallpaperSelectedExtra: EventExtras { + var wallpaperName: String? + var wallpaperType: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let wallpaperName = self.wallpaperName { + record["wallpaper_name"] = String(wallpaperName) + } + if let wallpaperType = self.wallpaperType { + record["wallpaper_type"] = String(wallpaperType) + } + + return record + } + } + + private static let themedWallpaperLabel = CounterMetricType( // generated from wallpaper_analytics.themed_wallpaper + CommonMetricData( + category: "wallpaper_analytics", + name: "themed_wallpaper", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Recorded when the user enters the background. This reports + /// the currently selected wallpaper if it's not the default. + static let themedWallpaper = try! LabeledMetricType( // generated from wallpaper_analytics.themed_wallpaper + category: "wallpaper_analytics", + name: "themed_wallpaper", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false, + subMetric: themedWallpaperLabel, + labels: nil + ) + + /// Recorded when the user selects a wallpaper from + /// the wallpaper settings screen. Wallpaper information + /// is sent in the extra keys. + static let wallpaperSelected = EventMetricType( // generated from wallpaper_analytics.wallpaper_selected + CommonMetricData( + category: "wallpaper_analytics", + name: "wallpaper_selected", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["wallpaper_name", "wallpaper_type"] + ) + + } + + enum Webview { + struct ShowErrorPageExtra: EventExtras { + var errorCode: String? + + func toExtraRecord() -> [String: String] { + var record = [String: String]() + + if let errorCode = self.errorCode { + record["error_code"] = String(errorCode) + } + + return record + } + } + + /// Recorded when an error occurred during navigation. + static let didFail = EventMetricType( // generated from webview.did_fail + CommonMetricData( + category: "webview", + name: "did_fail", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Recorded when an error occurs on early webview navigation. + static let didFailProvisional = EventMetricType( // generated from webview.did_fail_provisional + CommonMetricData( + category: "webview", + name: "did_fail_provisional", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , [] + ) + + /// Counts how long each page takes to load + static let pageLoad = TimingDistributionMetricType( // generated from webview.page_load + CommonMetricData( + category: "webview", + name: "page_load", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + , .millisecond + ) + + /// Recorded when an error page is shown on the webview. + static let showErrorPage = EventMetricType( // generated from webview.show_error_page + CommonMetricData( + category: "webview", + name: "show_error_page", + sendInPings: ["events"], + lifetime: .ping, + disabled: false + ) + , ["error_code"] + ) + + } + + enum Widget { + /// Counts how many times the large tabs widget opens url + static let lTabsOpenUrl = CounterMetricType( // generated from widget.l_tabs_open_url + CommonMetricData( + category: "widget", + name: "l_tabs_open_url", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times medium quick action + /// widget closes private tabs + static let mQuickActionClosePrivate = CounterMetricType( // generated from widget.m_quick_action_close_private + CommonMetricData( + category: "widget", + name: "m_quick_action_close_private", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times medium quick action + /// widget opens copied links + static let mQuickActionCopiedLink = CounterMetricType( // generated from widget.m_quick_action_copied_link + CommonMetricData( + category: "widget", + name: "m_quick_action_copied_link", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times medium quick action + /// widget opens firefox for private search + static let mQuickActionPrivateSearch = CounterMetricType( // generated from widget.m_quick_action_private_search + CommonMetricData( + category: "widget", + name: "m_quick_action_private_search", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times medium quick action + /// widget opens firefox for regular search + static let mQuickActionSearch = CounterMetricType( // generated from widget.m_quick_action_search + CommonMetricData( + category: "widget", + name: "m_quick_action_search", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times the medium tabs widget opens url + static let mTabsOpenUrl = CounterMetricType( // generated from widget.m_tabs_open_url + CommonMetricData( + category: "widget", + name: "m_tabs_open_url", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times user opens top site tabs + static let mTopSitesWidget = CounterMetricType( // generated from widget.m_top_sites_widget + CommonMetricData( + category: "widget", + name: "m_top_sites_widget", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + /// Counts how many times small quick action + /// widget opens firefox for regular search + static let sQuickActionSearch = CounterMetricType( // generated from widget.s_quick_action_search + CommonMetricData( + category: "widget", + name: "s_quick_action_search", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + enum Windows { + /// A snapshot of how many windows the user has opened on iPad. + static let ipadWindowCount = QuantityMetricType( // generated from windows.ipad_window_count + CommonMetricData( + category: "windows", + name: "ipad_window_count", + sendInPings: ["metrics"], + lifetime: .ping, + disabled: false + ) + ) + + } + + class Pings { + public static let shared = Pings() + private init() { + // Intentionally left private, no external user can instantiate a new global object. + } + + enum DauReportingReasonCodes: Int, ReasonCodes { + case active = 0 + case dirtyStartup = 1 + case inactive = 2 + + public func index() -> Int { + return self.rawValue + } + } + + /// Minimal ping to measure DAU. + /// Sent on the baseline schedule. + let dauReporting = Ping( + name: "dau-reporting", + includeClientId: true, + sendIfEmpty: true, + preciseTimestamps: true, + includeInfoSections: true, + enabled: true, + schedulesPings: [], + reasonCodes: ["active", "dirty_startup", "inactive"] + ) + + /// Recorded on first_session when the user installs the app. + let firstSession = Ping( + name: "first-session", + includeClientId: true, + sendIfEmpty: false, + preciseTimestamps: true, + includeInfoSections: true, + enabled: true, + schedulesPings: [], + reasonCodes: [] + ) + + /// A ping representing a single event occurring with or to a Firefox Suggestion. + /// Distinguishable by its `ping_type`. + /// Does not contain a `client_id`, preferring a `context_id` instead. + let fxSuggest = Ping( + name: "fx-suggest", + includeClientId: false, + sendIfEmpty: false, + preciseTimestamps: true, + includeInfoSections: true, + enabled: true, + schedulesPings: [], + reasonCodes: [] + ) + + /// Recorded when a sponsored top site is rendered and + /// visible on the newtab page. Visibility is qualified + /// as when the homepage is brought to the front of the + /// Browser, and sponsored tiles are 100% visible on screen. + let topsitesImpression = Ping( + name: "topsites-impression", + includeClientId: false, + sendIfEmpty: false, + preciseTimestamps: true, + includeInfoSections: true, + enabled: true, + schedulesPings: [], + reasonCodes: [] + ) + + } + +} diff --git a/firefox-ios/Client/Ecosia/RecoveredFromUpgrade/LegacySessionData.swift b/firefox-ios/Client/Ecosia/RecoveredFromUpgrade/LegacySessionData.swift new file mode 100644 index 0000000000000..cd76796a0259a --- /dev/null +++ b/firefox-ios/Client/Ecosia/RecoveredFromUpgrade/LegacySessionData.swift @@ -0,0 +1,94 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import Foundation +import Shared + +/// PR: https://github.com/mozilla-mobile/firefox-ios/pull/4387 +/// Commit: https://github.com/mozilla-mobile/firefox-ios/commit/8b1450fbeb87f1f559a2f8e42971c715dc96bcaf +/// InternalURL helps encapsulate all internal scheme logic for urls rather than using URL extension. Extensions to built-in classes should be more minimal that what was being done previously. +/// This migration was required mainly for above PR which is related to a PI request that +/// reduces security risk. Also, this particular method helps in cleaning up / migrating +/// old localhost:6571 URLs to internal: SessionData urls +private func migrate(urls: [URL]) -> [URL] { + return urls.compactMap { url in + var url = url + let port = AppInfo.webserverPort + [("http://localhost:\(port)/errors/error.html?url=", "\(InternalURL.baseUrl)/\(SessionRestoreHandler.path)?url=") + // TODO: handle reader pages ("http://localhost:6571/reader-mode/page?url=", "\(InternalScheme.url)/\(ReaderModeHandler.path)?url=") + ].forEach { oldItem, newItem in + if url.absoluteString.hasPrefix(oldItem) { + var urlStr = url.absoluteString.replacingOccurrences(of: oldItem, with: newItem) + let comp = urlStr.components(separatedBy: newItem) + if comp.count > 2 { + // get the last instance of incorrectly nested urls + urlStr = newItem + (comp.last ?? "") + assertionFailure("SessionData urls have nested internal links, investigate: [\(url.absoluteString)]") + } + url = URL(string: urlStr, invalidCharacters: false) ?? url + } + } + + if let internalUrl = InternalURL(url), internalUrl.isAuthorized, + let stripped = URL(string: internalUrl.stripAuthorization, invalidCharacters: false) { + return stripped + } + + return url + } +} +// Ecosia: Tabs architecture implementation from ~v112 to ~116 +// class LegacySessionData: Codable { +class LegacySessionData: NSObject, Codable, NSCoding { + + let currentPage: Int + let lastUsedTime: Timestamp + let urls: [URL] + + enum CodingKeys: String, CodingKey { + case currentPage + case lastUsedTime + case urls + } + + /** + Creates a new SessionData object representing a serialized tab. + + - parameter currentPage: The active page index. Must be in the range of (-N, 0], + where 1-N is the first page in history, and 0 is the last. + - parameter urls: The sequence of URLs in this tab's session history. + - parameter lastUsedTime: The last time this tab was modified. + **/ + init(currentPage: Int, urls: [URL], lastUsedTime: Timestamp) { + self.currentPage = currentPage + self.urls = migrate(urls: urls) + self.lastUsedTime = lastUsedTime + } + + // Ecosia: Tabs architecture implementation from ~v112 to ~116 + // This is temprorary in order to fix a migration error, can be removed after our Ecosia 10.0.0 has been well adopted + + var jsonDictionary: [String: Any] { + return [ + CodingKeys.currentPage.rawValue: String(self.currentPage), + CodingKeys.lastUsedTime.rawValue: String(self.lastUsedTime), + CodingKeys.urls.rawValue: urls.map { $0.absoluteString } + ] + } + + required init?(coder: NSCoder) { + self.currentPage = coder.decodeInteger(forKey: CodingKeys.currentPage.rawValue) + self.urls = migrate(urls: coder.decodeObject(forKey: CodingKeys.urls.rawValue) as? [URL] ?? [URL]()) + self.lastUsedTime = UInt64(coder.decodeInt64(forKey: CodingKeys.lastUsedTime.rawValue)) + } + + func encode(with coder: NSCoder) { + coder.encode(currentPage, forKey: CodingKeys.currentPage.rawValue) + coder.encode(migrate(urls: urls), forKey: CodingKeys.urls.rawValue) + coder.encode(Int64(lastUsedTime), forKey: CodingKeys.lastUsedTime.rawValue) + } + + // Ecosia: End Tabs architecture implementation from ~v112 to ~116 +} diff --git a/firefox-ios/Client/Ecosia/RecoveredFromUpgrade/SessionRestoreHandler.swift b/firefox-ios/Client/Ecosia/RecoveredFromUpgrade/SessionRestoreHandler.swift new file mode 100644 index 0000000000000..a82b4487b2a04 --- /dev/null +++ b/firefox-ios/Client/Ecosia/RecoveredFromUpgrade/SessionRestoreHandler.swift @@ -0,0 +1,66 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import WebKit +import GCDWebServers +import Shared + +private let apostropheEncoded = "%27" + +extension WKWebView { + // Use JS to redirect the page without adding a history entry + func replaceLocation(with url: URL) { + let safeUrl = url.absoluteString.replacingOccurrences(of: "'", with: apostropheEncoded) + evaluateJavascriptInDefaultContentWorld("location.replace('\(safeUrl)')") + } +} + +func generateResponseThatRedirects(toUrl url: URL) -> (URLResponse, Data) { + var urlString: String + if InternalURL.isValid(url: url), let authUrl = InternalURL.authorize(url: url) { + urlString = authUrl.absoluteString + } else { + urlString = url.absoluteString + } + + urlString = urlString.replacingOccurrences(of: "'", with: apostropheEncoded) + + let startTags = "" + let html = startTags + "location.replace('\(urlString)');" + endTags + + let data = html.data(using: .utf8)! + let response = InternalSchemeHandler.response(forUrl: url) + return (response, data) +} + +/// Handles requests to /about/sessionrestore to restore session history. +class SessionRestoreHandler: InternalSchemeResponse { + static let path = "sessionrestore" + + func response(forRequest request: URLRequest) -> (URLResponse, Data)? { + guard let _url = request.url, let url = InternalURL(_url) else { return nil } + + // Handle the 'url='query param + if let urlParam = url.extractedUrlParam { + return generateResponseThatRedirects(toUrl: urlParam) + } + + // From here on, handle 'history=' query param + let response = InternalSchemeHandler.response(forUrl: url.url) + guard let sessionRestorePath = Bundle.main.path(forResource: "SessionRestore", + ofType: "html"), + let html = try? String(contentsOfFile: sessionRestorePath).replacingOccurrences( + of: "%INSERT_UUID_VALUE%", + with: InternalURL.uuid), + let data = html.data(using: .utf8) + else { + assertionFailure() + return nil + } + + return (response, data) + } +} diff --git a/firefox-ios/Client/Ecosia/Settings/EcosiaDebugSettings.swift b/firefox-ios/Client/Ecosia/Settings/EcosiaDebugSettings.swift new file mode 100644 index 0000000000000..11faef4a8ca6c --- /dev/null +++ b/firefox-ios/Client/Ecosia/Settings/EcosiaDebugSettings.swift @@ -0,0 +1,696 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UIKit +import Shared +import Common +import Ecosia + +final class PushBackInstallation: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Push back installation by 3 days (needs restart).", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + Calendar.current.date(byAdding: .day, value: -3, to: User.shared.install).map { + User.shared.install = $0 + } + } +} + +final class ToggleImpactIntro: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Toggle - Show Impact intro", attributes: [:]) + } + + override var status: NSAttributedString? { + let isOn = User.shared.shouldShowImpactIntro + return NSAttributedString(string: isOn ? "True" : "False", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + if User.shared.shouldShowImpactIntro { + User.shared.hideImpactIntro() + } else { + User.shared.showImpactIntro() + } + settings.tableView.reloadData() + } +} + +final class ToggleDefaultBrowserPromo: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Toggle - Show Default Browser Promo", attributes: [:]) + } + + override var status: NSAttributedString? { + let introSeen = profile.prefs.intForKey(PrefsKeys.IntroSeen) != nil + return NSAttributedString(string: introSeen ? "False (click to reset)" : "True", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + profile.prefs.removeObjectForKey(PrefsKeys.IntroSeen) + settings.tableView.reloadData() + } + + let profile: Profile + override init(settings: SettingsTableViewController) { + self.profile = settings.profile + super.init(settings: settings) + } +} + +final class ResetOnboardingProductTour: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Reset product tour state", attributes: [:]) + } + + override var status: NSAttributedString? { + guard OnboardingProductTourExperiment.isEnabled else { + if OnboardingProductTourExperiment.isControl { + return NSAttributedString(string: "Current state: Control variant — use Unleash Onboarding Product Tour debug setting to reset") + } + return NSAttributedString(string: "Current state: Experiment disabled") + } + + let milestones = ProductTourManager.shared.completedMilestones + let state: String + if milestones.contains(.all) { + state = "All" + } else { + var completed: [String] = [] + if milestones.contains(.firstSearchDone) { completed.append("firstSearchDone") } + if milestones.contains(.searchSpotlightDone) { completed.append("searchSpotlightDone") } + if milestones.contains(.externalWebsiteVisitDone) { completed.append("externalWebsiteVisitDone") } + if milestones.contains(.externalWebsiteSpotlightDone) { completed.append("externalWebsiteSpotlightDone") } + state = completed.isEmpty ? "None" : completed.joined(separator: ", ") + } + let inTour = ProductTourManager.shared.isInProductTour ? "In tour" : "Tour complete" + return NSAttributedString(string: "Current state: \(inTour) | Milestones: \(state)") + } + + override init(settings: SettingsTableViewController) { + super.init(settings: settings) + } + + override func onClick(_ navigationController: UINavigationController?) { + guard OnboardingProductTourExperiment.isEnabled else { + let alert = AlertController(title: "Experiment Disabled or in Control", + message: "The onboarding product tour experiment is not enabled. Check your constraints (such as version on install) or use the \"Unleash Onboarding Product Tour\" debug setting to reset the Unleash cache and get a new variant assignment.", + preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(alert, animated: true) + return + } + + User.shared.firstTime = true + ProductTourManager.shared.resetTour() + Task { + try? await EcosiaAuthenticationService.shared.logout() + } + + let title = "Onboarding state reset" + let message = "Close and open the app to see welcome screen" + let alert = AlertController(title: title, message: message, preferredStyle: .alert) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + self.settings.tableView.reloadData() + } + } +} + +final class CreateReferralCode: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Referral Code \(User.shared.referrals.code ?? "-")", attributes: [:]) + } + + override var status: NSAttributedString? { + return .init(string: "Toggle to create or erase code") + } + + override func onClick(_ navigationController: UINavigationController?) { + + if User.shared.referrals.code == nil { + User.shared.referrals.code = "TEST123" + + let alertTitle = "Code created" + let alert = AlertController(title: alertTitle, message: User.shared.referrals.code, preferredStyle: .alert) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + self.settings.tableView.reloadData() + } + } else { + User.shared.referrals.code = nil + + let alert = AlertController(title: "Code erased!", message: "Reopen app to create new one", preferredStyle: .alert) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + self.settings.tableView.reloadData() + } + } + } +} + +final class AddReferral: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Add Referral", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + User.shared.referrals.claims += 1 + + let alertTitle = "Referral count increased by one." + let alert = AlertController(title: alertTitle, message: "Open NTP to see spotlight", preferredStyle: .alert) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + } + } +} + +final class AddClaim: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Add Referral Claim", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + User.shared.referrals.isClaimed = true + User.shared.referrals.isNewClaim = true + + let alertTitle = "User got referred." + let alert = AlertController(title: alertTitle, message: "Open NTP to see claim", preferredStyle: .alert) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + } + } +} + +final class ResetSearchCount: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Set search count to 0", attributes: [:]) + } + + override var status: NSAttributedString? { + return NSAttributedString(string: "\(User.shared.searchCount)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + User.shared.searchCount = 0 + self.settings.tableView.reloadData() + NotificationCenter.default.post(name: .HomePanelPrefsChanged, object: nil) + } +} + +final class ChangeSearchCount: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Increase search count by 10", attributes: [:]) + } + + override var status: NSAttributedString? { + return NSAttributedString(string: "\(User.shared.searchCount)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + User.shared.searchCount += 10 + self.settings.tableView.reloadData() + NotificationCenter.default.post(name: .HomePanelPrefsChanged, object: nil) + } +} + +final class ResetDefaultBrowserNudgeCard: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Makes the Default Browser nudge card visible again", attributes: [:]) + } + + override var status: NSAttributedString? { + let status = "\(User.shared.shouldShowDefaultBrowserSettingNudgeCard)" + let suggestion = User.shared.shouldShowDefaultBrowserSettingNudgeCard ? "" : " (Click to show)" + return NSAttributedString(string: "Card visible: \(status)\(suggestion)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + guard !User.shared.shouldShowDefaultBrowserSettingNudgeCard else { return } + User.shared.showDefaultBrowserSettingNudgeCard() + self.settings.settings = self.settings.generateSettings() + self.settings.tableView.reloadData() + } +} + +final class ResetAccountImpactNudgeCard: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Makes the Account Impact nudge card visible again", attributes: [:]) + } + + override var status: NSAttributedString? { + let status = "\(User.shared.shouldShowAccountImpactNudgeCard)" + let suggestion = User.shared.shouldShowAccountImpactNudgeCard ? "" : " (Click to show)" + return NSAttributedString(string: "Card visible: \(status)\(suggestion)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + guard !User.shared.shouldShowAccountImpactNudgeCard else { return } + User.shared.showAccountImpactNudgeCard() + self.settings.settings = self.settings.generateSettings() + self.settings.tableView.reloadData() + } +} + +class UnleashVariantResetSetting: HiddenSetting { + var titleName: String? { return nil } + var variant: Unleash.Variant? { return nil } + var unleashEnabled: Bool? { return nil } + + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Unleash \(titleName ?? "Unknown") variant", attributes: [:]) + } + + override var status: NSAttributedString? { + var statusName = variant?.name ?? "Unknown" + if statusName == "Unknown", let unleashEnabled = unleashEnabled { + statusName = unleashEnabled ? "enabled" : "disabled" + } + return NSAttributedString(string: "\(statusName) (Click to reset)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + Task { + do { + _ = try await Unleash.reset(env: .current, appVersion: AppInfo.ecosiaAppVersion) + } catch { + debugPrint(error) + } + await MainActor.run { + self.settings.tableView.reloadData() + let alert = AlertController(title: "Unleash reset ✅", + message: "The local Unleash cache has been wiped out", + preferredStyle: .alert) + alert.addAction(.init(title: "Ok", style: .default)) + navigationController?.topViewController?.present(alert, animated: true) + } + } + } +} + +final class UnleashBrazeIntegrationSetting: UnleashVariantResetSetting { + override var titleName: String? { + "Braze Integration" + } + + override var unleashEnabled: Bool? { + Unleash.isEnabled(.brazeIntegration) + } +} + +final class UnleashAISearchMVPSetting: UnleashVariantResetSetting { + override var titleName: String? { + "AI Search MVP" + } + + override var variant: Unleash.Variant? { + Unleash.getVariant(.aiSearchMVP) + } + + override var unleashEnabled: Bool? { + Unleash.isEnabled(.aiSearchMVP) + } +} + +final class UnleashOnboardingSetting: UnleashVariantResetSetting { + override var titleName: String? { + "Onboarding Product Tour" + } + + override var variant: Unleash.Variant? { + Unleash.getVariant(.onboardingProductTour) + } + + override var unleashEnabled: Bool? { + Unleash.isEnabled(.onboardingProductTour) + } +} + +final class AnalyticsIdentifierSetting: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Analytics Identifier", attributes: [:]) + } + + var analyticsIdentifier: String { User.shared.analyticsId.uuidString } + + override var status: NSAttributedString? { + return NSAttributedString(string: "\(analyticsIdentifier) (Click to copy)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + UIPasteboard.general.string = analyticsIdentifier + } +} + +final class UnleashIdentifierSetting: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Unleash Identifier", attributes: [:]) + } + + var analyticsIdentifier: String { Unleash.userId.uuidString } + + override var status: NSAttributedString? { + return NSAttributedString(string: "\(Unleash.userId.uuidString) (Click to copy)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + UIPasteboard.general.string = Unleash.userId.uuidString + } +} + +final class AnalyticsStagingUrlSetting: HiddenSetting { + + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Toggle - Swap Analytics Staging URL", attributes: [:]) + } + + override var status: NSAttributedString? { + let isOn = Analytics.shouldUseMicroInstance + let snowplowInstance = isOn ? "Micro" : "Mini" + return NSAttributedString(string: "\(snowplowInstance) instance (Click to toggle)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + Analytics.shouldUseMicroInstance.toggle() + settings.tableView.reloadData() + } +} + +final class SimulateAuthErrorSetting: HiddenSetting { + /// UserDefaults key for storing auth error simulation state + /// Note: Persists across app restarts - toggle again to disable + public static let debugKey = "DebugSimulateAuthError" + + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Toggle - Simulate Auth Error", attributes: [:]) + } + + override var status: NSAttributedString? { + let isEnabled = Self.isEnabled + let status = isEnabled ? "ON (Auth will fail)" : "OFF" + return NSAttributedString(string: "\(status) (Click to toggle)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + let currentValue = Self.isEnabled + UserDefaults.standard.set(!currentValue, forKey: Self.debugKey) + settings.tableView.reloadData() + + let alert = AlertController( + title: !currentValue ? "Auth Error Enabled ✅" : "Auth Error Disabled ✅", + message: !currentValue ? "Next login/logout will fail with an error." : "Auth errors disabled.", + preferredStyle: .alert + ) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + } + } + + /// Check if auth error simulation is enabled + public static var isEnabled: Bool { + UserDefaults.standard.bool(forKey: debugKey) + } +} + +final class SimulateImpactAPIErrorSetting: HiddenSetting { + /// UserDefaults key for storing impact API error simulation state + /// Note: Persists across app restarts - toggle again to disable + public static let debugKey = "DebugSimulateImpactAPIError" + + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Toggle - Simulate Impact API Error", attributes: [:]) + } + + override var status: NSAttributedString? { + let isEnabled = Self.isEnabled + let status = isEnabled ? "ON (API will fail)" : "OFF" + return NSAttributedString(string: "\(status) (Click to toggle)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + let currentValue = Self.isEnabled + UserDefaults.standard.set(!currentValue, forKey: Self.debugKey) + settings.tableView.reloadData() + + let alert = AlertController( + title: !currentValue ? "Impact API Error Enabled ✅" : "Impact API Error Disabled ✅", + message: !currentValue ? "Next impact API call will fail." : "Impact API errors disabled.", + preferredStyle: .alert + ) + navigationController?.topViewController?.present(alert, animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { + alert.dismiss(animated: true) + } + } + } + + /// Check if impact API error simulation is enabled + public static var isEnabled: Bool { + UserDefaults.standard.bool(forKey: debugKey) + } +} + +// MARK: - Seed & Level Debug Settings + +final class DebugAddSeedsLoggedOut: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Add 1 Seed (Logged Out) - 10s delay", attributes: [:]) + } + + override var status: NSAttributedString? { + let maxSeeds = UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers + let currentSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let remaining = max(0, maxSeeds - currentSeeds) + return NSAttributedString(string: "\(currentSeeds)/\(maxSeeds) seeds | \(remaining) remaining (cap always ON)", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + // Check if user is logged in + guard !EcosiaAuthenticationService.shared.isLoggedIn else { + let errorAlert = AlertController( + title: "Already Logged In", + message: "This feature is for logged-out users only. Please use the logged-in debug options instead.", + preferredStyle: .alert + ) + errorAlert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(errorAlert, animated: true) + return + } + + let currentSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let maxSeeds = UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers + + // Check if already at cap + if currentSeeds >= maxSeeds { + let alert = AlertController( + title: "Seed Cap Reached", + message: "Already at maximum (\(maxSeeds) seeds) for logged-out users", + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(alert, animated: true) + return + } + + let alert = AlertController( + title: "Seed Queued ✅", + message: "Navigate to home or open Account Impact within 10 seconds to see animation", + preferredStyle: .alert + ) + + navigationController?.topViewController?.present(alert, animated: true) { + // Dismiss alert after 2 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + alert.dismiss(animated: true) + } + + // Add seed after 10 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { + UserDefaultsSeedProgressManager.addSeeds(1) + EcosiaLogger.accounts.info("Debug: Added 1 seed for logged-out user") + } + } + } +} + +final class DebugAddSeedsLoggedIn: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Add 5 Seeds (Logged In) - 10s delay", attributes: [:]) + } + + override var status: NSAttributedString? { + let currentSeeds = EcosiaAuthUIStateProvider.shared.seedCount + return NSAttributedString(string: "Current: \(currentSeeds) seeds", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + guard EcosiaAuthenticationService.shared.isLoggedIn else { + let errorAlert = AlertController( + title: "Not Logged In", + message: "Please log in first to use this feature", + preferredStyle: .alert + ) + errorAlert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(errorAlert, animated: true) + return + } + + let alert = AlertController( + title: "Seeds Queued ✅", + message: "Navigate to home or open Account Impact within 10 seconds to see animation", + preferredStyle: .alert + ) + + navigationController?.topViewController?.present(alert, animated: true) { + // Dismiss alert after 2 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + alert.dismiss(animated: true) + } + + // Add seeds after 10 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { + Task { @MainActor in + EcosiaAuthUIStateProvider.shared.debugAddSeeds(5) + } + } + } + } +} + +final class DebugForceLevelUp: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Force Level Up (Logged In) - 10s delay", attributes: [:]) + } + + override var status: NSAttributedString? { + return NSAttributedString(string: "Triggers sparkle animation", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + guard EcosiaAuthenticationService.shared.isLoggedIn else { + let errorAlert = AlertController( + title: "Not Logged In", + message: "Please log in first to use this feature", + preferredStyle: .alert + ) + errorAlert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(errorAlert, animated: true) + return + } + + let alert = AlertController( + title: "Level Up Queued ✅", + message: "Navigate to Account Impact within 10 seconds to see level-up animation", + preferredStyle: .alert + ) + + navigationController?.topViewController?.present(alert, animated: true) { + // Dismiss alert after 2 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + alert.dismiss(animated: true) + } + + // Trigger level-up animation after 10 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { + Task { @MainActor in + EcosiaAuthUIStateProvider.shared.debugTriggerLevelUpAnimation() + } + } + } + } +} + +final class DebugAddCustomSeeds: HiddenSetting { + override var title: NSAttributedString? { + return NSAttributedString(string: "Debug: Add Custom Seeds (Logged In)", attributes: [:]) + } + + override var status: NSAttributedString? { + let currentSeeds = EcosiaAuthUIStateProvider.shared.seedCount + return NSAttributedString(string: "Current: \(currentSeeds) seeds | Input custom amount", attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + guard EcosiaAuthenticationService.shared.isLoggedIn else { + let errorAlert = AlertController( + title: "Not Logged In", + message: "Please log in first to use this feature", + preferredStyle: .alert + ) + errorAlert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(errorAlert, animated: true) + return + } + + let alert = AlertController( + title: "Add Custom Seeds", + message: "Enter the number of seeds to add (1-1000)", + preferredStyle: .alert + ) + + alert.addTextField { textField in + textField.placeholder = "Number of seeds" + textField.keyboardType = .numberPad + } + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + + alert.addAction(UIAlertAction(title: "Add", style: .default) { [weak self] _ in + guard let textField = alert.textFields?.first, + let text = textField.text, + let seedCount = Int(text), + seedCount > 0 && seedCount <= 1000 else { + let errorAlert = AlertController( + title: "Invalid Input", + message: "Please enter a number between 1 and 1000", + preferredStyle: .alert + ) + errorAlert.addAction(UIAlertAction(title: "OK", style: .default)) + navigationController?.topViewController?.present(errorAlert, animated: true) + return + } + + self?.addCustomSeeds(count: seedCount, navigationController: navigationController) + }) + + navigationController?.topViewController?.present(alert, animated: true) + } + + private func addCustomSeeds(count: Int, navigationController: UINavigationController?) { + let confirmAlert = AlertController( + title: "Seeds Queued ✅", + message: "Adding \(count) seeds in 10 seconds. Navigate to home or open Account Impact to see animation.", + preferredStyle: .alert + ) + + navigationController?.topViewController?.present(confirmAlert, animated: true) { + // Dismiss alert after 2 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + confirmAlert.dismiss(animated: true) + } + + // Add seeds after 10 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) { + Task { @MainActor in + EcosiaAuthUIStateProvider.shared.debugAddSeeds(count) + } + } + } + } +} diff --git a/firefox-ios/Client/Ecosia/Settings/EcosiaSettings.swift b/firefox-ios/Client/Ecosia/Settings/EcosiaSettings.swift new file mode 100644 index 0000000000000..a86ad4f30686a --- /dev/null +++ b/firefox-ios/Client/Ecosia/Settings/EcosiaSettings.swift @@ -0,0 +1,331 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Common +import Ecosia + +func ecosiaDisclosureIndicator(theme: Theme) -> UIImageView { + let config = UIImage.SymbolConfiguration(pointSize: 16) + let disclosureIndicator = UIImageView(image: .init(systemName: "chevron.right", withConfiguration: config)) + disclosureIndicator.contentMode = .center + disclosureIndicator.tintColor = theme.colors.ecosia.textSecondary + disclosureIndicator.sizeToFit() + return disclosureIndicator +} + +final class EcosiaDefaultBrowserSettings: Setting { + + override var accessoryView: UIImageView? { ecosiaDisclosureIndicator(theme: theme) } + + override var title: NSAttributedString? { + NSAttributedString(string: .localized(.defaultBrowserSettingTitle), attributes: [:]) + } + + override func onClick(_ navigationController: UINavigationController?) { + DefaultBrowserCoordinator.makeDefaultCoordinatorAndShowDetailViewFrom(navigationController, + analyticsLabel: .settings, + topViewContentBackground: EcosiaColor.DarkGreen50.color, + with: theme) + } +} + +final class SearchAreaSetting: Setting { + override var title: NSAttributedString? { + NSAttributedString(string: .localized(.searchRegion), attributes: [:]) + } + + override var accessoryView: UIImageView? { return ecosiaDisclosureIndicator(theme: theme) } + + override var style: UITableViewCell.CellStyle { return .value1 } + + override var status: NSAttributedString { return NSAttributedString(string: Markets.current ?? "") } + + override var accessibilityIdentifier: String? { return .localized(.searchRegion) } + + let windowUUID: UUID + init(settings: SettingsTableViewController) { + self.windowUUID = settings.windowUUID + super.init() + } + + override func onClick(_ navigationController: UINavigationController?) { + navigationController?.pushViewController(MarketsController(style: .insetGrouped, windowUUID: windowUUID), animated: true) + } + + override func onConfigureCell(_ cell: UITableViewCell, theme: Theme) { + super.onConfigureCell(cell, theme: theme) + cell.detailTextLabel?.numberOfLines = 2 + cell.detailTextLabel?.adjustsFontSizeToFitWidth = true + cell.detailTextLabel?.minimumScaleFactor = 0.8 + cell.detailTextLabel?.allowsDefaultTighteningForTruncation = true + cell.textLabel?.numberOfLines = 2 + } +} + +final class SafeSearchSettings: Setting { + override var title: NSAttributedString? { + NSAttributedString(string: .localized(.safeSearch), attributes: [:]) + } + + override var accessoryView: UIImageView? { return ecosiaDisclosureIndicator(theme: theme) } + + override var style: UITableViewCell.CellStyle { return .value1 } + + override var status: NSAttributedString { return NSAttributedString(string: FilterController.current ?? "") } + + override var accessibilityIdentifier: String? { return .localized(.searchRegion) } + + let windowUUID: UUID + init(settings: SettingsTableViewController) { + self.windowUUID = settings.windowUUID + super.init() + } + + override func onClick(_ navigationController: UINavigationController?) { + navigationController?.pushViewController(FilterController(style: .insetGrouped, windowUUID: windowUUID), animated: true) + } + + override func onConfigureCell(_ cell: UITableViewCell, theme: Theme) { + super.onConfigureCell(cell, theme: theme) + cell.detailTextLabel?.numberOfLines = 2 + cell.textLabel?.numberOfLines = 2 + } +} + +final class AutoCompleteSettings: BoolSetting { + convenience init(prefs: Prefs, theme: Theme) { + self.init(prefs: prefs, + theme: theme, + prefKey: "", + defaultValue: true, + titleText: .localized(.autocomplete), + statusText: .localized(.shownUnderSearchField), + settingDidChange: { value in + User.shared.autoComplete = value + }) + } + + override func displayBool(_ control: UISwitch) { + control.isOn = User.shared.autoComplete + } + + override func writeBool(_ control: UISwitch) { + User.shared.autoComplete = control.isOn + } +} + +final class PersonalSearchSettings: BoolSetting { + convenience init(prefs: Prefs, theme: Theme) { + self.init(prefs: prefs, + theme: theme, + prefKey: "", + defaultValue: false, + titleText: .localized(.personalizedResults), + statusText: .localized(.relevantResults), + settingDidChange: { value in + User.shared.personalized = value + }) + } + + override func displayBool(_ control: UISwitch) { + control.isOn = User.shared.personalized + } + + override func writeBool(_ control: UISwitch) { + User.shared.personalized = control.isOn + } +} + +final class AIOverviewsSearchSettings: BoolSetting { + convenience init(prefs: Prefs, theme: Theme) { + self.init(prefs: prefs, + theme: theme, + prefKey: "", + defaultValue: false, + titleText: .localized(.aiOverviewsTitle), + statusText: .localized(.aiOverviewsDescription), + settingDidChange: { value in + User.shared.aiOverviews = value + Analytics.shared.toggleAISearchOverviewsSetting(enabled: value) + }) + } + + override func displayBool(_ control: UISwitch) { + control.isOn = User.shared.aiOverviews + } + + override func writeBool(_ control: UISwitch) { + User.shared.aiOverviews = control.isOn + } +} + +final class EcosiaPrivacyPolicySetting: Setting { + override var title: NSAttributedString? { + return NSAttributedString(string: .localized(.privacy), attributes: [:]) + } + + override var url: URL? { + return EcosiaEnvironment.current.urlProvider.privacy + } + + let windowUUID: UUID + init(settings: SettingsTableViewController) { + self.windowUUID = settings.windowUUID + super.init() + } + + override func onClick(_ navigationController: UINavigationController?) { + setUpAndPushSettingsContentViewController(navigationController, url: self.url, windowUUID: windowUUID) + Analytics.shared.navigation(.open, label: .privacy) + } +} + +final class EcosiaTermsSetting: Setting { + override var title: NSAttributedString? { + return NSAttributedString(string: .localized(.terms), attributes: [:]) + } + + override var url: URL? { + return EcosiaEnvironment.current.urlProvider.terms + } + + let windowUUID: UUID + init(settings: SettingsTableViewController) { + self.windowUUID = settings.windowUUID + super.init() + } + + override func onClick(_ navigationController: UINavigationController?) { + setUpAndPushSettingsContentViewController(navigationController, url: self.url, windowUUID: windowUUID) + Analytics.shared.navigation(.open, label: .terms) + } +} + +final class EcosiaSendAnonymousUsageDataSetting: BoolSetting { + convenience init(prefs: Prefs, theme: Theme) { + self.init(prefs: prefs, + theme: theme, + prefKey: "", + defaultValue: true, + titleText: .localized(.sendUsageDataSettingsTitle), + statusText: .localized(.sendUsageDataSettingsDescription), + settingDidChange: { value in + User.shared.sendAnonymousUsageData = value + Analytics.shared.sendAnonymousUsageDataSetting(enabled: value) + }) + } + + override func displayBool(_ control: UISwitch) { + control.isOn = User.shared.sendAnonymousUsageData + } + + override func writeBool(_ control: UISwitch) { + User.shared.sendAnonymousUsageData = control.isOn + } +} + +final class HomepageSettings: Setting { + private var profile: Profile + + override var accessoryView: UIImageView? { ecosiaDisclosureIndicator(theme: theme) } + + let windowUUID: WindowUUID + init(settings: SettingsTableViewController, settingsDelegate: SettingsDelegate?) { + self.profile = settings.profile + self.windowUUID = settings.windowUUID + super.init(title: NSAttributedString(string: .localized(.homepage))) + self.delegate = settingsDelegate + } + + override func onClick(_ navigationController: UINavigationController?) { + let customizationViewController = NTPCustomizationSettingsViewController(windowUUID: windowUUID) + customizationViewController.profile = profile + customizationViewController.settingsDelegate = delegate + navigationController?.pushViewController(customizationViewController, animated: true) + } +} + +/// Setting option that opens the Ecosia Help Center +class HelpCenterSetting: Setting { + override var accessoryType: UITableViewCell.AccessoryType { return .disclosureIndicator } + + override var accessibilityIdentifier: String? { return "HelpCenter" } + + override var url: URL? { + return EcosiaEnvironment.current.urlProvider.faq + } + + let windowUUID = WindowUUID() + + override func onClick(_ navigationController: UINavigationController?) { + setUpAndPushSettingsContentViewController(navigationController, url: self.url, windowUUID: windowUUID) + Analytics.shared.navigation(.open, label: .help) + } + + init() { + super.init(title: NSAttributedString(string: String.localized(.helpCenter))) + } +} + +/// Setting for displaying the feedback view from the settings menu +class EcosiaSendFeedbackSetting: Setting { + override var accessoryType: UITableViewCell.AccessoryType { return .disclosureIndicator } + + override var accessibilityIdentifier: String? { return "SendFeedback" } + + let windowUUID: WindowUUID + + override func onClick(_ navigationController: UINavigationController?) { + let feedbackVC = FeedbackViewController(windowUUID: windowUUID) + feedbackVC.onFeedbackSubmitted = { [weak self, weak navigationController] in + self?.showThankYouToast(in: navigationController?.view) + } + navigationController?.present(feedbackVC, animated: true) + Analytics.shared.navigation(.open, label: .sendFeedback) + } + + /// Show a toast thanking the user for their feedback + private func showThankYouToast(in view: UIView?) { + guard let view, + let themeManager: ThemeManager = AppContainer.shared.resolve() else { + return + } + + // Get the default theme + let theme = themeManager.getCurrentTheme(for: windowUUID) + + // Show the thank you message + SimpleToast().ecosiaShowAlertWithText( + String.localized(.thankYouForYourFeedback), + bottomContainer: view, + theme: theme, + bottomInset: view.layoutMargins.bottom) + } + + init(settings: SettingsTableViewController) { + self.windowUUID = settings.windowUUID + super.init(title: NSAttributedString(string: String.localized(.sendFeedback))) + } + + init() { + self.windowUUID = WindowUUID() + super.init(title: NSAttributedString(string: String.localized(.sendFeedback))) + } +} + +extension Setting { + // Helper method to set up and push a SettingsContentViewController + func setUpAndPushSettingsContentViewController(_ navigationController: UINavigationController?, + url: URL? = nil, + windowUUID: WindowUUID) { + if let url = self.url { + let viewController = SettingsContentViewController(windowUUID: windowUUID) + viewController.settingsTitle = self.title + viewController.url = url + navigationController?.pushViewController(viewController, animated: true) + } + } +} diff --git a/firefox-ios/Client/Ecosia/Settings/NTPCustomizationSettingsViewController.swift b/firefox-ios/Client/Ecosia/Settings/NTPCustomizationSettingsViewController.swift new file mode 100644 index 0000000000000..9392cdd66dfa6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/Settings/NTPCustomizationSettingsViewController.swift @@ -0,0 +1,70 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Common +import Ecosia + +final class NTPCustomizationSettingsViewController: SettingsTableViewController { + init(windowUUID: WindowUUID) { + super.init(style: .insetGrouped, windowUUID: windowUUID) + + title = .localized(.homepage) + navigationItem.rightBarButtonItem = .init(title: .localized(.done), + style: .done) { [weak self] _ in + self?.settingsDelegate?.reloadHomepage() + self?.settingsDelegate?.didFinish() + } + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override func generateSettings() -> [SettingSection] { + let customizableSectionConfigs = HomepageSectionType.allCases.compactMap({ $0.customizableConfig }) + let settings: [Setting] = customizableSectionConfigs.map { config in + if config == .topSites { + return HomePageSettingViewController.TopSitesSettings(settings: self) + } + return NTPCustomizationSetting(prefs: profile.prefs, + theme: themeManager.getCurrentTheme(for: windowUUID), + config: config) + } + return [SettingSection(title: .init(string: .localized(.showOnHomepage)), children: settings)] + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationController?.navigationBar.prefersLargeTitles = false + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + settingsDelegate?.reloadHomepage() + } +} + +final class NTPCustomizationSetting: BoolSetting { + private var config: CustomizableNTPSettingConfig = .topSites + + convenience init(prefs: Prefs, theme: Theme, config: CustomizableNTPSettingConfig) { + self.init(prefs: prefs, + theme: theme, + accessibilityIdentifier: config.accessibilityIdentifierPrefix, + defaultValue: true, + titleText: .localized(config.localizedTitleKey)) + self.config = config + } + + override func displayBool(_ control: UISwitch) { + control.isOn = config.persistedFlag + } + + override func writeBool(_ control: UISwitch) { + config.persistedFlag = control.isOn + Analytics.shared.ntpCustomisation(control.isOn ? .enable : .disable, label: config.analyticsLabel) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/BeforeOrAfterView.swift b/firefox-ios/Client/Ecosia/UI/BeforeOrAfterView.swift new file mode 100644 index 0000000000000..a3ca25b7cad8d --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/BeforeOrAfterView.swift @@ -0,0 +1,155 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +final class BeforeOrAfterView: UIView, ThemeApplicable { + enum ContentType { + case before + case after + } + + private lazy var container: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.layer.cornerRadius = .ecosia.borderRadius._l + return view + }() + private lazy var labelStack: UIStackView = { + let stack = UIStackView() + stack.translatesAutoresizingMaskIntoConstraints = false + stack.axis = .horizontal + stack.spacing = .ecosia.space._2s + return stack + }() + private lazy var label: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.text = isBefore ? .localized(.before) : .localized(.after) + label.font = .preferredFont(forTextStyle: .subheadline).semibold() + label.adjustsFontForContentSizeCategory = true + label.setContentHuggingPriority(.required, for: .horizontal) + return label + }() + private lazy var treeImage: UIImageView = { + let view = UIImageView() + view.isHidden = isBefore + view.image = .init(named: "smallTree")?.withRenderingMode(.alwaysTemplate) + return view + }() + private lazy var ellipse: UIImageView = { + let image = UIImageView() + image.translatesAutoresizingMaskIntoConstraints = false + image.image = .init(named: isBefore ? "ellipseBefore" : "ellipseAfter") + image.setContentHuggingPriority(.required, for: .horizontal) + return image + }() + private lazy var dot: Dot = { + return Dot(effect: UIBlurEffect(style: .light)) + }() + + var isBefore: Bool { type == .before } + private let type: ContentType + init(type: ContentType) { + self.type = type + super.init(frame: .zero) + setup() + setupConstraints() + } + + required init?(coder: NSCoder) { nil } + + private func setup() { + addSubview(container) + labelStack.addArrangedSubview(treeImage) + labelStack.addArrangedSubview(label) + container.addSubview(labelStack) + addSubview(ellipse) + addSubview(dot) + } + + private func setupConstraints() { + var constraints: [NSLayoutConstraint] = [ + // View's height equals dot's since that's desired our Y anchor + topAnchor.constraint(equalTo: dot.topAnchor), + bottomAnchor.constraint(equalTo: dot.bottomAnchor), + + labelStack.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: .ecosia.space._s), + labelStack.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -.ecosia.space._s), + labelStack.topAnchor.constraint(equalTo: container.topAnchor, constant: .ecosia.space._s), + labelStack.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -.ecosia.space._s), + ] + + if isBefore { + constraints.append(contentsOf: [ + leadingAnchor.constraint(equalTo: container.leadingAnchor), + trailingAnchor.constraint(equalTo: dot.trailingAnchor), + ellipse.leadingAnchor.constraint(equalTo: container.trailingAnchor), + ellipse.topAnchor.constraint(equalTo: container.centerYAnchor), + dot.topAnchor.constraint(equalTo: ellipse.bottomAnchor), + dot.centerXAnchor.constraint(equalTo: ellipse.trailingAnchor), + ]) + } else { + constraints.append(contentsOf: [ + leadingAnchor.constraint(equalTo: dot.leadingAnchor), + trailingAnchor.constraint(equalTo: container.trailingAnchor), + ellipse.trailingAnchor.constraint(equalTo: container.leadingAnchor), + ellipse.bottomAnchor.constraint(equalTo: container.centerYAnchor), + dot.bottomAnchor.constraint(equalTo: ellipse.topAnchor), + dot.centerXAnchor.constraint(equalTo: ellipse.leadingAnchor), + ]) + } + NSLayoutConstraint.activate(constraints) + } + + func applyTheme(theme: Theme) { + container.backgroundColor = theme.colors.ecosia.backgroundSecondary + label.textColor = theme.colors.ecosia.textPrimary + treeImage.tintColor = theme.colors.ecosia.brandPrimary + dot.applyTheme(theme: theme) + } +} + +private final class Dot: UIVisualEffectView, ThemeApplicable { + struct UX { + static let size: CGFloat = 20 + static let centerSize: CGFloat = 8 + } + + private lazy var centerDot: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.layer.cornerRadius = UX.centerSize/2 + return view + }() + + override init(effect: UIVisualEffect?) { + super.init(effect: effect) + setup() + } + + required init?(coder: NSCoder) { nil } + + func setup() { + translatesAutoresizingMaskIntoConstraints = false + layer.cornerRadius = UX.size/2 + layer.masksToBounds = true + contentView.addSubview(centerDot) + + NSLayoutConstraint.activate([ + heightAnchor.constraint(equalToConstant: UX.size), + widthAnchor.constraint(equalToConstant: UX.size), + centerDot.heightAnchor.constraint(equalToConstant: UX.centerSize), + centerDot.widthAnchor.constraint(equalToConstant: UX.centerSize), + centerDot.centerYAnchor.constraint(equalTo: centerYAnchor), + centerDot.centerXAnchor.constraint(equalTo: centerXAnchor) + ]) + } + + func applyTheme(theme: Theme) { + centerDot.backgroundColor = theme.colors.ecosia.backgroundPrimary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_close.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_close.imageset/Contents.json new file mode 100644 index 0000000000000..93044cb41d346 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_close.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_close.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_close.imageset/Group.pdf new file mode 100644 index 0000000000000..1331a52cf6301 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_close.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_next.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_next.imageset/Contents.json new file mode 100644 index 0000000000000..c24e593d60a10 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_next.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_next.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_next.imageset/Group.pdf new file mode 100644 index 0000000000000..f0bd70115caa6 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_next.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_previous.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_previous.imageset/Contents.json new file mode 100644 index 0000000000000..c24e593d60a10 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_previous.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_previous.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_previous.imageset/Group.pdf new file mode 100644 index 0000000000000..22fe8f2fa65ce Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/Find In Page/find_previous.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-add.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-add.imageset/Contents.json new file mode 100644 index 0000000000000..91328e8a06f84 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-add.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-add.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-add.imageset/Group.pdf new file mode 100644 index 0000000000000..49ba749011667 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-add.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-back.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-back.imageset/Contents.json new file mode 100644 index 0000000000000..55e34df475475 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-back.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal", + "language-direction" : "left-to-right" + }, + { + "idiom" : "universal", + "language-direction" : "right-to-left" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-back.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-back.imageset/Group.pdf new file mode 100644 index 0000000000000..8197271bf0be2 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-back.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-forward.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-forward.imageset/Contents.json new file mode 100644 index 0000000000000..55e34df475475 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-forward.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal", + "language-direction" : "left-to-right" + }, + { + "idiom" : "universal", + "language-direction" : "right-to-left" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-forward.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-forward.imageset/Group.pdf new file mode 100644 index 0000000000000..a91bbe6c2a087 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-forward.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-menu.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-menu.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-menu.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-menu.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-menu.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..dff540dbb1912 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-menu.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-stop.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-stop.imageset/Contents.json new file mode 100644 index 0000000000000..62cdac6fd2363 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-stop.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-stop.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-stop.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..b0216faa699f7 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/TabToolbar/nav-stop.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_pin.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_pin.imageset/Contents.json new file mode 100644 index 0000000000000..7f0a05f487ef6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_pin.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Outline icons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_pin.imageset/Outline icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_pin.imageset/Outline icons.pdf new file mode 100644 index 0000000000000..526c7046f38e6 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_pin.imageset/Outline icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_unpin.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_unpin.imageset/Contents.json new file mode 100644 index 0000000000000..bd77badc6c322 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_unpin.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Solid Icons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_unpin.imageset/Solid Icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_unpin.imageset/Solid Icons.pdf new file mode 100644 index 0000000000000..c46ce6812a0ba Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/action_unpin.imageset/Solid Icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/addToReadingListUpdate.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/addToReadingListUpdate.imageset/Contents.json new file mode 100644 index 0000000000000..e214375ac26c4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/addToReadingListUpdate.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "reader_add.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/addToReadingListUpdate.imageset/reader_add.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/addToReadingListUpdate.imageset/reader_add.pdf new file mode 100644 index 0000000000000..773a78cc3b384 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/addToReadingListUpdate.imageset/reader_add.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/Contents.json new file mode 100644 index 0000000000000..f82b6af6cf6dc --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "apnConsentImageControl@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "apnConsentImageControl@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/apnConsentImageControl@2x.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/apnConsentImageControl@2x.png new file mode 100644 index 0000000000000..c1e4f3c1f7085 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/apnConsentImageControl@2x.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/apnConsentImageControl@3x.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/apnConsentImageControl@3x.png new file mode 100644 index 0000000000000..1eeafc2c7c9a6 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageControl.imageset/apnConsentImageControl@3x.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/Contents.json new file mode 100644 index 0000000000000..2834df9a6fcf2 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "apnConsentImageTest1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "apnConsentImageTest1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/apnConsentImageTest1@2x.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/apnConsentImageTest1@2x.png new file mode 100644 index 0000000000000..33130ff8e5ece Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/apnConsentImageTest1@2x.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/apnConsentImageTest1@3x.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/apnConsentImageTest1@3x.png new file mode 100644 index 0000000000000..eda54232da180 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/apnConsentImageTest1.imageset/apnConsentImageTest1@3x.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/backChevron.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/backChevron.imageset/Contents.json new file mode 100644 index 0000000000000..0d93a22e047fc --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/backChevron.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "backChevron.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/backChevron.imageset/backChevron.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/backChevron.imageset/backChevron.pdf new file mode 100644 index 0000000000000..f4a93e3ecc9c9 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/backChevron.imageset/backChevron.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkAdd.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkAdd.imageset/Contents.json new file mode 100644 index 0000000000000..1d5c3fdab142c --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkAdd.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "bookmark-add.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkAdd.imageset/bookmark-add.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkAdd.imageset/bookmark-add.svg new file mode 100644 index 0000000000000..78eb2e86f3a93 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkAdd.imageset/bookmark-add.svg @@ -0,0 +1,4 @@ + + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkFill.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkFill.imageset/Contents.json new file mode 100644 index 0000000000000..154ac7622d0d6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkFill.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Solid Icons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkFill.imageset/Solid Icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkFill.imageset/Solid Icons.pdf new file mode 100644 index 0000000000000..242de53e505bb Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkFill.imageset/Solid Icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkImportExport.imageset/Component 1.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkImportExport.imageset/Component 1.svg new file mode 100644 index 0000000000000..02d2b0fee0cbd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkImportExport.imageset/Component 1.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkImportExport.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkImportExport.imageset/Contents.json new file mode 100644 index 0000000000000..6e9157bfe9872 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkImportExport.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Component 1.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkSuccess.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkSuccess.imageset/Contents.json new file mode 100644 index 0000000000000..5c327a805bc3f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkSuccess.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "bookmarkSuccess.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkSuccess.imageset/bookmarkSuccess.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkSuccess.imageset/bookmarkSuccess.svg new file mode 100644 index 0000000000000..a16270cb2880b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarkSuccess.imageset/bookmarkSuccess.svg @@ -0,0 +1,3 @@ + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarksEmpty.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarksEmpty.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarksEmpty.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarksEmpty.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarksEmpty.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..b399c411493dc Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/bookmarksEmpty.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/chevronDown.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/chevronDown.imageset/Contents.json new file mode 100644 index 0000000000000..98c89b637ca45 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/chevronDown.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "chevronDown.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/chevronDown.imageset/chevronDown.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/chevronDown.imageset/chevronDown.pdf new file mode 100644 index 0000000000000..47c04006965b9 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/chevronDown.imageset/chevronDown.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/closeButtonFilled.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/closeButtonFilled.imageset/Contents.json new file mode 100644 index 0000000000000..b8234260e8340 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/closeButtonFilled.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "closeButton.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/closeButtonFilled.imageset/closeButton.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/closeButtonFilled.imageset/closeButton.pdf new file mode 100644 index 0000000000000..b1d3a4157eac0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/closeButtonFilled.imageset/closeButton.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/customisation.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/customisation.imageset/Contents.json new file mode 100644 index 0000000000000..848858873e63b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/customisation.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "customisation.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/customisation.imageset/customisation.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/customisation.imageset/customisation.pdf new file mode 100644 index 0000000000000..bbb17dfe3f498 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/customisation.imageset/customisation.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkMode.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkMode.imageset/Contents.json new file mode 100644 index 0000000000000..936cc3c97c99f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkMode.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "darkMode.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkMode.imageset/darkMode.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkMode.imageset/darkMode.svg new file mode 100644 index 0000000000000..72c9dce0c86c2 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkMode.imageset/darkMode.svg @@ -0,0 +1,3 @@ + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkModeSolid.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkModeSolid.imageset/Contents.json new file mode 100644 index 0000000000000..aff27b2499abf --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkModeSolid.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "darkModeSolid.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkModeSolid.imageset/darkModeSolid.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkModeSolid.imageset/darkModeSolid.svg new file mode 100644 index 0000000000000..ca9c0acd70a4d --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/darkModeSolid.imageset/darkModeSolid.svg @@ -0,0 +1,3 @@ + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/Contents.json new file mode 100644 index 0000000000000..3826caccc495f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/Contents.json @@ -0,0 +1,31 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "filename" : "showcase-impact.png", + "idiom" : "iphone", + "scale" : "2x" + }, + { + "filename" : "showcase-impact-1.png", + "idiom" : "iphone", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "scale" : "1x" + }, + { + "filename" : "showcase-impact-2.png", + "idiom" : "ipad", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact-1.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact-1.png new file mode 100644 index 0000000000000..06a3fb4ad5aa7 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact-1.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact-2.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact-2.png new file mode 100644 index 0000000000000..8c38f6a9ab18d Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact-2.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact.png new file mode 100644 index 0000000000000..2b078bae5fb8f Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowser.imageset/showcase-impact.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowserWaves.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowserWaves.imageset/Contents.json new file mode 100644 index 0000000000000..a922bfa15530e --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowserWaves.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "wave-forms-horizontal.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowserWaves.imageset/wave-forms-horizontal.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowserWaves.imageset/wave-forms-horizontal.pdf new file mode 100644 index 0000000000000..7ef790e63a9f2 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/defaultBrowserWaves.imageset/wave-forms-horizontal.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmpty.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmpty.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmpty.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmpty.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmpty.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..1ad3e1c55a874 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmpty.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmptyDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmptyDark.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmptyDark.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmptyDark.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmptyDark.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..1ad3e1c55a874 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/downloadsEmptyDark.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/Contents.json new file mode 100644 index 0000000000000..f79d7ff543202 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "filename" : "ecosia-logo.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "ecosia-logo-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/ecosia-logo-dark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/ecosia-logo-dark.pdf new file mode 100644 index 0000000000000..09403a91698d6 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/ecosia-logo-dark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/ecosia-logo.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/ecosia-logo.pdf new file mode 100644 index 0000000000000..4d12230435006 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaLogoLaunch.imageset/ecosia-logo.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaShop.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaShop.imageset/Contents.json new file mode 100644 index 0000000000000..92678434bf3ea --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaShop.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "ecosiaShop.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaShop.imageset/ecosiaShop.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaShop.imageset/ecosiaShop.pdf new file mode 100644 index 0000000000000..b18f85e7409aa Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ecosiaShop.imageset/ecosiaShop.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseAfter.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseAfter.imageset/Contents.json new file mode 100644 index 0000000000000..9b5c1f2ac1051 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseAfter.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Ellipse_2.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseAfter.imageset/Ellipse_2.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseAfter.imageset/Ellipse_2.pdf new file mode 100644 index 0000000000000..c0894444a289e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseAfter.imageset/Ellipse_2.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseBefore.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseBefore.imageset/Contents.json new file mode 100644 index 0000000000000..186725a980a5f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseBefore.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Ellipse.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseBefore.imageset/Ellipse.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseBefore.imageset/Ellipse.pdf new file mode 100644 index 0000000000000..e278a44a948d0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/ellipseBefore.imageset/Ellipse.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/exportShare.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/exportShare.imageset/Contents.json new file mode 100644 index 0000000000000..f7b73d4b6420e --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/exportShare.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "export-share.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/exportShare.imageset/export-share.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/exportShare.imageset/export-share.svg new file mode 100644 index 0000000000000..0a424711a923a --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/exportShare.imageset/export-share.svg @@ -0,0 +1,4 @@ + + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/fail.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/fail.imageset/Contents.json new file mode 100644 index 0000000000000..e0345e8dde178 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/fail.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "fail.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/fail.imageset/fail.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/fail.imageset/fail.pdf new file mode 100644 index 0000000000000..5787bb52521c4 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/fail.imageset/fail.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/faqs.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/faqs.imageset/Contents.json new file mode 100644 index 0000000000000..757ec792ff4ae --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/faqs.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "faqs.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/faqs.imageset/faqs.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/faqs.imageset/faqs.pdf new file mode 100644 index 0000000000000..121819a963d5d Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/faqs.imageset/faqs.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/forwardChevron.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/forwardChevron.imageset/Contents.json new file mode 100644 index 0000000000000..7fbebfed35a1c --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/forwardChevron.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "forwardChevron.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/forwardChevron.imageset/forwardChevron.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/forwardChevron.imageset/forwardChevron.pdf new file mode 100644 index 0000000000000..6d833841fa3c4 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/forwardChevron.imageset/forwardChevron.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friends.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friends.imageset/Contents.json new file mode 100644 index 0000000000000..676b184b23ba0 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friends.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "friends.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friends.imageset/friends.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friends.imageset/friends.pdf new file mode 100644 index 0000000000000..89779ab168616 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friends.imageset/friends.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friendsDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friendsDark.imageset/Contents.json new file mode 100644 index 0000000000000..8a5ab5cb906e9 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friendsDark.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "friendsDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friendsDark.imageset/friendsDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friendsDark.imageset/friendsDark.pdf new file mode 100644 index 0000000000000..6382dd90592a2 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/friendsDark.imageset/friendsDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorks.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorks.imageset/Contents.json new file mode 100644 index 0000000000000..6563ce890fc1b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorks.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "howItWorks.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorks.imageset/howItWorks.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorks.imageset/howItWorks.pdf new file mode 100644 index 0000000000000..056045618bce3 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorks.imageset/howItWorks.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorksDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorksDark.imageset/Contents.json new file mode 100644 index 0000000000000..73b4adeca2525 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorksDark.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "howItWorksDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorksDark.imageset/howItWorksDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorksDark.imageset/howItWorksDark.pdf new file mode 100644 index 0000000000000..6dccb5d9911b0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/howItWorksDark.imageset/howItWorksDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/image_placeholder.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/image_placeholder.imageset/Contents.json new file mode 100644 index 0000000000000..fad1babd7f8fb --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/image_placeholder.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "image_placeholder.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/image_placeholder.imageset/image_placeholder.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/image_placeholder.imageset/image_placeholder.pdf new file mode 100644 index 0000000000000..e5aed949eb38c Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/image_placeholder.imageset/image_placeholder.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDown.imageset/Buttons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDown.imageset/Buttons.pdf new file mode 100644 index 0000000000000..41463cbf20920 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDown.imageset/Buttons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDown.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDown.imageset/Contents.json new file mode 100644 index 0000000000000..35f262761ee5f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDown.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Buttons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDownDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDownDark.imageset/Contents.json new file mode 100644 index 0000000000000..4253a4225862d --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDownDark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "impactDownDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDownDark.imageset/impactDownDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDownDark.imageset/impactDownDark.pdf new file mode 100644 index 0000000000000..13f68ebf5b0b8 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactDownDark.imageset/impactDownDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferrals.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferrals.imageset/Contents.json new file mode 100644 index 0000000000000..2fce08ba49a70 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferrals.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "referrals.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferrals.imageset/referrals.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferrals.imageset/referrals.pdf new file mode 100644 index 0000000000000..24145c1b61a01 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferrals.imageset/referrals.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferralsDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferralsDark.imageset/Contents.json new file mode 100644 index 0000000000000..65d077078c653 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferralsDark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "referralsDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferralsDark.imageset/referralsDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferralsDark.imageset/referralsDark.pdf new file mode 100644 index 0000000000000..31b7549243c47 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactReferralsDark.imageset/referralsDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUp.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUp.imageset/Contents.json new file mode 100644 index 0000000000000..27d1f023c3521 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUp.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Group 407.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUp.imageset/Group 407.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUp.imageset/Group 407.pdf new file mode 100644 index 0000000000000..fca322f65b603 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUp.imageset/Group 407.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUpDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUpDark.imageset/Contents.json new file mode 100644 index 0000000000000..27d1f023c3521 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUpDark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Group 407.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUpDark.imageset/Group 407.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUpDark.imageset/Group 407.pdf new file mode 100644 index 0000000000000..cded01210c44f Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/impactUpDark.imageset/Group 407.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/launchScreenBackground.colorset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/launchScreenBackground.colorset/Contents.json new file mode 100644 index 0000000000000..ea0adeb743dc8 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/launchScreenBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF6", + "green" : "0xF8", + "red" : "0xF8" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1A", + "green" : "0x1A", + "red" : "0x1A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryDownloads.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryDownloads.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryDownloads.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryDownloads.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryDownloads.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..1ad3e1c55a874 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryDownloads.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryFavorites.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryFavorites.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryFavorites.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryFavorites.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryFavorites.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..863580c476455 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryFavorites.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryHistory.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryHistory.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryHistory.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryHistory.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryHistory.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..72fe023e7857e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryHistory.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryReading.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryReading.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryReading.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryReading.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryReading.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..926c7908948a4 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/libraryReading.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/lightMode.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/lightMode.imageset/Contents.json new file mode 100644 index 0000000000000..9464237657fc9 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/lightMode.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "lightMode.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/lightMode.imageset/lightMode.svg b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/lightMode.imageset/lightMode.svg new file mode 100644 index 0000000000000..6f54015b07887 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/lightMode.imageset/lightMode.svg @@ -0,0 +1,3 @@ + + + diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Bookmark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Bookmark.imageset/Contents.json new file mode 100644 index 0000000000000..7f0a05f487ef6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Bookmark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Outline icons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Bookmark.imageset/Outline icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Bookmark.imageset/Outline icons.pdf new file mode 100644 index 0000000000000..cc591e1cdb20b Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Bookmark.imageset/Outline icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Copy-Link.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Copy-Link.imageset/Contents.json new file mode 100644 index 0000000000000..62cdac6fd2363 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Copy-Link.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Copy-Link.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Copy-Link.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..d4389334ca309 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Copy-Link.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-FindInPageUpdate.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-FindInPageUpdate.imageset/Contents.json new file mode 100644 index 0000000000000..7f0a05f487ef6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-FindInPageUpdate.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Outline icons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-FindInPageUpdate.imageset/Outline icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-FindInPageUpdate.imageset/Outline icons.pdf new file mode 100644 index 0000000000000..170ace49081c0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-FindInPageUpdate.imageset/Outline icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-RequestDesktopSite.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-RequestDesktopSite.imageset/Contents.json new file mode 100644 index 0000000000000..7f0a05f487ef6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-RequestDesktopSite.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Outline icons.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-RequestDesktopSite.imageset/Outline icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-RequestDesktopSite.imageset/Outline icons.pdf new file mode 100644 index 0000000000000..58f23e1922fb2 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-RequestDesktopSite.imageset/Outline icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ScanQRCode.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ScanQRCode.imageset/Contents.json new file mode 100644 index 0000000000000..91328e8a06f84 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ScanQRCode.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ScanQRCode.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ScanQRCode.imageset/Group.pdf new file mode 100644 index 0000000000000..8d1d27998e914 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ScanQRCode.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Send-to-Device.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Send-to-Device.imageset/Contents.json new file mode 100644 index 0000000000000..02b5d87ae1b32 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Send-to-Device.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Send-to-Device.imageset/Group.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Send-to-Device.imageset/Group.pdf new file mode 100644 index 0000000000000..0973fcace4b1e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-Send-to-Device.imageset/Group.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ViewMobile.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ViewMobile.imageset/Contents.json new file mode 100644 index 0000000000000..aa15bdc65d2bc --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ViewMobile.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Icon-mobile.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ViewMobile.imageset/Icon-mobile.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ViewMobile.imageset/Icon-mobile.pdf new file mode 100644 index 0000000000000..9abf0b1c94156 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/menu-ViewMobile.imageset/Icon-mobile.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/myImpact.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/myImpact.imageset/Contents.json new file mode 100644 index 0000000000000..73463ed766b70 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/myImpact.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/myImpact.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/myImpact.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..4ae6582fd73ad Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/myImpact.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/news.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/news.imageset/Contents.json new file mode 100644 index 0000000000000..b251baf43f4f3 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/news.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "news.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/news.imageset/news.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/news.imageset/news.pdf new file mode 100644 index 0000000000000..c87e58ee5c983 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/news.imageset/news.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/newsletterCardImage.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/newsletterCardImage.imageset/Contents.json new file mode 100644 index 0000000000000..8ec1e1ec05834 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/newsletterCardImage.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "newsletterCardImage.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/newsletterCardImage.imageset/newsletterCardImage.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/newsletterCardImage.imageset/newsletterCardImage.pdf new file mode 100644 index 0000000000000..b125a5849035a Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/newsletterCardImage.imageset/newsletterCardImage.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/noInternet.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/noInternet.imageset/Contents.json new file mode 100644 index 0000000000000..a7f794a6ce361 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/noInternet.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "noInternet.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/noInternet.imageset/noInternet.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/noInternet.imageset/noInternet.pdf new file mode 100644 index 0000000000000..0cb9e1c5e4c75 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/noInternet.imageset/noInternet.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/openLink.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/openLink.imageset/Contents.json new file mode 100644 index 0000000000000..c004c090db654 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/openLink.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "tab-open.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/openLink.imageset/tab-open.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/openLink.imageset/tab-open.pdf new file mode 100644 index 0000000000000..0c28567ad59e2 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/openLink.imageset/tab-open.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/paperplane.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/paperplane.imageset/Contents.json new file mode 100644 index 0000000000000..866c8b48ff56f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/paperplane.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "paperplane.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/paperplane.imageset/paperplane.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/paperplane.imageset/paperplane.pdf new file mode 100644 index 0000000000000..be4c05ec9cd70 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/paperplane.imageset/paperplane.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounter.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounter.imageset/Contents.json new file mode 100644 index 0000000000000..13509b783a6f0 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounter.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "personalCounter.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounter.imageset/personalCounter.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounter.imageset/personalCounter.pdf new file mode 100644 index 0000000000000..45de3558188b3 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounter.imageset/personalCounter.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounterDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounterDark.imageset/Contents.json new file mode 100644 index 0000000000000..df72f902ebca2 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounterDark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "personalCounterDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounterDark.imageset/personalCounterDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounterDark.imageset/personalCounterDark.pdf new file mode 100644 index 0000000000000..2580a11568475 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/personalCounterDark.imageset/personalCounterDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/Contents.json new file mode 100644 index 0000000000000..f533b88e33004 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "trees-planted.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "trees-planted (1).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/trees-planted (1).pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/trees-planted (1).pdf new file mode 100644 index 0000000000000..b13e2450a690e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/trees-planted (1).pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/trees-planted.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/trees-planted.pdf new file mode 100644 index 0000000000000..6c6b4c908427e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/plantedSeedling.imageset/trees-planted.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/privateSearch.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/privateSearch.imageset/Contents.json new file mode 100644 index 0000000000000..cf0fa787c9dea --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/privateSearch.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "privateSearch.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/privateSearch.imageset/privateSearch.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/privateSearch.imageset/privateSearch.pdf new file mode 100644 index 0000000000000..1426d6425c643 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/privateSearch.imageset/privateSearch.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/problem.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/problem.imageset/Contents.json new file mode 100644 index 0000000000000..6adb547dea800 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/problem.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "problem.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/problem.imageset/problem.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/problem.imageset/problem.pdf new file mode 100644 index 0000000000000..b0374fae4407d Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/problem.imageset/problem.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/reader-action-bar.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/reader-action-bar.imageset/Contents.json new file mode 100644 index 0000000000000..aa6d0dfcd6509 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/reader-action-bar.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "reader-action-bar.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/reader-action-bar.imageset/reader-action-bar.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/reader-action-bar.imageset/reader-action-bar.pdf new file mode 100644 index 0000000000000..dfe116af2b18c Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/reader-action-bar.imageset/reader-action-bar.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/readerEmpty.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/readerEmpty.imageset/Contents.json new file mode 100644 index 0000000000000..6559aa39e2063 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/readerEmpty.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "readerEmpty.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/readerEmpty.imageset/readerEmpty.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/readerEmpty.imageset/readerEmpty.pdf new file mode 100644 index 0000000000000..96729141abe27 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/readerEmpty.imageset/readerEmpty.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandBookmarkRemove.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandBookmarkRemove.imageset/Contents.json new file mode 100644 index 0000000000000..9e1136ed263b3 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandBookmarkRemove.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "rebrandBookmarkRemove.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandBookmarkRemove.imageset/rebrandBookmarkRemove.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandBookmarkRemove.imageset/rebrandBookmarkRemove.pdf new file mode 100644 index 0000000000000..029ab895d0668 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandBookmarkRemove.imageset/rebrandBookmarkRemove.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandNewTab.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandNewTab.imageset/Contents.json new file mode 100644 index 0000000000000..d1825cd4c6f1f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandNewTab.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "rebrandNewTab.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandNewTab.imageset/rebrandNewTab.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandNewTab.imageset/rebrandNewTab.pdf new file mode 100644 index 0000000000000..4ba0c77c7156e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandNewTab.imageset/rebrandNewTab.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandPrivate.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandPrivate.imageset/Contents.json new file mode 100644 index 0000000000000..e4e52d8966644 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandPrivate.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "rebrandPrivate.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandPrivate.imageset/rebrandPrivate.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandPrivate.imageset/rebrandPrivate.pdf new file mode 100644 index 0000000000000..14cff6d60fed2 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandPrivate.imageset/rebrandPrivate.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandRemove.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandRemove.imageset/Contents.json new file mode 100644 index 0000000000000..4fd82753e13e9 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandRemove.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "rebrandRemove.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandRemove.imageset/rebrandRemove.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandRemove.imageset/rebrandRemove.pdf new file mode 100644 index 0000000000000..7fccadf340813 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/rebrandRemove.imageset/rebrandRemove.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/recentlyClosed.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/recentlyClosed.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/recentlyClosed.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/recentlyClosed.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/recentlyClosed.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..b6e4d320c4ea0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/recentlyClosed.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/safari.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/safari.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/safari.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/safari.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/safari.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..5c52fbc7e3dc6 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/safari.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchAppend.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchAppend.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchAppend.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchAppend.imageset/Icon Set.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchAppend.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..a6848a815291b Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchAppend.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchFilled.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchFilled.imageset/Contents.json new file mode 100644 index 0000000000000..e30a6644eaa28 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchFilled.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "search.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchFilled.imageset/search.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchFilled.imageset/search.pdf new file mode 100644 index 0000000000000..04b5aa5d378c8 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchFilled.imageset/search.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchUrl.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchUrl.imageset/Contents.json new file mode 100644 index 0000000000000..e30a6644eaa28 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchUrl.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "search.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchUrl.imageset/search.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchUrl.imageset/search.pdf new file mode 100644 index 0000000000000..04b5aa5d378c8 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchUrl.imageset/search.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searches.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searches.imageset/Contents.json new file mode 100644 index 0000000000000..77ab243e42c20 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searches.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "searches.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searches.imageset/searches.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searches.imageset/searches.pdf new file mode 100644 index 0000000000000..4768c889ad27d Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searches.imageset/searches.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchesDark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchesDark.imageset/Contents.json new file mode 100644 index 0000000000000..5b4c88213575b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchesDark.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "searchesDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchesDark.imageset/searchesDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchesDark.imageset/searchesDark.pdf new file mode 100644 index 0000000000000..25a90485f94c8 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/searchesDark.imageset/searchesDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/secureLock.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/secureLock.imageset/Contents.json new file mode 100644 index 0000000000000..a875b347a2ed2 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/secureLock.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "secureLock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/secureLock.imageset/secureLock.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/secureLock.imageset/secureLock.pdf new file mode 100644 index 0000000000000..2a5528431ff62 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/secureLock.imageset/secureLock.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/seedIcon.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/seedIcon.imageset/Contents.json new file mode 100644 index 0000000000000..0c56bb1346165 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/seedIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "seedIcon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/seedIcon.imageset/seedIcon.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/seedIcon.imageset/seedIcon.pdf new file mode 100644 index 0000000000000..5a50a85a69f08 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/seedIcon.imageset/seedIcon.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/Contents.json new file mode 100644 index 0000000000000..e33f1db72cbe5 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "shopIcon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "shopIcon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "shopIcon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon.png new file mode 100644 index 0000000000000..92e49a1a255a9 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon@2x.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon@2x.png new file mode 100644 index 0000000000000..4534d70b61898 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon@2x.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon@3x.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon@3x.png new file mode 100644 index 0000000000000..a6ce21d5eca8e Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/shopIcon.imageset/shopIcon@3x.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/smallTree.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/smallTree.imageset/Contents.json new file mode 100644 index 0000000000000..ec2048b9ead98 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/smallTree.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "tourTree.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/smallTree.imageset/tourTree.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/smallTree.imageset/tourTree.pdf new file mode 100644 index 0000000000000..bb7360dd81d15 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/smallTree.imageset/tourTree.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashMask.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashMask.imageset/Contents.json new file mode 100644 index 0000000000000..90df12ca46f0a --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashMask.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "mask.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashMask.imageset/mask.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashMask.imageset/mask.pdf new file mode 100644 index 0000000000000..175c50c5031c7 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashMask.imageset/mask.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree1.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree1.imageset/Contents.json new file mode 100644 index 0000000000000..d80db317304c4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree1.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Vector-2.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree1.imageset/Vector-2.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree1.imageset/Vector-2.pdf new file mode 100644 index 0000000000000..69b8733c8f243 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree1.imageset/Vector-2.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree2.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree2.imageset/Contents.json new file mode 100644 index 0000000000000..596dc1df2d1f4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree2.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Vector-3.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree2.imageset/Vector-3.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree2.imageset/Vector-3.pdf new file mode 100644 index 0000000000000..34769d2fa5c82 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/splashTree2.imageset/Vector-3.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/Contents.json new file mode 100644 index 0000000000000..97c5916e6d340 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "photo-energy.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "photo-energy (1).png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/photo-energy (1).png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/photo-energy (1).png new file mode 100644 index 0000000000000..58902bfcc02f6 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/photo-energy (1).png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/photo-energy.png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/photo-energy.png new file mode 100644 index 0000000000000..80254519ee1a4 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightCleanEnergy.imageset/photo-energy.png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/Contents.json new file mode 100644 index 0000000000000..d17f51462fd7a --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "photo-before-after (1).png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "photo-before-after (2).png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/photo-before-after (1).png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/photo-before-after (1).png new file mode 100644 index 0000000000000..f0d57ce9991f8 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/photo-before-after (1).png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/photo-before-after (2).png b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/photo-before-after (2).png new file mode 100644 index 0000000000000..a1c8c782da0fb Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/spotlightPlanetProfits.imageset/photo-before-after (2).png differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/stroke.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/stroke.imageset/Contents.json new file mode 100644 index 0000000000000..b7bd38354b1fe --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/stroke.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "stroke.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/stroke.imageset/stroke.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/stroke.imageset/stroke.pdf new file mode 100644 index 0000000000000..485a25ce3af2b Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/stroke.imageset/stroke.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/switchTab.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/switchTab.imageset/Contents.json new file mode 100644 index 0000000000000..28099cbc2dd63 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/switchTab.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "switchTab.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/switchTab.imageset/switchTab.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/switchTab.imageset/switchTab.pdf new file mode 100644 index 0000000000000..b6e4d320c4ea0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/switchTab.imageset/switchTab.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tabs.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tabs.imageset/Contents.json new file mode 100644 index 0000000000000..a3fcbdc67d9d7 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tabs.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "tabs.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tabs.imageset/tabs.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tabs.imageset/tabs.pdf new file mode 100644 index 0000000000000..c5a7fb77267b4 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tabs.imageset/tabs.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tail.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tail.imageset/Contents.json new file mode 100644 index 0000000000000..8a52fecff1558 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tail.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Tail.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tail.imageset/Tail.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tail.imageset/Tail.pdf new file mode 100644 index 0000000000000..ce40b5b9025f7 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tail.imageset/Tail.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Brand-Icons 1.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Brand-Icons 1.pdf new file mode 100644 index 0000000000000..5823cea495917 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Brand-Icons 1.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Brand-Icons.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Brand-Icons.pdf new file mode 100644 index 0000000000000..5cbf1db8c8f26 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Brand-Icons.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Contents.json new file mode 100644 index 0000000000000..cfae28596e8a6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tigerIncognito.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "Brand-Icons.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Brand-Icons 1.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tree.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tree.imageset/Contents.json new file mode 100644 index 0000000000000..3fcf8567480ff --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tree.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "tree.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tree.imageset/tree.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tree.imageset/tree.pdf new file mode 100644 index 0000000000000..59653bf8c7bca Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/tree.imageset/tree.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/unsecureLock.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/unsecureLock.imageset/Contents.json new file mode 100644 index 0000000000000..9e2ea3a07fe19 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/unsecureLock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "unsecureLock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/unsecureLock.imageset/unsecureLock.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/unsecureLock.imageset/unsecureLock.pdf new file mode 100644 index 0000000000000..64b9c650c47f3 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/unsecureLock.imageset/unsecureLock.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/waves.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/waves.imageset/Contents.json new file mode 100644 index 0000000000000..8fd6e9d768dac --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/waves.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Wave.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/waves.imageset/Wave.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/waves.imageset/Wave.pdf new file mode 100644 index 0000000000000..ea30db0d8dcc9 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/waves.imageset/Wave.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/whatsNewTrees.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/whatsNewTrees.imageset/Contents.json new file mode 100644 index 0000000000000..8921bd39d3018 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/whatsNewTrees.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "whatsNewTrees.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/whatsNewTrees.imageset/whatsNewTrees.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/whatsNewTrees.imageset/whatsNewTrees.pdf new file mode 100644 index 0000000000000..3ad2788c18381 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/whatsNewTrees.imageset/whatsNewTrees.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/xmark.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/xmark.imageset/Contents.json new file mode 100644 index 0000000000000..8d4f6d4b1c9d5 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/xmark.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "xmark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/xmark.imageset/xmark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/xmark.imageset/xmark.pdf new file mode 100644 index 0000000000000..7da766a5b0b0f Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/xmark.imageset/xmark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/Contents.json b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/Contents.json new file mode 100644 index 0000000000000..380446377d31b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "yourImpact.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "yourImpactDark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/yourImpact.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/yourImpact.pdf new file mode 100644 index 0000000000000..fa0580e33ed9f Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/yourImpact.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/yourImpactDark.pdf b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/yourImpactDark.pdf new file mode 100644 index 0000000000000..ca521e3cd86d0 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Ecosia.xcassets/yourImpact.imageset/yourImpactDark.pdf differ diff --git a/firefox-ios/Client/Ecosia/UI/EcosiaFindInPageBar.swift b/firefox-ios/Client/Ecosia/UI/EcosiaFindInPageBar.swift new file mode 100644 index 0000000000000..4535301bd8daa --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EcosiaFindInPageBar.swift @@ -0,0 +1,247 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Shared +import Common +import Ecosia + +protocol EcosiaFindInPageBarDelegate: AnyObject { + func findInPage(_ findInPage: EcosiaFindInPageBar, didTextChange text: String) + func findInPage(_ findInPage: EcosiaFindInPageBar, didFindPreviousWithText text: String) + func findInPage(_ findInPage: EcosiaFindInPageBar, didFindNextWithText text: String) + func findInPageDidPressClose(_ findInPage: EcosiaFindInPageBar) +} + +/// Ecosia's custom UI for FindInPageBar. +/// +/// You can find the Firefox original view in Client/Frontend/Browser/FindInPageBar (removed from Target since no longer used) +final class EcosiaFindInPageBar: UIView, ThemeApplicable { + private struct UX { + static let barHeight: CGFloat = 60 + static let searchViewTopBottomSpacing: CGFloat = 8 + static let searchViewLeadingOffset = 16 + static let searchTextFieldLeadingOffset = 16 + static let previousButtonLeadingOffset = 14 + static let nextButtonLeadingOffset = 29 + static let closeButtonLeadingTrailingSpacing = 14 + static let topBorderHeight = 1 + } + + private lazy var searchView: UIView = { + let view = UIView() + view.layer.cornerRadius = (UX.barHeight - 2*UX.searchViewTopBottomSpacing)/2 + return view + }() + private lazy var searchTextField: UITextField = { + let textField = UITextField() + textField.addTarget(self, action: #selector(didTextChange), for: .editingChanged) + textField.font = .preferredFont(forTextStyle: .body) + textField.autocapitalizationType = .none + textField.autocorrectionType = .no + textField.inputAssistantItem.leadingBarButtonGroups = [] + textField.inputAssistantItem.trailingBarButtonGroups = [] + textField.enablesReturnKeyAutomatically = true + textField.returnKeyType = .search + textField.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.FindInPage.searchField + textField.delegate = self + return textField + }() + private lazy var matchCountLabel: UILabel = { + let label = UILabel() + label.font = .preferredFont(forTextStyle: .footnote) + label.textAlignment = .right + label.isHidden = true + label.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.FindInPage.matchCount + return label + }() + private lazy var previousButton: UIButton = { + let button = UIButton() + button.setImage(UIImage.templateImageNamed("find_previous"), for: .normal) + button.isEnabled = false + button.accessibilityLabel = .FindInPagePreviousAccessibilityLabel + button.addTarget(self, action: #selector(didFindPrevious), for: .touchUpInside) + button.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.FindInPage.findPrevious + return button + }() + private lazy var nextButton: UIButton = { + let button = UIButton() + button.setImage(UIImage.templateImageNamed("find_next"), for: .normal) + button.isEnabled = false + button.accessibilityLabel = .FindInPageNextAccessibilityLabel + button.addTarget(self, action: #selector(didFindNext), for: .touchUpInside) + button.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.FindInPage.findNext + return button + }() + private lazy var closeButton: UIButton = { + let button = UIButton() + button.setTitle(.localized(.done), for: .normal) + button.accessibilityLabel = .FindInPageDoneAccessibilityLabel + button.addTarget(self, action: #selector(didPressClose), for: .touchUpInside) + button.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.FindInPage.findClose + return button + }() + private lazy var topBorder = UIView() + + weak var delegate: EcosiaFindInPageBarDelegate? + + private static let savedTextKey = "findInPageSavedTextKey" + static var retrieveSavedText: String? { + return UserDefaults.standard.object(forKey: EcosiaFindInPageBar.savedTextKey) as? String + } + var currentResult = 0 { + didSet { + if totalResults > 500 { + matchCountLabel.text = "\(currentResult)/500+" + } else { + matchCountLabel.text = "\(currentResult)/\(totalResults)" + } + } + } + var totalResults = 0 { + didSet { + if totalResults > 500 { + matchCountLabel.text = "\(currentResult)/500+" + } else { + matchCountLabel.text = "\(currentResult)/\(totalResults)" + } + previousButton.isEnabled = totalResults > 1 + nextButton.isEnabled = previousButton.isEnabled + } + } + var text: String? { + get { + return searchTextField.text + } + + set { + searchTextField.text = newValue + didTextChange(searchTextField) + } + } + + // MARK: - Init + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(searchView) + searchView.addSubview(searchTextField) + searchView.addSubview(matchCountLabel) + addSubview(previousButton) + addSubview(nextButton) + addSubview(closeButton) + addSubview(topBorder) + + setupConstraints() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + @discardableResult override func becomeFirstResponder() -> Bool { + searchTextField.becomeFirstResponder() + return super.becomeFirstResponder() + } + + func applyTheme(theme: Theme) { + backgroundColor = theme.colors.ecosia.backgroundSecondary + searchView.backgroundColor = theme.colors.ecosia.backgroundTertiary + searchTextField.textColor = theme.colors.ecosia.textPrimary + searchTextField.attributedPlaceholder = .init(string: .localized(.findInPage), + attributes: [.foregroundColor: theme.colors.ecosia.textSecondary]) + matchCountLabel.textColor = theme.colors.ecosia.textSecondary + previousButton.tintColor = theme.colors.ecosia.buttonContentSecondary + nextButton.tintColor = theme.colors.ecosia.buttonContentSecondary + closeButton.setTitleColor(theme.colors.ecosia.buttonBackgroundPrimary, for: .normal) + topBorder.backgroundColor = theme.colors.ecosia.borderDecorative + } + + private func setupConstraints() { + self.snp.makeConstraints { make in + make.height.equalTo(UX.barHeight) + } + + searchView.snp.makeConstraints { make in + make.leading.equalToSuperview().offset(UX.searchViewLeadingOffset) + make.top.equalToSuperview().offset(UX.searchViewTopBottomSpacing) + make.bottom.equalToSuperview().inset(UX.searchViewTopBottomSpacing) + } + + searchTextField.snp.makeConstraints { make in + make.leading.equalToSuperview().offset(UX.searchTextFieldLeadingOffset) + make.centerY.equalToSuperview() + } + searchTextField.setContentHuggingPriority(.defaultLow, for: .horizontal) + searchTextField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + + matchCountLabel.snp.makeConstraints { make in + make.leading.equalTo(searchTextField.snp.trailing) + make.trailing.equalToSuperview().inset(13) + make.centerY.equalToSuperview() + } + matchCountLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) + matchCountLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + + previousButton.snp.makeConstraints { make in + make.leading.equalTo(searchView.snp.trailing).offset(UX.previousButtonLeadingOffset) + make.centerY.equalToSuperview() + } + + nextButton.snp.makeConstraints { make in + make.leading.equalTo(previousButton.snp.trailing).offset(UX.nextButtonLeadingOffset) + make.centerY.equalToSuperview() + } + + closeButton.snp.makeConstraints { make in + make.leading.equalTo(nextButton.snp.trailing).offset(UX.closeButtonLeadingTrailingSpacing) + make.trailing.equalToSuperview().inset(UX.closeButtonLeadingTrailingSpacing) + make.trailing.centerY.equalToSuperview() + } + + topBorder.snp.makeConstraints { make in + make.height.equalTo(UX.topBorderHeight) + make.left.right.top.equalToSuperview() + } + } + + @objc private func didFindPrevious(_ sender: UIButton) { + delegate?.findInPage(self, didFindPreviousWithText: searchTextField.text ?? "") + } + + @objc private func didFindNext(_ sender: UIButton) { + delegate?.findInPage(self, didFindNextWithText: searchTextField.text ?? "") + } + + @objc private func didTextChange(_ sender: UITextField) { + matchCountLabel.isHidden = searchTextField.text?.trimmingCharacters(in: .whitespaces).isEmpty ?? true + saveSearchText(searchTextField.text) + delegate?.findInPage(self, didTextChange: searchTextField.text ?? "") + } + + @objc private func didPressClose(_ sender: UIButton) { + delegate?.findInPageDidPressClose(self) + } + + private func saveSearchText(_ searchText: String?) { + guard let text = searchText, !text.isEmpty else { return } + UserDefaults.standard.set(text, forKey: EcosiaFindInPageBar.savedTextKey) + } +} + +extension EcosiaFindInPageBar: UITextFieldDelegate { + // Keyboard with a .search returnKeyType doesn't dismiss when return pressed. Handle this manually. + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + if string == "\n" { + textField.resignFirstResponder() + return false + } + return true + } +} diff --git a/firefox-ios/Client/Ecosia/UI/EcosiaNavigation.swift b/firefox-ios/Client/Ecosia/UI/EcosiaNavigation.swift new file mode 100644 index 0000000000000..28eb5dc30f7c4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EcosiaNavigation.swift @@ -0,0 +1,54 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class EcosiaNavigation: UINavigationController, Themeable { + + // MARK: - Themeable Properties + + var themeManager: ThemeManager { AppContainer.shared.resolve() } + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + var currentWindowUUID: WindowUUID? { (topViewController as? Themeable)?.currentWindowUUID } + + // MARK: - Init + + override init(rootViewController: UIViewController) { + super.init(rootViewController: rootViewController) + commonInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + private func commonInit() { + if traitCollection.userInterfaceIdiom == .pad { + modalPresentationStyle = .formSheet + preferredContentSize = .init(width: 544, height: .max) + } else { + modalPresentationCapturesStatusBarAppearance = true + } + } + + override func viewDidLoad() { + super.viewDidLoad() + navigationBar.setBackgroundImage(UIImage(), for: .default) + navigationBar.shadowImage = UIImage() + navigationBar.prefersLargeTitles = true + listenForThemeChange(self.view) + applyTheme() + } + + func applyTheme() { + (topViewController as? Themeable)?.applyTheme() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + .lightContent + } +} diff --git a/firefox-ios/Client/Ecosia/UI/EcosiaPrimaryButton.swift b/firefox-ios/Client/Ecosia/UI/EcosiaPrimaryButton.swift new file mode 100644 index 0000000000000..906057789b3d8 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EcosiaPrimaryButton.swift @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +class EcosiaPrimaryButton: UIButton { + let windowUUID: WindowUUID + + init(windowUUID: WindowUUID) { + self.windowUUID = windowUUID + super.init(frame: .zero) + } + required init?(coder: NSCoder) { nil } + + override var isSelected: Bool { + get { + return super.isSelected + } + set { + super.isSelected = newValue + update() + } + } + + override var isHighlighted: Bool { + get { + return super.isHighlighted + } + set { + super.isHighlighted = newValue + update() + } + } + + private func update() { + let themeManager: ThemeManager = AppContainer.shared.resolve() + let theme = themeManager.getCurrentTheme(for: windowUUID) + backgroundColor = (isSelected || isHighlighted) ? theme.colors.ecosia.buttonBackgroundPrimaryActive : theme.colors.ecosia.buttonBackgroundPrimary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/EmptyBookmarksView.swift b/firefox-ios/Client/Ecosia/UI/EmptyBookmarksView.swift new file mode 100644 index 0000000000000..340170e4a3939 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EmptyBookmarksView.swift @@ -0,0 +1,283 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +final class EmptyBookmarksView: UIView, ThemeApplicable { + + private enum UX { + static let TitleLabelFont = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)) + static let SectionLabelFont = UIFontMetrics(forTextStyle: .callout).scaledFont(for: .systemFont(ofSize: 16)) + static let SectionEnumerationFont = UIFontMetrics(forTextStyle: .callout).scaledFont(for: .monospacedDigitSystemFont(ofSize: 16, weight: .regular)) + static let LearnMoreButtonLabelFont = UIFontMetrics(forTextStyle: .callout).scaledFont(for: .systemFont(ofSize: 16)) + static let ImportButtonLabelFont = UIFontMetrics(forTextStyle: .callout).scaledFont(for: .systemFont(ofSize: 16)) + static let ImportButtonPaddingInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + static let LayoutMargingsInset: CGFloat = 12 + static let ImportButtonBorderWidth: CGFloat = 1 + static let ImportButtonCornerRadius: CGFloat = 20 + static let TitleSpacerHeight: CGFloat = 24 + static let InBetweenSpacerWidth: CGFloat = 8 + static let SectionSpacerWidth: CGFloat = 36 + static let SectionIconLabelSpacerWidth: CGFloat = 24 + static let SectionEndSpacerHeight: CGFloat = 16 + static let SectionIconWidth: CGFloat = 18 + static let SectionContainerMaxWidth: CGFloat = 450 + static let ButtonAreaHeight: CGFloat = 50 + } + + private let titleLabel: UILabel = { + let label = UILabel() + label.text = .localized(.noBookmarksYet) + label.textAlignment = .center + label.font = UX.TitleLabelFont + label.adjustsFontForContentSizeCategory = true + return label + }() + + private let containerStackView: UIStackView = { + let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + return stackView + }() + + private let learnMoreButton: UIButton = { + let button = UIButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle(.localized(.learnMore), for: .normal) + button.setContentCompressionResistancePriority(.required, for: .horizontal) + return button + }() + + private let importBookmarksButton: UIButton = { + let button = UIButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle(.localized(.importBookmarks), for: .normal) + button.layer.borderWidth = UX.ImportButtonBorderWidth + button.layer.cornerRadius = UX.ImportButtonCornerRadius + button.setInsets( + forContentPadding: UX.ImportButtonPaddingInset, + imageTitlePadding: 0 + ) + button.setContentCompressionResistancePriority(.required, for: .horizontal) + return button + }() + + weak var delegate: EmptyBookmarksViewDelegate? + + var bottomMarginConstraint: NSLayoutConstraint? + + // MARK: - Init + + required init?(coder: NSCoder) { + assertionFailure("This view is only supposed to be instantiated programmatically") + return nil + } + + init( + initialBottomMargin: CGFloat + ) { + super.init(frame: .zero) + setup(initialBottomMargin) + } + + private func setup(_ initialBottomMargin: CGFloat) { + addSubview(containerStackView) + + bottomMarginConstraint = containerStackView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: initialBottomMargin) + + NSLayoutConstraint.activate([ + containerStackView.leadingAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.leadingAnchor, constant: UX.LayoutMargingsInset), + containerStackView.trailingAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.trailingAnchor, constant: -UX.LayoutMargingsInset), + containerStackView.widthAnchor.constraint(lessThanOrEqualToConstant: UX.SectionContainerMaxWidth), + bottomMarginConstraint, + containerStackView.centerXAnchor.constraint(equalTo: centerXAnchor), + ].compactMap { $0 }) + + // title + containerStackView.addArrangedSubview(titleLabel) + + // space between title and first section + let spacerOne = UIView.build { + $0.heightAnchor.constraint(equalToConstant: UX.TitleSpacerHeight).isActive = true + } + containerStackView.addArrangedSubview(spacerOne) + + addSection(imageNamed: "bookmarkAdd", text: .localized(.bookmarksEmptyViewItem0)) + addSection(imageNamed: "exportShare", text: .localized(.bookmarksEmptyViewItem1), listItems: [ + .localized(.bookmarksEmptyViewItem1NumberedItem0), + .localized(.bookmarksEmptyViewItem1NumberedItem1) + ]) + + let buttonsContainerCenterView = UIView.build { centerView in + let buttonsContainer = UIView.build { view in + view.addSubview(self.learnMoreButton) + view.addSubview(self.importBookmarksButton) + NSLayoutConstraint.activate([ + self.learnMoreButton.centerYAnchor.constraint(equalTo: view.centerYAnchor), + self.importBookmarksButton.centerYAnchor.constraint(equalTo: view.centerYAnchor), + self.learnMoreButton.leadingAnchor.constraint(equalTo: view.leadingAnchor), + self.learnMoreButton.trailingAnchor.constraint(equalTo: self.importBookmarksButton.leadingAnchor, constant: -UX.InBetweenSpacerWidth).priority(.defaultHigh), + self.importBookmarksButton.trailingAnchor.constraint(equalTo: view.trailingAnchor), + view.heightAnchor.constraint(equalToConstant: UX.ButtonAreaHeight) + ]) + } + centerView.addSubview(buttonsContainer) + NSLayoutConstraint.activate([ + buttonsContainer.centerXAnchor.constraint(equalTo: centerView.centerXAnchor), + buttonsContainer.bottomAnchor.constraint(equalTo: centerView.bottomAnchor), + buttonsContainer.topAnchor.constraint(equalTo: centerView.topAnchor) + ]) + } + + containerStackView.addArrangedSubview(buttonsContainerCenterView) + + // setup buttons + learnMoreButton.addTarget(self, action: #selector(onLearnMoreTapped), for: .touchUpInside) + importBookmarksButton.addTarget(self, action: #selector(onImportTapped), for: .touchUpInside) + } + + private func addSection(imageNamed: String, text: String, listItems: [String]? = nil) { + // first section (tap the bookmark icon when you find a page you want to share) + let sectionStackView = UIStackView() + sectionStackView.axis = .horizontal + sectionStackView.alignment = .top + + if traitCollection.userInterfaceIdiom == .pad { + sectionStackView.addArrangedSubview(createSpacerView(width: UX.SectionSpacerWidth)) + } + + let sectionIcon = UIImageView() + sectionIcon.contentMode = .scaleAspectFit + sectionIcon.image = UIImage.templateImageNamed(imageNamed) + sectionIcon.setContentCompressionResistancePriority(.required, for: .horizontal) + sectionIcon.setContentHuggingPriority(.required, for: .horizontal) + sectionIcon.translatesAutoresizingMaskIntoConstraints = false + sectionIcon.widthAnchor.constraint(equalToConstant: UX.SectionIconWidth).priority(.required).isActive = true + + sectionStackView.addArrangedSubview(sectionIcon) + + let sectionOneIconLabelSpacer = UIView.build { + $0.widthAnchor.constraint(equalToConstant: UX.SectionIconLabelSpacerWidth).isActive = true + } + sectionStackView.addArrangedSubview(sectionOneIconLabelSpacer) + + let sectionLabelsStackView = UIStackView() + sectionLabelsStackView.axis = .vertical + sectionStackView.addArrangedSubview(sectionLabelsStackView) + + let sectionLabel = UILabel() + sectionLabel.font = UX.SectionLabelFont + sectionLabel.numberOfLines = 0 + sectionLabel.text = text + sectionLabel.adjustsFontForContentSizeCategory = true + sectionLabelsStackView.addArrangedSubview(sectionLabel) + + if let listItems = listItems { + for (index, listItem) in listItems.enumerated() { + let enumerationLabel = UILabel() + enumerationLabel.font = UX.SectionEnumerationFont + enumerationLabel.text = " \(index+1). " + + let textLabel = UILabel() + textLabel.font = UX.SectionLabelFont + textLabel.text = listItem + + [enumerationLabel, textLabel].forEach { + $0.setContentHuggingPriority(.required, for: .horizontal) + $0.setContentCompressionResistancePriority(.required, for: .horizontal) + $0.numberOfLines = 0 + $0.adjustsFontForContentSizeCategory = true + } + + let listItemStackView = UIStackView() + listItemStackView.axis = .horizontal + listItemStackView.alignment = .leading + listItemStackView.distribution = .fill + + listItemStackView.addArrangedSubview(enumerationLabel) + listItemStackView.addArrangedSubview(textLabel) + + listItemStackView.addArrangedSubview(UIView.build { + $0.setContentHuggingPriority(.required, for: .horizontal) + $0.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + }) + + sectionLabelsStackView.addArrangedSubview(listItemStackView) + } + } + + if traitCollection.userInterfaceIdiom == .pad { + sectionStackView.addArrangedSubview(createSpacerView(width: UX.SectionSpacerWidth)) + } + + containerStackView.addArrangedSubview(sectionStackView) + + let sectionEndSpacer = UIView.build { + $0.heightAnchor.constraint(equalToConstant: UX.SectionEndSpacerHeight).isActive = true + } + + containerStackView.addArrangedSubview(sectionEndSpacer) + } + + private func createSpacerView(width: CGFloat) -> UIView { + UIView.build { + $0.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) + $0.widthAnchor.constraint(equalToConstant: width).isActive = true + } + } + + @objc func onLearnMoreTapped() { + Analytics.shared.bookmarksEmptyLearnMoreClicked() + delegate?.emptyBookmarksViewLearnMoreTapped(self) + } + + @objc private func onImportTapped() { + delegate?.emptyBookmarksViewImportBookmarksTapped(self) + } + + func applyTheme(theme: Theme) { + backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + importBookmarksButton.layer.borderColor = theme.colors.ecosia.textPrimary.cgColor + learnMoreButton.setTitleColor(theme.colors.ecosia.textPrimary, for: .normal) + learnMoreButton.titleLabel?.font = UX.LearnMoreButtonLabelFont + importBookmarksButton.setTitleColor(theme.colors.ecosia.textPrimary, for: .normal) + importBookmarksButton.titleLabel?.font = UX.ImportButtonLabelFont + titleLabel.textColor = theme.colors.ecosia.textPrimary + applyThemeToSectionsIn(containerStackView, theme: theme) + } + + func applyThemeToSectionsIn(_ stackView: UIStackView, theme: Theme) { + var finalResult = [UIView]() + var iterateThroughSubviews: ((UIStackView) -> Void)? + + iterateThroughSubviews = { stackView in + for subview in stackView.arrangedSubviews { + if let subStackView = subview as? UIStackView { + iterateThroughSubviews?(subStackView) + } else { + finalResult.append(subview) + } + } + } + + stackView.arrangedSubviews.forEach { + if let stackView = $0 as? UIStackView { + iterateThroughSubviews?(stackView) + } + } + + finalResult.forEach { + switch $0 { + case let label as UILabel: + label.textColor = theme.colors.ecosia.textSecondary + default: + $0.tintColor = theme.colors.ecosia.textSecondary + break + } + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/EmptyBookmarksViewDelegate.swift b/firefox-ios/Client/Ecosia/UI/EmptyBookmarksViewDelegate.swift new file mode 100644 index 0000000000000..54ff8def79535 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EmptyBookmarksViewDelegate.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol EmptyBookmarksViewDelegate: AnyObject { + func emptyBookmarksViewLearnMoreTapped(_ view: EmptyBookmarksView) + func emptyBookmarksViewImportBookmarksTapped(_ view: EmptyBookmarksView) +} diff --git a/firefox-ios/Client/Ecosia/UI/EmptyHeader.swift b/firefox-ios/Client/Ecosia/UI/EmptyHeader.swift new file mode 100644 index 0000000000000..135b74e783538 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EmptyHeader.swift @@ -0,0 +1,72 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class EmptyHeader: UITableViewHeaderFooterView, ThemeApplicable { + private let icon: String + private weak var labelTitle: UILabel? + private weak var labelSubtitle: UILabel? + private weak var image: UIImageView? + + // MARK: - Init + + required init?(coder: NSCoder) { nil } + + init(icon: String, title: String, subtitle: String) { + self.icon = icon + super.init(reuseIdentifier: "EmptyHeader") + frame.size.height = 170 + + let image = UIImageView() + image.translatesAutoresizingMaskIntoConstraints = false + image.clipsToBounds = true + image.contentMode = .center + contentView.addSubview(image) + self.image = image + + let labelTitle = UILabel() + labelTitle.translatesAutoresizingMaskIntoConstraints = false + labelTitle.numberOfLines = 0 + labelTitle.text = title + labelTitle.font = .preferredFont(forTextStyle: .subheadline).bold() + labelTitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + labelTitle.adjustsFontForContentSizeCategory = true + labelTitle.textAlignment = .center + contentView.addSubview(labelTitle) + self.labelTitle = labelTitle + + let labelSubtitle = UILabel() + labelSubtitle.translatesAutoresizingMaskIntoConstraints = false + labelSubtitle.numberOfLines = 0 + labelSubtitle.text = subtitle + labelSubtitle.font = .preferredFont(forTextStyle: .subheadline) + labelSubtitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + labelSubtitle.adjustsFontForContentSizeCategory = true + labelSubtitle.textAlignment = .center + contentView.addSubview(labelSubtitle) + self.labelSubtitle = labelSubtitle + + image.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 32).isActive = true + image.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true + + labelTitle.topAnchor.constraint(equalTo: image.bottomAnchor, constant: 12).isActive = true + labelTitle.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true + labelTitle.widthAnchor.constraint(lessThanOrEqualToConstant: 180).isActive = true + + labelSubtitle.topAnchor.constraint(equalTo: labelTitle.bottomAnchor, constant: 4).isActive = true + labelSubtitle.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true + labelSubtitle.widthAnchor.constraint(lessThanOrEqualToConstant: 180).isActive = true + } + + // MARK: - ThemeApplicable + + func applyTheme(theme: Theme) { + image?.image = .init(named: icon)?.withRenderingMode(.alwaysTemplate) + image?.tintColor = theme.colors.ecosia.textSecondary + labelTitle?.textColor = theme.colors.ecosia.textPrimary + labelSubtitle?.textColor = theme.colors.ecosia.textSecondary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/EmptyReadingListView.swift b/firefox-ios/Client/Ecosia/UI/EmptyReadingListView.swift new file mode 100644 index 0000000000000..91697b4dd643c --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/EmptyReadingListView.swift @@ -0,0 +1,136 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class EmptyReadingListView: UIView, Themeable { + + struct ReadingListPanelUX { + static let WelcomeScreenPadding: CGFloat = 24 + static let WelcomeScreenHorizontalMinPadding: CGFloat = 40 + + static let WelcomeScreenMaxWidth: CGFloat = 400 + static let WelcomeScreenItemImageWidth: CGFloat = 20 + + static let WelcomeScreenTopPadding: CGFloat = 120 + } + + // MARK: - Themeable Properties + + let windowUUID: UUID + var themeManager: ThemeManager { AppContainer.shared.resolve() } + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + + // MARK: - Properties + + var welcomeLabel: UILabel = .build { label in + label.text = .ReaderPanelWelcome + label.textAlignment = .center + label.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .body, size: 16, weight: .semibold) + label.adjustsFontSizeToFitWidth = true + } + var readerModeLabel: UILabel = .build { label in + label.text = .ReaderPanelReadingModeDescription + label.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .body, size: 16, weight: .light) + label.numberOfLines = 0 + } + var readerModeImageView: UIImageView = .build { imageView in + imageView.contentMode = .scaleAspectFit + imageView.image = UIImage(named: StandardImageIdentifiers.Large.readerView)?.withRenderingMode(.alwaysTemplate) + } + var readingListLabel: UILabel = .build { label in + label.text = .ReaderPanelReadingListDescription + label.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .body, size: 16, weight: .light) + label.numberOfLines = 0 + } + var readingListImageView: UIImageView = .build { imageView in + imageView.contentMode = .scaleAspectFit + imageView.image = UIImage(named: StandardImageIdentifiers.Large.readingListAdd)?.withRenderingMode(.alwaysTemplate) + } + + // MARK: - Init + + required init?(coder: NSCoder) { + assertionFailure("This view is only supposed to be instantiated programmatically") + return nil + } + + init(windowUUID: WindowUUID) { + self.windowUUID = windowUUID + super.init(frame: .zero) + setup() + applyTheme() + } + + // MARK: - Setup + + private func setup() { + + translatesAutoresizingMaskIntoConstraints = false + + let emptyStateViewWrapper: UIView = .build { view in + view.addSubviews(self.welcomeLabel, + self.readerModeLabel, + self.readerModeImageView, + self.readingListLabel, + self.readingListImageView) + } + + addSubview(emptyStateViewWrapper) + + NSLayoutConstraint.activate([ + // title + welcomeLabel.topAnchor.constraint(equalTo: emptyStateViewWrapper.topAnchor), + welcomeLabel.leadingAnchor.constraint(equalTo: emptyStateViewWrapper.leadingAnchor), + welcomeLabel.trailingAnchor.constraint(equalTo: emptyStateViewWrapper.trailingAnchor), + + // first row + readerModeLabel.topAnchor.constraint(equalTo: welcomeLabel.bottomAnchor, constant: ReadingListPanelUX.WelcomeScreenPadding), + readerModeImageView.leadingAnchor.constraint(equalTo: welcomeLabel.leadingAnchor), + readerModeImageView.trailingAnchor.constraint(equalTo: readerModeLabel.leadingAnchor, constant: -ReadingListPanelUX.WelcomeScreenPadding), + + readerModeImageView.centerYAnchor.constraint(equalTo: readerModeLabel.centerYAnchor), + readerModeLabel.trailingAnchor.constraint(equalTo: welcomeLabel.trailingAnchor), + readerModeImageView.widthAnchor.constraint(equalToConstant: ReadingListPanelUX.WelcomeScreenItemImageWidth), + + // second row + readingListLabel.topAnchor.constraint(equalTo: readerModeLabel.bottomAnchor, constant: ReadingListPanelUX.WelcomeScreenPadding), + readingListImageView.leadingAnchor.constraint(equalTo: welcomeLabel.leadingAnchor), + readingListImageView.trailingAnchor.constraint(equalTo: readingListLabel.leadingAnchor, constant: -ReadingListPanelUX.WelcomeScreenPadding), + + readingListImageView.centerYAnchor.constraint(equalTo: readingListLabel.centerYAnchor), + readingListLabel.trailingAnchor.constraint(equalTo: welcomeLabel.trailingAnchor), + readingListImageView.widthAnchor.constraint(equalToConstant: ReadingListPanelUX.WelcomeScreenItemImageWidth), + + readingListLabel.bottomAnchor.constraint(equalTo: emptyStateViewWrapper.bottomAnchor).priority(.defaultLow), + + // overall positioning of emptyStateViewWrapper + emptyStateViewWrapper.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: ReadingListPanelUX.WelcomeScreenHorizontalMinPadding), + emptyStateViewWrapper.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -ReadingListPanelUX.WelcomeScreenHorizontalMinPadding), + emptyStateViewWrapper.widthAnchor.constraint(lessThanOrEqualToConstant: ReadingListPanelUX.WelcomeScreenMaxWidth), + + emptyStateViewWrapper.centerXAnchor.constraint(equalTo: centerXAnchor), + emptyStateViewWrapper.topAnchor.constraint(equalTo: topAnchor, constant: ReadingListPanelUX.WelcomeScreenTopPadding), + emptyStateViewWrapper.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + + welcomeLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) + readerModeLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) + readingListLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) + + listenForThemeChange(self) + } + + // MARK: - Themeable + + func applyTheme() { + welcomeLabel.textColor = themeManager.getCurrentTheme(for: windowUUID).colors.textPrimary + readerModeLabel.textColor = themeManager.getCurrentTheme(for: windowUUID).colors.textSecondary + readerModeImageView.tintColor = themeManager.getCurrentTheme(for: windowUUID).colors.textSecondary + readingListLabel.textColor = themeManager.getCurrentTheme(for: windowUUID).colors.textSecondary + readingListImageView.tintColor = themeManager.getCurrentTheme(for: windowUUID).colors.textSecondary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/FilterController.swift b/firefox-ios/Client/Ecosia/UI/FilterController.swift new file mode 100644 index 0000000000000..246b2d8e4d43e --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/FilterController.swift @@ -0,0 +1,69 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Ecosia +import UIKit +import Common + +private let items: [(AdultFilter, String)] = [ + (.strict, .localized(.strict)), + (.moderate, .localized(.moderate)), + (.off, .localized(.off))] + +final class FilterController: ThemedTableViewController { + + private let identifier = "filter" + static var current: String? { + items.first(where: { $0.0 == User.shared.adultFilter }).map { $0.1 } + } + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.title = .localized(.safeSearch) + navigationItem.largeTitleDisplayMode = .never + + tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true + tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + applyTheme() + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + items.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: identifier) ?? ThemedTableViewCell(style: .default, reuseIdentifier: identifier) + cell.textLabel!.text = items[cellForRowAt.row].1 + cell.accessoryType = User.shared.adultFilter == items[cellForRowAt.row].0 ? .checkmark : .none + return cell + } + + override func tableView(_: UITableView, didSelectRowAt: IndexPath) { + User.shared.adultFilter = items[didSelectRowAt.row].0 + tableView.reloadData() + } + + override func applyTheme() { + super.applyTheme() + let theme = themeManager.getCurrentTheme(for: windowUUID) + tableView.visibleCells.forEach { + ($0 as? Themeable)?.applyTheme() + ($0 as? ThemeApplicable)?.applyTheme(theme: theme) + } + + view.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + tableView.tintColor = theme.colors.ecosia.brandPrimary + tableView.separatorColor = theme.colors.ecosia.borderDecorative + tableView.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + } +} diff --git a/firefox-ios/Client/Ecosia/UI/Font/FoundersGroteskCondensed-Semibold.ttf b/firefox-ios/Client/Ecosia/UI/Font/FoundersGroteskCondensed-Semibold.ttf new file mode 100644 index 0000000000000..5857bbbfb3296 Binary files /dev/null and b/firefox-ios/Client/Ecosia/UI/Font/FoundersGroteskCondensed-Semibold.ttf differ diff --git a/firefox-ios/Client/Ecosia/UI/LaunchScreen/EcosiaLaunchScreen.xib b/firefox-ios/Client/Ecosia/UI/LaunchScreen/EcosiaLaunchScreen.xib new file mode 100644 index 0000000000000..0d90b9bc29c1a --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/LaunchScreen/EcosiaLaunchScreen.xib @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/firefox-ios/Client/Ecosia/UI/LaunchScreen/EcosiaLaunchScreenView.swift b/firefox-ios/Client/Ecosia/UI/LaunchScreen/EcosiaLaunchScreenView.swift new file mode 100644 index 0000000000000..9f384814a7e68 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/LaunchScreen/EcosiaLaunchScreenView.swift @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +/// LaunchScreen is the EcosiaLaunchScreen.xib we show at launch, but loaded programmatically +public class EcosiaLaunchScreenView: UIView { + private static let viewName = "EcosiaLaunchScreen" + + public class func fromNib() -> UIView { + Bundle.main.loadNibNamed(EcosiaLaunchScreenView.viewName, + owner: nil, + options: nil)![0] as! UIView + } +} diff --git a/firefox-ios/Client/Ecosia/UI/LoadingScreen.swift b/firefox-ios/Client/Ecosia/UI/LoadingScreen.swift new file mode 100644 index 0000000000000..3e5a54828f198 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/LoadingScreen.swift @@ -0,0 +1,140 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +final class LoadingScreen: UIViewController { + private weak var profile: Profile! + private weak var progress: UIProgressView! + private weak var referrals: Referrals! + private var referralCode: String? + + var themeManager: ThemeManager + let windowUUID: WindowUUID + let loadingGroup = DispatchGroup() + + required init?(coder: NSCoder) { nil } + init(profile: Profile, + referrals: Referrals, + windowUUID: WindowUUID, + themeManager: ThemeManager = AppContainer.shared.resolve(), + referralCode: String? = nil) { + self.profile = profile + self.referrals = referrals + self.windowUUID = windowUUID + self.themeManager = themeManager + self.referralCode = referralCode + super.init(nibName: nil, bundle: nil) + modalPresentationStyle = .fullScreen + modalTransitionStyle = .crossDissolve + } + + override func viewDidLoad() { + super.viewDidLoad() + let theme = themeManager.getCurrentTheme(for: windowUUID) + view.backgroundColor = theme.colors.ecosia.backgroundPrimary + + let logo = UIImageView(image: UIImage(named: "ecosiaLogoLaunch")) + logo.translatesAutoresizingMaskIntoConstraints = false + logo.clipsToBounds = true + logo.contentMode = .center + view.addSubview(logo) + + let progress = UIProgressView() + progress.translatesAutoresizingMaskIntoConstraints = false + progress.progressTintColor = theme.colors.ecosia.brandPrimary + view.addSubview(progress) + self.progress = progress + + let message = UILabel() + message.translatesAutoresizingMaskIntoConstraints = false + message.text = .localized(.sitTightWeAre) + message.font = .preferredFont(forTextStyle: .footnote) + message.numberOfLines = 0 + message.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + message.textColor = theme.colors.ecosia.textPrimary + message.textAlignment = .center + view.addSubview(message) + + logo.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + logo.bottomAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + + progress.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + progress.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 24).isActive = true + progress.widthAnchor.constraint(equalToConstant: 173).isActive = true + progress.heightAnchor.constraint(equalToConstant: 3).isActive = true + + message.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + message.topAnchor.constraint(equalTo: progress.bottomAnchor, constant: 25).isActive = true + message.widthAnchor.constraint(lessThanOrEqualToConstant: 280).isActive = true + + if let code = referralCode { + loadingGroup.enter() + claimReferral(code) + } + + loadingGroup.notify(queue: .main) { [weak self] in + self?.dismiss(animated: true) + } + } + + // MARK: Referrals + private func claimReferral(_ code: String) { + Task { [weak self] in + do { + try await self?.referrals.claim(referrer: code) + self?.loadingGroup.leave() + Analytics.shared.referral(action: .claim) + } catch { + self?.showReferralError(error as? Referrals.Error ?? .genericError) + } + User.shared.referrals.pendingClaim = nil + } + } + + private func showReferralError(_ error: Referrals.Error) { + guard !error.title.isEmpty else { return } + let alert = UIAlertController(title: error.title, + message: error.message, + preferredStyle: .alert) + alert.addAction(.init(title: .localized(.continueMessage), style: .cancel) { [weak self] _ in + self?.loadingGroup.leave() + }) + alert.addAction(.init(title: .localized(.retryMessage), style: .default) { [weak self] _ in + guard let code = self?.referralCode else { return } + self?.claimReferral(code) + }) + present(alert, animated: true) + } +} + +extension Referrals.Error { + var title: String { + switch self { + case .badRequest, .invalidCode, .genericError: + return .localized(.invalidReferralLink) + case .alreadyUsed: + return .localized(.linkAlreadyUsedTitle) + case .noConnection: + return .localized(.networkError) + case .notFound: + return "" + } + } + + var message: String { + switch self { + case .badRequest, .invalidCode, .genericError: + return .localized(.invalidReferralLinkMessage) + case .alreadyUsed: + return .localized(.linkAlreadyUsedMessage) + case .noConnection: + return .localized(.noConnectionMessage) + case .notFound: + return "" + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/MarketsController.swift b/firefox-ios/Client/Ecosia/UI/MarketsController.swift new file mode 100644 index 0000000000000..b6a721fad3cf4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/MarketsController.swift @@ -0,0 +1,81 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +final class Markets { + private(set) static var all: [Market] = { + return (try? JSONDecoder().decode([Market].self, from: Data(contentsOf: Bundle.ecosia.url(forResource: "markets", withExtension: "json")!)))?.sorted(by: { $0.displayName.compare($1.displayName) == .orderedAscending }) ?? [] + }() + + static var current: String? { + Markets.all.first { User.shared.marketCode == $0.value }.map { + $0.displayName + } + } +} + +extension Market { + var displayName: String { + let comps = value.rawValue.components(separatedBy: "-") + let country = Locale.current.localizedString(forRegionCode: comps.last ?? "") ?? .localized(label) + + if languageInLabel, let lan = comps.first, let language = Locale.current.localizedString(forLanguageCode: lan) { + return "\(country) (\(language))" + } else { + return country + } + } +} + +final class MarketsController: ThemedTableViewController { + private let identifier = "market" + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.title = .localized(.searchRegion) + navigationItem.largeTitleDisplayMode = .never + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + applyTheme() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Markets.all.firstIndex { User.shared.marketCode == $0.value }.map { + tableView.scrollToRow(at: .init(row: $0, section: 0), at: .middle, animated: true) + } + } + + override func tableView(_: UITableView, numberOfRowsInSection: Int) -> Int { + Markets.all.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: identifier) ?? ThemedTableViewCell(style: .default, reuseIdentifier: identifier) + + let market = Markets.all[cellForRowAt.row] + cell.textLabel?.text = market.displayName + cell.accessoryType = User.shared.marketCode == Markets.all[cellForRowAt.row].value ? .checkmark : .none + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt: IndexPath) { + guard Markets.all[didSelectRowAt.row].value != User.shared.marketCode else { return } + User.shared.marketCode = Markets.all[didSelectRowAt.row].value + tableView.reloadData() + Analytics.shared.navigationChangeMarket(User.shared.marketCode.rawValue) + } + + override func applyTheme() { + super.applyTheme() + let theme = themeManager.getCurrentTheme(for: windowUUID) + tableView.tintColor = theme.colors.ecosia.brandPrimary + view.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + } +} diff --git a/firefox-ios/Client/Ecosia/UI/MultiplyImpact/MultiplyImpact.swift b/firefox-ios/Client/Ecosia/UI/MultiplyImpact/MultiplyImpact.swift new file mode 100644 index 0000000000000..11f640d0a8353 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/MultiplyImpact/MultiplyImpact.swift @@ -0,0 +1,577 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import UniformTypeIdentifiers +import MobileCoreServices +import LinkPresentation +import Common +import Ecosia + +class MultiplyImpact: UIViewController, Themeable { + + // MARK: - UX + + private struct UX { + private init() {} + static let hardcodedDarkTextColor = EcosiaDarkTheme().colors.ecosia.textPrimary + static let defaultPadding: CGFloat = 16 + static let subtitleMargin: CGFloat = 8 + static let defaultCornerRadius: CGFloat = 10 + + struct Card { + private init() {} + static let distanceFromCardBottom: CGFloat = 32 + static let cardBottomMargin: CGFloat = 17 + static let cardIconTopMargin: CGFloat = 17 + static let cardTitleLeftMargin: CGFloat = 12 + static let cardTreeCountLeftMargin: CGFloat = 12 + static let cardTreeIconLeftMargin: CGFloat = 8 + } + + struct InviteFriendsFeature { + private init() {} + static let copyControlBorderWidth: CGFloat = 1 + static let copyLinkRightMargin: CGFloat = -10 + static let copyTextRightMargin: CGFloat = -12 + static let copyDividerLeftRightMargin: CGFloat = -10 + static let defaultTopMargin: CGFloat = 21 + static let cornerRadius: CGFloat = 22 + static let defaultHeight: CGFloat = 44 + } + + struct Flow { + private init() {} + static let flowTitleStackTopMargin: CGFloat = 36 + static let flowBackgroundBottomMargin: CGFloat = -20 + static let stackViewSpacing: CGFloat = 12 + } + } + + // MARK: - Properties + weak var delegate: SharedHomepageCellDelegate? + + private weak var subtitle: UILabel? + private weak var topBackground: UIView? + private weak var yourInvites: UILabel? + private lazy var referralImpactRowView: NTPImpactRowView = { + let view = NTPImpactRowView(info: referralInfo) + view.forceHideActionButton = true + view.position = (0, 1) + return view + }() + + var referralInfo: ClimateImpactInfo { + .referral(value: User.shared.referrals.count) + } + + private weak var sharingYourLink: UILabel? + private weak var sharing: UIView? + private(set) weak var copyControl: UIControl? + private weak var copyLink: UILabel? + private weak var copyText: UILabel? + private weak var copyDividerLeft: UIView? + private weak var copyDividerRight: UIView? + private weak var moreSharingMethods: UILabel? + private(set) weak var inviteButton: EcosiaPrimaryButton! + + private(set) weak var learnMoreButton: UIButton? + + private weak var flowTitle: UILabel? + private weak var flowBackground: UIView? + private weak var flowStack: UIStackView? + + private weak var firstStep: MultiplyImpactStep? + private weak var secondStep: MultiplyImpactStep? + private weak var thirdStep: MultiplyImpactStep? + private weak var fourthStep: MultiplyImpactStep? + + private weak var referrals: Referrals! + + // MARK: - Themeable Properties + + let windowUUID: WindowUUID + var currentWindowUUID: WindowUUID? { windowUUID } + var themeManager: ThemeManager + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + + // MARK: - Init + + required init?(coder: NSCoder) { nil } + init(referrals: Referrals, + windowUUID: WindowUUID, + themeManager: ThemeManager = AppContainer.shared.resolve()) { + self.referrals = referrals + self.windowUUID = windowUUID + self.themeManager = themeManager + super.init(nibName: nil, bundle: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + title = .localized(.growingTogether) + + let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped)) + navigationItem.rightBarButtonItem = done + + let scroll = UIScrollView() + scroll.translatesAutoresizingMaskIntoConstraints = false + scroll.contentInsetAdjustmentBehavior = .scrollableAxes + view.addSubview(scroll) + + let content = UIView() + content.translatesAutoresizingMaskIntoConstraints = false + scroll.addSubview(content) + + let topBackground = UIView() + topBackground.translatesAutoresizingMaskIntoConstraints = false + content.addSubview(topBackground) + self.topBackground = topBackground + + let subtitle = UILabel() + subtitle.translatesAutoresizingMaskIntoConstraints = false + subtitle.numberOfLines = 0 + subtitle.text = .localized(.inviteYourFriendsToCheck) + subtitle.font = .preferredFont(forTextStyle: .body) + subtitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + subtitle.adjustsFontForContentSizeCategory = true + content.addSubview(subtitle) + self.subtitle = subtitle + + let yourInvites = UILabel() + yourInvites.text = .localized(.yourInvites) + content.addSubview(yourInvites) + self.yourInvites = yourInvites + + content.addSubview(referralImpactRowView) + + let sharingYourLink = UILabel() + sharingYourLink.text = .localized(.sharingYourLink) + content.addSubview(sharingYourLink) + self.sharingYourLink = sharingYourLink + + let sharing = UIView() + self.sharing = sharing + + let copyControl = UIControl() + copyControl.layer.cornerRadius = UX.defaultCornerRadius + copyControl.layer.borderWidth = UX.InviteFriendsFeature.copyControlBorderWidth + copyControl.addTarget(self, action: #selector(copyCode), for: .touchUpInside) + copyControl.addTarget(self, action: #selector(hover), for: .touchDown) + copyControl.addTarget(self, action: #selector(unhover), for: .touchUpInside) + copyControl.addTarget(self, action: #selector(unhover), for: .touchUpOutside) + copyControl.addTarget(self, action: #selector(unhover), for: .touchCancel) + self.copyControl = copyControl + + let copyLink = UILabel() + copyLink.translatesAutoresizingMaskIntoConstraints = false + copyLink.adjustsFontForContentSizeCategory = true + copyLink.font = .preferredFont(forTextStyle: .body) + copyLink.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + copyLink.numberOfLines = 1 + copyControl.addSubview(copyLink) + self.copyLink = copyLink + + let copyText = UILabel() + copyText.translatesAutoresizingMaskIntoConstraints = false + copyText.text = .localized(.copy) + copyText.font = .preferredFont(forTextStyle: .body) + copyText.adjustsFontForContentSizeCategory = true + copyControl.addSubview(copyText) + self.copyText = copyText + + let copyDividerLeft = UIView() + self.copyDividerLeft = copyDividerLeft + + let copyDividerRight = UIView() + self.copyDividerRight = copyDividerRight + + let moreSharingMethods = UILabel() + moreSharingMethods.translatesAutoresizingMaskIntoConstraints = false + moreSharingMethods.adjustsFontForContentSizeCategory = true + moreSharingMethods.font = .systemFont(ofSize: UIFont.preferredFont(forTextStyle: .footnote).pointSize, weight: .semibold) + moreSharingMethods.text = .localized(.moreSharingMethods) + sharing.addSubview(moreSharingMethods) + self.moreSharingMethods = moreSharingMethods + + let inviteFriends = EcosiaPrimaryButton(windowUUID: windowUUID) + inviteFriends.setTitle(.localized(.inviteFriends), for: []) + inviteFriends.titleLabel!.font = .preferredFont(forTextStyle: .callout) + inviteFriends.titleLabel!.adjustsFontForContentSizeCategory = true + inviteFriends.layer.cornerRadius = UX.InviteFriendsFeature.cornerRadius + inviteFriends.addTarget(self, action: #selector(self.inviteFriends), for: .touchUpInside) + self.inviteButton = inviteFriends + + let flowTitleStack = UIStackView() + flowTitleStack.translatesAutoresizingMaskIntoConstraints = false + flowTitleStack.alignment = .fill + flowTitleStack.axis = .horizontal + content.addSubview(flowTitleStack) + + let flowTitle = UILabel() + flowTitle.text = .localized(.howItWorks) + flowTitleStack.addArrangedSubview(flowTitle) + self.flowTitle = flowTitle + + let learnMoreButton = UIButton(type: .system) + learnMoreButton.addTarget(self, action: #selector(learnMore), for: .primaryActionTriggered) + learnMoreButton.translatesAutoresizingMaskIntoConstraints = false + learnMoreButton.setTitle(.localized(.learnMore), for: .normal) + learnMoreButton.titleLabel?.font = .preferredFont(forTextStyle: .callout) + learnMoreButton.titleLabel?.adjustsFontForContentSizeCategory = true + learnMoreButton.setContentHuggingPriority(.required, for: .horizontal) + flowTitleStack.addArrangedSubview(learnMoreButton) + self.learnMoreButton = learnMoreButton + + let flowBackground = UIView() + self.flowBackground = flowBackground + + let flowStack = UIStackView() + flowStack.translatesAutoresizingMaskIntoConstraints = false + flowStack.axis = .vertical + flowStack.alignment = .leading + flowStack.spacing = UX.Flow.stackViewSpacing + flowBackground.addSubview(flowStack) + + let firstStep = MultiplyImpactStep(title: .localized(.inviteYourFriends), subtitle: .localized(.sendAnInvite), image: "paperplane") + flowStack.addArrangedSubview(firstStep) + self.firstStep = firstStep + + let secondStep = MultiplyImpactStep(title: .localized(.theyDownloadTheApp), subtitle: .localized(.viaTheAppStore), image: "libraryDownloads") + flowStack.addArrangedSubview(secondStep) + self.secondStep = secondStep + + let thirdStep = MultiplyImpactStep(title: .localized(.theyOpenYourInviteLink), subtitle: .localized(.yourFriendClicks), image: "menu-Copy-Link") + flowStack.addArrangedSubview(thirdStep) + self.thirdStep = thirdStep + + let fourthStep = MultiplyImpactStep(title: .localized(.eachOfYouHelpsPlant), subtitle: .localized(.whenAFriendUses), image: "myImpact") + flowStack.addArrangedSubview(fourthStep) + self.fourthStep = fourthStep + + [yourInvites, sharingYourLink, flowTitle].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.font = .preferredFont(forTextStyle: .headline).bold() + $0.adjustsFontForContentSizeCategory = true + $0.setContentHuggingPriority(.defaultLow, for: .horizontal) + } + + [sharing, flowBackground].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.layer.cornerRadius = UX.defaultCornerRadius + content.addSubview($0) + + $0.leftAnchor.constraint(equalTo: content.leftAnchor, constant: UX.defaultPadding).isActive = true + $0.rightAnchor.constraint(equalTo: content.rightAnchor, constant: -UX.defaultPadding).isActive = true + } + + [copyControl, inviteFriends].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + sharing.addSubview($0) + + $0.leftAnchor.constraint(equalTo: sharing.leftAnchor, constant: UX.defaultPadding).isActive = true + $0.rightAnchor.constraint(equalTo: sharing.rightAnchor, constant: -UX.defaultPadding).isActive = true + $0.heightAnchor.constraint(equalToConstant: UX.InviteFriendsFeature.defaultHeight).isActive = true + } + + [copyDividerLeft, copyDividerRight].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.isUserInteractionEnabled = false + sharing.addSubview($0) + + $0.topAnchor.constraint(equalTo: copyControl.bottomAnchor, constant: UX.InviteFriendsFeature.defaultTopMargin).isActive = true + $0.heightAnchor.constraint(equalToConstant: 1).isActive = true + } + + scroll.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + scroll.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true + scroll.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true + scroll.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + + content.topAnchor.constraint(equalTo: scroll.contentLayoutGuide.topAnchor).isActive = true + content.leftAnchor.constraint(equalTo: scroll.frameLayoutGuide.leftAnchor).isActive = true + content.rightAnchor.constraint(equalTo: scroll.frameLayoutGuide.rightAnchor).isActive = true + content.bottomAnchor.constraint(equalTo: scroll.contentLayoutGuide.bottomAnchor).isActive = true + + subtitle.topAnchor.constraint(equalTo: content.topAnchor, constant: UX.subtitleMargin).isActive = true + subtitle.leftAnchor.constraint(equalTo: content.leftAnchor, constant: UX.defaultPadding).isActive = true + subtitle.rightAnchor.constraint(lessThanOrEqualTo: content.rightAnchor, constant: -UX.defaultPadding).isActive = true + + topBackground.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true + topBackground.trailingAnchor.constraint(equalTo: content.trailingAnchor).isActive = true + topBackground.topAnchor.constraint(equalTo: content.topAnchor).isActive = true + topBackground.bottomAnchor.constraint(equalTo: subtitle.bottomAnchor, constant: UX.subtitleMargin).isActive = true + + yourInvites.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: UX.defaultPadding).isActive = true + yourInvites.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -UX.defaultPadding).isActive = true + yourInvites.topAnchor.constraint(equalTo: topBackground.bottomAnchor, constant: UX.defaultPadding).isActive = true + + referralImpactRowView.leftAnchor.constraint(equalTo: content.leftAnchor, constant: UX.defaultPadding).isActive = true + referralImpactRowView.rightAnchor.constraint(equalTo: content.rightAnchor, constant: -UX.defaultPadding).isActive = true + referralImpactRowView.topAnchor.constraint(equalTo: yourInvites.bottomAnchor, constant: UX.defaultPadding).isActive = true + + sharingYourLink.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: UX.defaultPadding).isActive = true + sharingYourLink.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -UX.defaultPadding).isActive = true + sharingYourLink.topAnchor.constraint(equalTo: referralImpactRowView.bottomAnchor, constant: UX.Card.distanceFromCardBottom).isActive = true + + sharing.topAnchor.constraint(equalTo: sharingYourLink.bottomAnchor, constant: UX.defaultPadding).isActive = true + + copyControl.topAnchor.constraint(equalTo: sharing.topAnchor, constant: UX.defaultPadding).isActive = true + + copyLink.centerYAnchor.constraint(equalTo: copyControl.centerYAnchor).isActive = true + copyLink.leftAnchor.constraint(equalTo: copyControl.leftAnchor, constant: UX.defaultPadding).isActive = true + copyLink.rightAnchor.constraint(lessThanOrEqualTo: copyText.leftAnchor, constant: UX.InviteFriendsFeature.copyLinkRightMargin).isActive = true + + copyText.centerYAnchor.constraint(equalTo: copyControl.centerYAnchor).isActive = true + copyText.rightAnchor.constraint(equalTo: copyControl.rightAnchor, constant: UX.InviteFriendsFeature.copyTextRightMargin).isActive = true + + copyDividerLeft.leftAnchor.constraint(equalTo: copyControl.leftAnchor).isActive = true + copyDividerLeft.rightAnchor.constraint(equalTo: moreSharingMethods.leftAnchor, constant: UX.InviteFriendsFeature.copyDividerLeftRightMargin).isActive = true + copyDividerRight.rightAnchor.constraint(equalTo: copyControl.rightAnchor).isActive = true + copyDividerRight.leftAnchor.constraint(equalTo: moreSharingMethods.rightAnchor, constant: UX.InviteFriendsFeature.copyDividerLeftRightMargin).isActive = true + + moreSharingMethods.centerXAnchor.constraint(equalTo: sharing.centerXAnchor).isActive = true + moreSharingMethods.centerYAnchor.constraint(equalTo: copyDividerLeft.centerYAnchor).isActive = true + + inviteFriends.topAnchor.constraint(equalTo: copyDividerLeft.bottomAnchor, constant: UX.InviteFriendsFeature.defaultTopMargin).isActive = true + inviteFriends.bottomAnchor.constraint(equalTo: sharing.bottomAnchor, constant: -UX.defaultPadding).isActive = true + + flowTitleStack.leftAnchor.constraint(equalTo: content.leftAnchor, constant: UX.defaultPadding).isActive = true + flowTitleStack.rightAnchor.constraint(equalTo: content.rightAnchor, constant: -UX.defaultPadding).isActive = true + flowTitleStack.topAnchor.constraint(equalTo: sharing.bottomAnchor, constant: UX.Flow.flowTitleStackTopMargin).isActive = true + + flowBackground.topAnchor.constraint(equalTo: flowTitleStack.bottomAnchor, constant: UX.defaultPadding).isActive = true + flowBackground.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: UX.Flow.flowBackgroundBottomMargin).isActive = true + + flowStack.leftAnchor.constraint(equalTo: flowBackground.leftAnchor, constant: UX.defaultPadding).isActive = true + flowStack.rightAnchor.constraint(equalTo: flowBackground.rightAnchor, constant: -UX.defaultPadding).isActive = true + flowStack.topAnchor.constraint(equalTo: flowBackground.topAnchor, constant: UX.defaultPadding).isActive = true + flowStack.bottomAnchor.constraint(equalTo: flowBackground.bottomAnchor, constant: -UX.defaultPadding).isActive = true + + applyTheme() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + updateInviteLink() + refreshReferrals() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Analytics.shared.referral(action: .view, label: .inviteScreen) + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + applyTheme() + } + + func applyTheme() { + let theme = themeManager.getCurrentTheme(for: windowUUID) + + view.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + inviteButton.backgroundColor = theme.colors.ecosia.buttonBackgroundFeatured + inviteButton.setTitleColor(theme.colors.ecosia.buttonContentSecondaryStatic, for: .normal) + learnMoreButton?.setTitleColor(theme.colors.ecosia.brandPrimary, for: .normal) + topBackground?.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + subtitle?.textColor = theme.colors.ecosia.textSecondary + copyControl?.backgroundColor = theme.colors.ecosia.backgroundSecondary + copyControl?.layer.borderColor = theme.colors.ecosia.borderDecorative.cgColor + moreSharingMethods?.textColor = theme.colors.ecosia.textSecondary + copyText?.textColor = theme.colors.ecosia.brandPrimary + + [yourInvites, sharingYourLink, flowTitle, copyLink].forEach { + $0?.textColor = theme.colors.ecosia.textPrimary + } + + [sharing, flowBackground].forEach { + $0?.backgroundColor = theme.colors.ecosia.backgroundElevation1 + } + + [firstStep, secondStep, thirdStep, fourthStep].forEach { + $0?.applyTheme(theme: theme) + } + + [copyDividerLeft, copyDividerRight].forEach { + $0?.backgroundColor = theme.colors.ecosia.borderDecorative + } + + referralImpactRowView.customBackgroundColor = theme.colors.ecosia.backgroundElevation1 + referralImpactRowView.applyTheme(theme: theme) + + updateBarAppearance(theme: theme) + } + + private func updateBarAppearance(theme: Theme) { + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.largeTitleTextAttributes = [.foregroundColor: theme.colors.ecosia.textPrimary] + appearance.titleTextAttributes = [.foregroundColor: theme.colors.ecosia.textPrimary] + appearance.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + appearance.shadowColor = nil + navigationItem.standardAppearance = appearance + navigationItem.scrollEdgeAppearance = appearance + navigationController?.navigationBar.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + navigationController?.navigationBar.tintColor = theme.colors.ecosia.buttonContentSecondary + } + + private func updateInviteLink() { + copyLink?.text = inviteLink ?? inviteLinkPlaceholder + } + + private func refreshReferrals() { + Task { [weak self] in + do { + try await self?.referrals.refresh(force: true, createCode: true) + guard let self = self else { return } + self.updateInviteLink() + self.referralImpactRowView.info = self.referralInfo + } catch { + self?.showRefreshReferralsError(error as? Referrals.Error ?? .genericError) + } + } + } + + @objc private func learnMore() { + delegate?.openLink(url: EcosiaEnvironment.current.urlProvider.referHelp) + dismiss(animated: true) + Analytics.shared.referral(action: .click, label: .learnMore) + } + + @objc private func hover() { + copyControl?.alpha = 0.3 + } + + @objc private func unhover() { + copyControl?.alpha = 1 + } + + @objc private func copyCode() { + unhover() + guard let message = inviteMessage else { return } + UIPasteboard.general.setValue(message, forPasteboardType: UTType.plainText.identifier) + copyText?.text = .localized(.copied) + Analytics.shared.referral(action: .click, label: .linkCopying) + } + + @objc private func inviteFriends() { + guard let message = inviteMessage else { + Task { [weak self] in + do { + try await self?.referrals.refresh(createCode: true) + guard let self = self else { return } + self.share(message: self.inviteMessage!) + } catch { + self?.showInviteFriendsError(error as? Referrals.Error ?? .genericError) + } + } + return + } + share(message: message) + } + + private func share(message: String) { + let share = UIActivityViewController(activityItems: [SharingMessage(message: message)], + applicationActivities: nil) + share.popoverPresentationController?.sourceView = inviteButton + share.completionWithItemsHandler = { _, completed, _, _ in + if completed { + Analytics.shared.referral(action: .send, label: .invite) + } + } + present(share, animated: true) + + Analytics.shared.referral(action: .click, label: .invite) + } + + private func showInviteFriendsError(_ error: Referrals.Error) { + let alert = UIAlertController(title: error.title, + message: error.message, + preferredStyle: .alert) + alert.addAction(.init(title: .localized(.continueMessage), style: .cancel)) + alert.addAction(.init(title: .localized(.retryMessage), style: .default) { [weak self] _ in + self?.inviteFriends() + }) + present(alert, animated: true) + } + + private func showRefreshReferralsError(_ error: Referrals.Error) { + let alert = UIAlertController(title: error.title, + message: error.message, + preferredStyle: .alert) + alert.addAction(.init(title: .localized(.continueMessage), style: .cancel)) + alert.addAction(.init(title: .localized(.retryMessage), style: .default) { [weak self] _ in + self?.refreshReferrals() + }) + present(alert, animated: true) + } + + @objc private func doneTapped() { + dismiss(animated: true) + } + + private var inviteMessage: String? { + guard let inviteDeepLink, let inviteDeepLinkUrl = URL(string: inviteDeepLink) else { return nil } + guard let inviteLink else { return nil } + + return """ +\(String(format: .localized(.messageMentioningActiveUsers), activeUsers)) + +\(inviteLink) + +\(String.localized(.tapLinkToConfirm)) + +\(inviteDeepLinkUrl.absoluteString) +""" + } + + private let inviteLinkPlaceholder = "-" + + private var inviteLink: String? { + guard let code = User.shared.referrals.code else { return nil } + return "\(Referrals.sharingLinkRoot)\(code)" + } + + private var inviteDeepLink: String? { + guard let code = User.shared.referrals.code else { return nil } + return "\(Referrals.deepLinkPath)\(code)" + } + + // MARK: Number formatting + private var activeUsers: String { + let activeUsers = Int(Statistics.shared.activeUsers) + let oneMillion = 1000000 + let million = activeUsers / oneMillion + return "\(million)" + } +} + +private final class SharingMessage: NSObject, UIActivityItemSource { + private let message: String + + init(message: String) { + self.message = message + super.init() + } + + func activityViewControllerPlaceholderItem(_: UIActivityViewController) -> Any { + String() + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType: UIActivity.ActivityType?) -> Any? { + message + } + + func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType: UIActivity.ActivityType?) -> String { + .localized(.plantTreesWithMe) + } + + func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? { + let metadata = LPLinkMetadata() + metadata.title = .localized(.plantTreesWithMe) + return metadata + } +} diff --git a/firefox-ios/Client/Ecosia/UI/MultiplyImpact/MultiplyImpactStep.swift b/firefox-ios/Client/Ecosia/UI/MultiplyImpact/MultiplyImpactStep.swift new file mode 100644 index 0000000000000..d5c954d5a5f70 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/MultiplyImpact/MultiplyImpactStep.swift @@ -0,0 +1,69 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class MultiplyImpactStep: UIView, ThemeApplicable { + private weak var indicator: UIImageView? + private weak var titleLabel: UILabel? + private weak var subtitleLabel: UILabel? + + // MARK: - Init + + required init?(coder: NSCoder) { nil } + init(title: String, subtitle: String, image: String) { + super.init(frame: .zero) + translatesAutoresizingMaskIntoConstraints = false + isUserInteractionEnabled = false + + let indicator = UIImageView(image: .init(named: image)?.withRenderingMode(.alwaysTemplate)) + indicator.translatesAutoresizingMaskIntoConstraints = false + indicator.isUserInteractionEnabled = false + indicator.clipsToBounds = true + addSubview(indicator) + self.indicator = indicator + + let titleLabel = UILabel() + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.numberOfLines = 0 + titleLabel.text = title + titleLabel.font = .preferredFont(forTextStyle: .subheadline).bold() + titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + titleLabel.adjustsFontForContentSizeCategory = true + addSubview(titleLabel) + self.titleLabel = titleLabel + + let subtitleLabel = UILabel() + subtitleLabel.translatesAutoresizingMaskIntoConstraints = false + subtitleLabel.numberOfLines = 0 + subtitleLabel.text = subtitle + subtitleLabel.font = .preferredFont(forTextStyle: .subheadline) + subtitleLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + subtitleLabel.adjustsFontForContentSizeCategory = true + addSubview(subtitleLabel) + self.subtitleLabel = subtitleLabel + + bottomAnchor.constraint(equalTo: subtitleLabel.bottomAnchor).isActive = true + + indicator.topAnchor.constraint(equalTo: topAnchor).isActive = true + indicator.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + indicator.widthAnchor.constraint(equalToConstant: 24).isActive = true + indicator.heightAnchor.constraint(equalTo: indicator.widthAnchor).isActive = true + + titleLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true + titleLabel.leftAnchor.constraint(equalTo: indicator.rightAnchor, constant: 12).isActive = true + titleLabel.rightAnchor.constraint(lessThanOrEqualTo: rightAnchor, constant: -12).isActive = true + + subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4).isActive = true + subtitleLabel.leftAnchor.constraint(equalTo: indicator.rightAnchor, constant: 12).isActive = true + subtitleLabel.rightAnchor.constraint(lessThanOrEqualTo: rightAnchor, constant: -12).isActive = true + } + + func applyTheme(theme: Theme) { + indicator?.tintColor = theme.colors.ecosia.brandPrimary + titleLabel?.textColor = theme.colors.ecosia.textPrimary + subtitleLabel?.textColor = theme.colors.ecosia.textSecondary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/CircleButton.swift b/firefox-ios/Client/Ecosia/UI/NTP/CircleButton.swift new file mode 100644 index 0000000000000..99ee84263f3d4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/CircleButton.swift @@ -0,0 +1,84 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Shared +import Common +import Ecosia + +class CircleButton: ToolbarButton { + enum Config { + case search + case newTab + + var image: String { + switch self { + case .search: return "searchUrl" + case .newTab: return "nav-add" + } + } + var shouldHideCircle: Bool { + switch self { + case .search: return false + case .newTab: return true + } + } + var accessibilityLabel: String { + switch self { + case .search: return .TabToolbarSearchAccessibilityLabel + case .newTab: return .TabTrayButtonNewTabAccessibilityLabel + } + } + } + + let circle = UIView() + var config: Config = .search { + didSet { + setup() + } + } + private var margin: CGFloat = 8 + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + convenience init(config: Config, margin: CGFloat = 8) { + self.init(frame: .zero) + self.config = config + self.margin = margin + setup() + } + + private func setup() { + setImage(.templateImageNamed(config.image), for: .normal) + circle.isUserInteractionEnabled = false + addSubview(circle) + sendSubviewToBack(circle) + accessibilityLabel = config.accessibilityLabel + accessibilityIdentifier = EcosiaAccessibilityIdentifiers.TabToolbar.circleButton + } + + override func layoutSubviews() { + super.layoutSubviews() + let height = bounds.height - margin + circle.bounds = .init(size: .init(width: height, height: height)) + circle.layer.cornerRadius = circle.bounds.height / 2 + circle.center = .init(x: bounds.width/2, y: bounds.height/2) + circle.isHidden = config.shouldHideCircle + } + + override func applyTheme(theme: Theme) { + circle.backgroundColor = theme.colors.ecosia.backgroundTertiary + tintColor = config.shouldHideCircle ? theme.colors.ecosia.textPrimary : theme.colors.ecosia.buttonBackgroundPrimary + selectedTintColor = theme.colors.ecosia.buttonBackgroundPrimaryActive + unselectedTintColor = tintColor + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Customization/CustomizableNTPSettingConfig.swift b/firefox-ios/Client/Ecosia/UI/NTP/Customization/CustomizableNTPSettingConfig.swift new file mode 100644 index 0000000000000..0b7411e798797 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Customization/CustomizableNTPSettingConfig.swift @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia + +enum CustomizableNTPSettingConfig: CaseIterable { + case topSites + case climateImpact + case ecosiaNews + + var localizedTitleKey: String.Key { + switch self { + case .topSites: return .topSites + case .climateImpact: return .climateImpact + case .ecosiaNews: return .ecosiaNews + } + } + + var persistedFlag: Bool { + get { + switch self { + case .topSites: return User.shared.showTopSites + case .climateImpact: return User.shared.showClimateImpact + case .ecosiaNews: return User.shared.showEcosiaNews + } + } + set { + switch self { + case .topSites: User.shared.showTopSites = newValue + case .climateImpact: User.shared.showClimateImpact = newValue + case .ecosiaNews: User.shared.showEcosiaNews = newValue + } + } + } + + var analyticsLabel: Analytics.Label.NTP { + switch self { + case .topSites: return .topSites + case .climateImpact: return .impact + case .ecosiaNews: return .news + } + } + + var accessibilityIdentifierPrefix: String { + switch self { + case .topSites: "top_sites" + case .climateImpact: "climate_impact" + case .ecosiaNews: "ecosia_news" + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Customization/NTPCustomizationCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/Customization/NTPCustomizationCell.swift new file mode 100644 index 0000000000000..f95015e655867 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Customization/NTPCustomizationCell.swift @@ -0,0 +1,74 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +final class NTPCustomizationCell: UICollectionViewCell, ThemeApplicable, ReusableCell { + struct UX { + static let buttonHeight: CGFloat = 40 + static let horizontalInset: CGFloat = 16 + static let verticalInset: CGFloat = 12 + static let imageTitlePadding: CGFloat = 4 + } + + weak var delegate: NTPCustomizationCellDelegate? + + private lazy var button: UIButton = { + let button = UIButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle(.localized(.customizeHomepage), for: .normal) + button.titleLabel?.font = .preferredFont(forTextStyle: .callout) + button.titleLabel?.adjustsFontSizeToFitWidth = true + button.setImage(.init(named: StandardImageIdentifiers.Large.settings)?.withRenderingMode(.alwaysTemplate), for: .normal) + button.setImage(.init(named: StandardImageIdentifiers.Large.settings)?.withRenderingMode(.alwaysTemplate), for: .highlighted) + button.imageView?.contentMode = .scaleAspectFit + button.setInsets(forContentPadding: .init(top: UX.verticalInset, left: UX.horizontalInset, bottom: UX.verticalInset, right: UX.horizontalInset), + imageTitlePadding: UX.imageTitlePadding) + button.addTarget(self, action: #selector(touchButtonAction), for: .touchUpInside) + button.clipsToBounds = true + return button + }() + + // MARK: - Init + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + override func layoutSubviews() { + super.layoutSubviews() + button.layer.cornerRadius = button.frame.height/2 + } + + private func setup() { + isAccessibilityElement = true + accessibilityIdentifier = "customize_homepage" + + contentView.addSubview(button) + + NSLayoutConstraint.activate([ + button.heightAnchor.constraint(equalToConstant: UX.buttonHeight), + button.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + button.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) + ]) + } + + func applyTheme(theme: Theme) { + button.imageView?.tintColor = theme.colors.ecosia.buttonContentSecondary + button.setTitleColor(theme.colors.ecosia.buttonContentSecondary, for: .normal) + button.setBackgroundColor(theme.colors.ecosia.buttonBackgroundSecondary, forState: .normal) + button.setBackgroundColor(theme.colors.ecosia.buttonBackgroundSecondaryActive, forState: .highlighted) + } + + @objc func touchButtonAction() { + delegate?.openNTPCustomizationSettings() + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Customization/NTPCustomizationCellViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/Customization/NTPCustomizationCellViewModel.swift new file mode 100644 index 0000000000000..f4c21918ee87a --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Customization/NTPCustomizationCellViewModel.swift @@ -0,0 +1,63 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +protocol NTPCustomizationCellDelegate: AnyObject { + func openNTPCustomizationSettings() +} + +final class NTPCustomizationCellViewModel { + weak var delegate: NTPCustomizationCellDelegate? + var theme: Theme + + init(delegate: NTPCustomizationCellDelegate? = nil, theme: Theme) { + self.delegate = delegate + self.theme = theme + } +} + +extension NTPCustomizationCellViewModel: HomepageViewModelProtocol { + + func setTheme(theme: Theme) { + self.theme = theme + } + + var sectionType: HomepageSectionType { .ntpCustomization } + + var headerViewModel: LabelButtonHeaderViewModel { .emptyHeader } + + var isEnabled: Bool { true } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem( + layoutSize: .init(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(NTPCustomizationCell.UX.buttonHeight)) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: .init(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(NTPCustomizationCell.UX.buttonHeight)), + subitem: item, + count: 1 + ) + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = sectionType.sectionInsets(traitCollection) + return section + } + + func numberOfItemsInSection() -> Int { 1 } +} + +extension NTPCustomizationCellViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = cell as? NTPCustomizationCell else { + return UICollectionViewCell() + } + cell.delegate = delegate + cell.applyTheme(theme: theme) + return cell + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/DefaultBrowser.swift b/firefox-ios/Client/Ecosia/UI/NTP/DefaultBrowser.swift new file mode 100644 index 0000000000000..fcc12b59033ca --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/DefaultBrowser.swift @@ -0,0 +1,267 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +@available(iOS 14, *) +protocol DefaultBrowserDelegate: AnyObject { + func defaultBrowserDidShow(_ defaultBrowser: DefaultBrowser) +} + +@available(iOS 14, *) +final class DefaultBrowser: UIViewController, Themeable { + + /// The minimum amount of searches required to show the Default Browser + static var minPromoSearches = 50 + + weak var content: UIView! + weak var image: UIImageView! + weak var waves: UIImageView! + weak var headline: UILabel! + weak var text1: UILabel! + weak var text2: UILabel! + weak var arrow1: UIImageView! + weak var arrow2: UIImageView! + weak var cta: UIButton! + weak var skip: UIButton! + weak var delegate: DefaultBrowserDelegate? + + // MARK: - Themeable Properties + + let windowUUID: WindowUUID + var currentWindowUUID: WindowUUID? { windowUUID } + var themeManager: ThemeManager { AppContainer.shared.resolve() } + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + + init(windowUUID: WindowUUID, delegate: DefaultBrowserDelegate) { + self.windowUUID = windowUUID + super.init(nibName: nil, bundle: nil) + self.delegate = delegate + if traitCollection.userInterfaceIdiom == .pad { + modalPresentationStyle = .formSheet + preferredContentSize = .init(width: 544, height: 600) + } else { + modalPresentationCapturesStatusBarAppearance = true + } + } + + required init?(coder: NSCoder) { nil } + + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + traitCollection.userInterfaceIdiom == .pad ? .all : .portrait + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + .lightContent + } + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + applyTheme() + + listenForThemeChange(self.view) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + Analytics.shared.defaultBrowser(.view) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + modalTransitionStyle = .crossDissolve + self.delegate?.defaultBrowserDidShow(self) + } + + private func setupViews() { + let content = UIView() + content.translatesAutoresizingMaskIntoConstraints = false + content.layer.cornerRadius = 10 + content.clipsToBounds = true + view.addSubview(content) + self.content = content + + content.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + content.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + content.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + content.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor).isActive = true + + let contentHeight = content.heightAnchor.constraint(equalToConstant: 300) + contentHeight.priority = .defaultHigh + contentHeight.isActive = true + + let image = UIImageView(image: .init(named: "defaultBrowser")) + image.contentMode = .scaleAspectFill + image.clipsToBounds = true + image.translatesAutoresizingMaskIntoConstraints = false + content.addSubview(image) + self.image = image + + image.topAnchor.constraint(equalTo: content.topAnchor).isActive = true + image.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true + image.trailingAnchor.constraint(equalTo: content.trailingAnchor).isActive = true + image.setContentCompressionResistancePriority(.required, for: .vertical) + image.setContentHuggingPriority(.required, for: .vertical) + + let forest = UIImageView(image: .init(named: "forestIcons")) + forest.translatesAutoresizingMaskIntoConstraints = false + forest.contentMode = .bottom + content.addSubview(forest) + forest.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + forest.bottomAnchor.constraint(equalTo: image.bottomAnchor).isActive = true + + if view.traitCollection.userInterfaceIdiom == .pad { + forest.widthAnchor.constraint(equalToConstant: 544).isActive = true + forest.heightAnchor.constraint(equalToConstant: 135).isActive = true + forest.contentMode = .scaleAspectFit + } + + let waves = UIImageView(image: .init(named: "waves")) + waves.translatesAutoresizingMaskIntoConstraints = false + waves.contentMode = .scaleToFill + content.addSubview(waves) + self.waves = waves + + waves.bottomAnchor.constraint(equalTo: image.bottomAnchor).isActive = true + waves.heightAnchor.constraint(equalToConstant: 34).isActive = true + waves.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true + waves.trailingAnchor.constraint(equalTo: content.trailingAnchor).isActive = true + + let headline = UILabel() + headline.text = .localized(.makeEcosiaYourDefaultBrowser) + headline.translatesAutoresizingMaskIntoConstraints = false + headline.font = .preferredFont(forTextStyle: .title3).bold() + headline.adjustsFontForContentSizeCategory = true + headline.numberOfLines = 0 + headline.setContentCompressionResistancePriority(.required, for: .vertical) + content.addSubview(headline) + self.headline = headline + + headline.topAnchor.constraint(equalTo: waves.bottomAnchor, constant: 16).isActive = true + headline.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 16).isActive = true + headline.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -16).isActive = true + let headlineHeight = headline.heightAnchor.constraint(greaterThanOrEqualToConstant: 100) + headlineHeight.priority = .defaultLow + headlineHeight.isActive = true + + let labelStack = UIStackView() + labelStack.translatesAutoresizingMaskIntoConstraints = false + labelStack.axis = .vertical + labelStack.spacing = 4 + view.addSubview(labelStack) + + labelStack.leadingAnchor.constraint(equalTo: headline.leadingAnchor).isActive = true + labelStack.trailingAnchor.constraint(equalTo: headline.trailingAnchor).isActive = true + labelStack.topAnchor.constraint(equalTo: headline.bottomAnchor, constant: 16).isActive = true + + let line1 = UIStackView() + line1.spacing = 10 + line1.axis = .horizontal + labelStack.addArrangedSubview(line1) + + let arrow1 = UIImageView(image: .init(systemName: "checkmark")) + arrow1.contentMode = .scaleAspectFit + arrow1.widthAnchor.constraint(equalToConstant: 16).isActive = true + line1.addArrangedSubview(arrow1) + self.arrow1 = arrow1 + + let text1 = UILabel() + text1.text = .localized(.openAllLinksAutomatically) + text1.translatesAutoresizingMaskIntoConstraints = false + text1.font = .preferredFont(forTextStyle: .subheadline) + text1.adjustsFontForContentSizeCategory = true + text1.numberOfLines = 0 + text1.setContentCompressionResistancePriority(.required, for: .vertical) + text1.setContentCompressionResistancePriority(.required, for: .horizontal) + line1.addArrangedSubview(text1) + self.text1 = text1 + + let line2 = UIStackView() + line2.spacing = 10 + line2.axis = .horizontal + labelStack.addArrangedSubview(line2) + + let arrow2 = UIImageView(image: .init(systemName: "checkmark")) + arrow2.contentMode = .scaleAspectFit + arrow2.widthAnchor.constraint(equalToConstant: 16).isActive = true + line2.addArrangedSubview(arrow2) + self.arrow2 = arrow2 + + let text2 = UILabel() + text2.text = .localized(.beClimateActive) + text2.translatesAutoresizingMaskIntoConstraints = false + text2.font = .preferredFont(forTextStyle: .subheadline) + text2.adjustsFontForContentSizeCategory = true + text2.numberOfLines = 0 + text2.setContentCompressionResistancePriority(.required, for: .vertical) + text2.setContentCompressionResistancePriority(.required, for: .horizontal) + line2.addArrangedSubview(text2) + self.text2 = text2 + + let cta = EcosiaPrimaryButton(windowUUID: windowUUID) + cta.contentEdgeInsets = .init(top: 0, left: 16, bottom: 0, right: 16) + cta.translatesAutoresizingMaskIntoConstraints = false + cta.setTitle(.localized(.defaultBrowserPromptExperimentButtonControl), for: .normal) + cta.titleLabel?.font = .preferredFont(forTextStyle: .callout) + cta.titleLabel?.adjustsFontForContentSizeCategory = true + cta.layer.cornerRadius = 25 + cta.addTarget(self, action: #selector(ctaTapped), for: .primaryActionTriggered) + content.addSubview(cta) + self.cta = cta + + cta.topAnchor.constraint(equalTo: labelStack.bottomAnchor, constant: 24).isActive = true + cta.heightAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive = true + cta.leadingAnchor.constraint(equalTo: headline.leadingAnchor).isActive = true + cta.trailingAnchor.constraint(equalTo: headline.trailingAnchor).isActive = true + cta.setContentHuggingPriority(.required, for: .vertical) + + let skip = UIButton(type: .system) + skip.translatesAutoresizingMaskIntoConstraints = false + skip.backgroundColor = .clear + skip.titleLabel?.font = .preferredFont(forTextStyle: .callout) + skip.titleLabel?.adjustsFontForContentSizeCategory = true + skip.setTitle(.localized(.maybeLater), for: .normal) + skip.addTarget(self, action: #selector(skipTapped), for: .primaryActionTriggered) + content.addSubview(skip) + self.skip = skip + + skip.topAnchor.constraint(equalTo: cta.bottomAnchor, constant: 8).isActive = true + skip.leadingAnchor.constraint(equalTo: cta.leadingAnchor).isActive = true + skip.trailingAnchor.constraint(equalTo: cta.trailingAnchor).isActive = true + skip.heightAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive = true + skip.bottomAnchor.constraint(equalTo: content.safeAreaLayoutGuide.bottomAnchor, constant: -16).isActive = true + } + + @objc func applyTheme() { + let theme = themeManager.getCurrentTheme(for: windowUUID) + view.backgroundColor = .clear + headline.textColor = theme.colors.ecosia.textPrimary + text1.textColor = theme.colors.ecosia.textSecondary + text2.textColor = theme.colors.ecosia.textSecondary + arrow1.tintColor = theme.colors.ecosia.buttonBackgroundPrimary + arrow2.tintColor = theme.colors.ecosia.buttonBackgroundPrimary + content.backgroundColor = theme.colors.ecosia.ntpIntroBackground + waves.tintColor = theme.colors.ecosia.ntpIntroBackground + cta.setTitleColor(theme.colors.ecosia.textInversePrimary, for: .normal) + skip.setTitleColor(theme.colors.ecosia.buttonBackgroundPrimary, for: .normal) + cta.backgroundColor = theme.colors.ecosia.buttonBackgroundPrimary + } + + @objc private func skipTapped() { + Analytics.shared.defaultBrowser(.close) + dismiss(animated: true) + } + + @objc private func ctaTapped() { + Analytics.shared.defaultBrowser(.click) + + dismiss(animated: true) { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:]) + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/DefaultBrowserViewController.swift b/firefox-ios/Client/Ecosia/UI/NTP/DefaultBrowserViewController.swift new file mode 100644 index 0000000000000..410c44d379d5e --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/DefaultBrowserViewController.swift @@ -0,0 +1,270 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +protocol DefaultBrowserDelegate: AnyObject { + func defaultBrowserDidShow(_ defaultBrowser: DefaultBrowserViewController) +} + +final class DefaultBrowserViewController: UIViewController, Themeable { + static let minSearchCountToTrigger = 50 + + struct UX { + static let imageHeight: CGFloat = 300 + static let wavesHeight: CGFloat = 92 + static let buttonHeight: CGFloat = 48 + static let checksSize: CGFloat = 24 + } + + private lazy var contentView: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.layer.cornerRadius = .ecosia.borderRadius._l + view.clipsToBounds = true + return view + }() + private lazy var imageView: UIImageView = { + let view = UIImageView(image: .init(named: "defaultBrowser")) + view.contentMode = .scaleAspectFill + view.clipsToBounds = true + view.translatesAutoresizingMaskIntoConstraints = false + view.setContentCompressionResistancePriority(.required, for: .vertical) + view.setContentHuggingPriority(.required, for: .vertical) + return view + }() + private lazy var waves: UIImageView = { + let view = UIImageView(image: .init(named: "defaultBrowserWaves")) + view.translatesAutoresizingMaskIntoConstraints = false + view.contentMode = .scaleAspectFill + return view + }() + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.text = .localized(.defaultBrowserPromptExperimentTitle) + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .preferredFont(forTextStyle: .title2).bold() + label.adjustsFontForContentSizeCategory = true + label.adjustsFontSizeToFitWidth = true + label.setContentCompressionResistancePriority(.required, for: .vertical) + return label + }() + private lazy var actionButton: UIButton = { + let button = EcosiaPrimaryButton(windowUUID: windowUUID) + button.translatesAutoresizingMaskIntoConstraints = false + button.setTitle(.localized(.defaultBrowserPromptExperimentButton), for: .normal) + button.titleLabel?.font = .preferredFont(forTextStyle: .body) + button.titleLabel?.adjustsFontForContentSizeCategory = true + button.layer.cornerRadius = UX.buttonHeight/2 + button.addTarget(self, action: #selector(clickAction), for: .primaryActionTriggered) + return button + }() + private lazy var skipButton: UIButton = { + let button = UIButton(type: .system) + button.translatesAutoresizingMaskIntoConstraints = false + button.backgroundColor = .clear + button.titleLabel?.font = .preferredFont(forTextStyle: .body) + button.titleLabel?.adjustsFontForContentSizeCategory = true + button.setTitle(.localized(.notNow), for: .normal) + button.addTarget(self, action: #selector(skipAction), for: .primaryActionTriggered) + button.setContentCompressionResistancePriority(.defaultLow, for: .vertical) + return button + }() + private lazy var triviaView: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + view.layer.cornerRadius = .ecosia.borderRadius._l + view.clipsToBounds = true + return view + }() + private lazy var triviaTitleLabel: UILabel = { + let label = UILabel() + label.text = .localized(.defaultBrowserPromptExperimentDescriptionTitle) + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .preferredFont(forTextStyle: .body).semibold() + return label + }() + private lazy var triviaDecriptionLabel: UILabel = { + let label = UILabel() + let text = String.localized(.defaultBrowserPromptExperimentDescription) + let highlight = String.localized(.defaultBrowserPromptExperimentDescriptionHighlight) + label.attributedText = text.semiboldHighlight(highlight) + label.translatesAutoresizingMaskIntoConstraints = false + label.adjustsFontForContentSizeCategory = true + label.numberOfLines = 0 + return label + }() + private lazy var beforeView: BeforeOrAfterView = { + let view = BeforeOrAfterView(type: .before) + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + private lazy var afterView: BeforeOrAfterView = { + let view = BeforeOrAfterView(type: .after) + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + var screenWidth: CGFloat { UIScreen.main.bounds.width } + + // MARK: Themeable Properties + let windowUUID: WindowUUID + var currentWindowUUID: WindowUUID? { windowUUID } + var themeManager: ThemeManager { AppContainer.shared.resolve() } + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + + weak var delegate: DefaultBrowserDelegate? + init(windowUUID: WindowUUID, delegate: DefaultBrowserDelegate) { + self.windowUUID = windowUUID + super.init(nibName: nil, bundle: nil) + self.delegate = delegate + if traitCollection.userInterfaceIdiom == .pad { + modalPresentationStyle = .formSheet + preferredContentSize = .init(width: 544, height: 600) + } else { + modalPresentationCapturesStatusBarAppearance = true + } + } + + required init?(coder: NSCoder) { nil } + + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + traitCollection.userInterfaceIdiom == .pad ? .all : .portrait + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + .lightContent + } + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + setupConstraints() + applyTheme() + + listenForThemeChange(self.view) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + Analytics.shared.defaultBrowser(.view) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + modalTransitionStyle = .crossDissolve + self.delegate?.defaultBrowserDidShow(self) + } + + private func setupViews() { + view.addSubview(contentView) + contentView.addSubview(imageView) + contentView.addSubview(waves) + contentView.addSubview(titleLabel) + contentView.addSubview(actionButton) + contentView.addSubview(skipButton) + view.addSubview(triviaView) + + triviaView.addSubview(triviaTitleLabel) + triviaView.addSubview(triviaDecriptionLabel) + contentView.addSubview(beforeView) + contentView.addSubview(afterView) + } + + private func setupConstraints() { + NSLayoutConstraint.activate([ + contentView.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor), + contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + + imageView.topAnchor.constraint(equalTo: contentView.topAnchor), + imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + imageView.heightAnchor.constraint(equalToConstant: UX.imageHeight), + + waves.bottomAnchor.constraint(equalTo: imageView.bottomAnchor), + waves.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + waves.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + waves.heightAnchor.constraint(equalToConstant: UX.wavesHeight), + + titleLabel.topAnchor.constraint(equalTo: waves.bottomAnchor, constant: .ecosia.space._3l), + titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: .ecosia.space._m), + titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -.ecosia.space._m), + + triviaView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: .ecosia.space._m), + triviaView.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor), + triviaView.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor), + + actionButton.topAnchor.constraint(equalTo: triviaView.bottomAnchor, constant: .ecosia.space._1l).priority(.defaultLow), + actionButton.heightAnchor.constraint(greaterThanOrEqualToConstant: UX.buttonHeight), + actionButton.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor), + actionButton.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor), + + skipButton.topAnchor.constraint(equalTo: actionButton.bottomAnchor, constant: .ecosia.space._s), + skipButton.leadingAnchor.constraint(equalTo: actionButton.leadingAnchor), + skipButton.trailingAnchor.constraint(equalTo: actionButton.trailingAnchor), + skipButton.heightAnchor.constraint(greaterThanOrEqualToConstant: UX.buttonHeight), + skipButton.bottomAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.bottomAnchor, constant: -.ecosia.space._m), + + triviaTitleLabel.topAnchor.constraint(equalTo: triviaView.topAnchor, constant: .ecosia.space._m), + triviaDecriptionLabel.topAnchor.constraint(equalTo: triviaTitleLabel.bottomAnchor, constant: .ecosia.space._m), + triviaDecriptionLabel.bottomAnchor.constraint(equalTo: triviaView.bottomAnchor, constant: -.ecosia.space._m), + triviaTitleLabel.leadingAnchor.constraint(equalTo: triviaView.leadingAnchor, constant: .ecosia.space._m), + triviaTitleLabel.trailingAnchor.constraint(equalTo: triviaView.trailingAnchor, constant: -.ecosia.space._m), + triviaDecriptionLabel.leadingAnchor.constraint(equalTo: triviaView.leadingAnchor, constant: .ecosia.space._m), + triviaDecriptionLabel.trailingAnchor.constraint(equalTo: triviaView.trailingAnchor, constant: -.ecosia.space._m), + + beforeView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor), + afterView.centerYAnchor.constraint(equalTo: beforeView.centerYAnchor), + beforeView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor, constant: -screenWidth/4), + afterView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor, constant: screenWidth/4) + ]) + } + + @objc func applyTheme() { + let theme = themeManager.getCurrentTheme(for: windowUUID) + view.backgroundColor = .clear + titleLabel.textColor = theme.colors.ecosia.textPrimary + contentView.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + waves.tintColor = theme.colors.ecosia.backgroundPrimaryDecorative + actionButton.setTitleColor(theme.colors.ecosia.textInversePrimary, for: .normal) + skipButton.setTitleColor(theme.colors.ecosia.buttonBackgroundPrimary, for: .normal) + actionButton.backgroundColor = theme.colors.ecosia.buttonBackgroundPrimary + triviaView.backgroundColor = theme.colors.ecosia.backgroundElevation1 + triviaTitleLabel.textColor = theme.colors.ecosia.textPrimary + triviaDecriptionLabel.textColor = theme.colors.ecosia.textSecondary + beforeView.applyTheme(theme: theme) + afterView.applyTheme(theme: theme) + } + + @objc private func skipAction() { + Analytics.shared.defaultBrowser(.close) + dismiss(animated: true) + } + + @objc private func clickAction() { + Analytics.shared.defaultBrowser(.click) + + dismiss(animated: true) { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:]) + } + } +} + +fileprivate extension String { + func semiboldHighlight(_ highlight: String, baseFont: UIFont = .preferredFont(forTextStyle: .body)) -> NSAttributedString { + let fullText = String(format: self, highlight) + let attributedText = NSMutableAttributedString(string: fullText, + attributes: [.font: baseFont]) + if let range = fullText.range(of: highlight) { + attributedText.addAttributes([ + .font: UIFont.systemFont(ofSize: baseFont.pointSize, weight: .semibold) + ], range: .init(range, in: fullText)) + } + return fullText.attributedText(boldString: highlight, font: baseFont) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/LocalizedSearchSuggestions.swift b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/LocalizedSearchSuggestions.swift new file mode 100644 index 0000000000000..ee98db09a18d4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/LocalizedSearchSuggestions.swift @@ -0,0 +1,111 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +/// Utility for providing region and language specific search suggestions +struct LocalizedSearchSuggestions { + + /// Represents a region and language combination + enum RegionLanguage: String { + case franceFrench = "fr_FR" + case franceEnglish = "en_FR" + case germanyGerman = "de_DE" + case germanyEnglish = "en_DE" + case ukEnglish = "en_GB" + case usEnglish = "en_US" + case `default` = "default" + + /// Returns the locale identifier for this region language + private static func identifier(for locale: Locale) -> String { + // Concatenated since locale.identifier has strange behaviour with mismatched language and region + // E.g. English (US) on Germany is "en_us@rg=dezzzz" + if let language = locale.languageIdentifier, + let region = locale.regionIdentifier { + return language + "_" + region + } else { + return locale.identifier + } + } + + /// Returns the analytics-formatted identifier (with hyphens instead of underscores) + var analyticsIdentifier: String { + Self.identifier(for: .current).replacingOccurrences(of: "_", with: "-") + } + + static func current() -> RegionLanguage { + return from(locale: Locale.current) + } + + static func from(locale: Locale) -> RegionLanguage { + let identifier = Self.identifier(for: locale) + return RegionLanguage(rawValue: identifier) ?? .default + } + } + + /// Returns the current region language identifier for analytics + static func currentRegionLanguageAnalyticsIdentifier() -> String { + return RegionLanguage.current().analyticsIdentifier + } + + /// Returns localized search suggestions based on the user's region and language + static func suggestions(for regionLanguage: RegionLanguage = .current()) -> [String] { + switch regionLanguage { + case .franceFrench: + return [ + "Météo Toulouse", + "Notre-Dame de Paris", + "Quelle est la hauteur de la tour Eiffel ?", + "\"Bonne journée\" en espagnol", + "Activités à Marseille" + ] + case .franceEnglish: + return [ + "Weather in Toulouse", + "Notre-Dame of Paris", + "What is the height of the Eiffel Tower?", + "\"Have a nice day\" in Spanish", + "Things to do in Marseille" + ] + case .germanyGerman: + return [ + "Wetter Berlin", + "Neuschwanstein", + "Wie hoch ist der Berliner Fernsehturm?", + "\"Hab einen schönen Tag\" auf Spanisch", + "Aktivitäten in München" + ] + case .germanyEnglish: + return [ + "Weather in Berlin", + "Neuschwanstein Castle", + "How tall is the Berlin TV Tower?", + "\"Have a nice day\" in Spanish", + "Things to do in Munich", + ] + case .ukEnglish: + return [ + "Weather in London", + "Palace of Westminster", + "How to see the Northern Lights?", + "\"Have a nice day\" in Spanish", + "Things to do in London" + ] + case .usEnglish: + return [ + "New York City", + "Things to do in Miami", + "Weather in San Francisco", + "How long is the Rio Grande?", + "\"Have a nice day\" in Spanish" + ] + case .default: + return [ + "Time in New York", + "Climate change", + "How to see the Northern Lights?", + "Solar System", + "100 USD to EUR" + ] + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchCell.swift new file mode 100644 index 0000000000000..c02028dc277a0 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchCell.swift @@ -0,0 +1,110 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import SwiftUI + +/// Cell displayed during the product tour's "First Search" state +/// This is a lightweight wrapper around the SwiftUI NTPFirstSearchView +final class NTPFirstSearchCell: UICollectionViewCell, ReusableCell, ThemeApplicable { + + // MARK: - Properties + + var onCloseButtonTapped: (() -> Void)? + var onSearchSuggestionTapped: ((String) -> Void)? + + private var hostingController: UIHostingController? + private var windowUUID: WindowUUID = .XCTestDefaultUUID + + // MARK: - Initialization + + override init(frame: CGRect) { + super.init(frame: frame) + setupCell() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupCell() + } + + // MARK: - Setup + + private func setupCell() { + contentView.backgroundColor = .clear + } + + private func setupSwiftUIView(title: String, description: String, suggestions: [String]) { + // Create SwiftUI view + let swiftUIView = NTPFirstSearchView( + title: title, + description: description, + suggestions: suggestions, + windowUUID: windowUUID, + onClose: { [weak self] in + self?.onCloseButtonTapped?() + }, + onSearchSuggestionTapped: { [weak self] suggestion in + self?.onSearchSuggestionTapped?(suggestion) + } + ) + + // Update the existing hosting controller's root view if already set up + if let hosting = hostingController { + hosting.rootView = swiftUIView + return + } + + // Create and configure hosting controller + let hosting = UIHostingController(rootView: swiftUIView) + hosting.view.backgroundColor = .clear + hosting.view.translatesAutoresizingMaskIntoConstraints = false + + // Add to content view + contentView.addSubview(hosting.view) + + NSLayoutConstraint.activate([ + hosting.view.topAnchor.constraint(equalTo: contentView.topAnchor), + hosting.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + hosting.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + hosting.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + ]) + + hostingController = hosting + } + + // MARK: - Configuration + + func configure(title: String, + description: String, + suggestions: [String], + windowUUID: WindowUUID) { + self.windowUUID = windowUUID + setupSwiftUIView(title: title, description: description, suggestions: suggestions) + } + + // MARK: - ThemeApplicable + + func applyTheme(theme: Theme) { + // No need to do anything here - the SwiftUI view handles theme changes + // via the ecosiaThemed modifier automatically + contentView.backgroundColor = .clear + } + + // MARK: - Lifecycle + + override func prepareForReuse() { + super.prepareForReuse() + + // Clean up hosting controller + hostingController?.view.removeFromSuperview() + hostingController?.removeFromParent() + hostingController = nil + + // Reset stored values + onCloseButtonTapped = nil + onSearchSuggestionTapped = nil + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchView.swift b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchView.swift new file mode 100644 index 0000000000000..9f8bfd3dabc11 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchView.swift @@ -0,0 +1,145 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common +import Ecosia + +struct NTPFirstSearchView: View { + + struct UX { + static let contentTopSpacing: CGFloat = 40 // Includes space for the icon + static let contentSpacing: CGFloat = 8 + static let extraSuggestionsSpacing: CGFloat = 12 + static let cornerRadius: CGFloat = 12 + static let iconSize: CGFloat = 36 + static let iconCircleSize: CGFloat = 56 + static let closeButtonSize: CGFloat = 40 + static let closeButtonImageSize: CGFloat = 16 + static let closeButtonMargin: CGFloat = 8 + static let contentHorizontalPadding: CGFloat = 16 + static let contentBottomPadding: CGFloat = 24 + } + + // MARK: - Properties + + let title: String + let description: String + let suggestions: [String] + let windowUUID: WindowUUID + let onClose: () -> Void + let onSearchSuggestionTapped: (String) -> Void + + @State private var theme = NTPFirstSearchViewTheme() + @SwiftUI.Environment(\.themeManager) var themeManager: any ThemeManager + + // MARK: - Body + + var body: some View { + ZStack(alignment: .top) { + // Main container + VStack(spacing: 0) { + // Top spacing for icon overlap + Color.clear + .frame(height: UX.iconCircleSize / 2) + + // Content container + ZStack(alignment: .topTrailing) { + // Main content + VStack(spacing: UX.contentSpacing) { + Text(title) + .font(.callout.weight(.semibold)) + .foregroundColor(theme.textPrimary) + .multilineTextAlignment(.center) + + Text(description) + .font(.subheadline) + .foregroundColor(theme.textSecondary) + .multilineTextAlignment(.center) + .fixedSize(horizontal: false, vertical: true) + + // Search suggestions + if !suggestions.isEmpty { + SearchSuggestionFlowLayout( + suggestions: suggestions, + onSuggestionTapped: onSearchSuggestionTapped, + theme: themeManager.getCurrentTheme(for: windowUUID), + availableWidth: nil // Will use parent width + ) + .padding(.top, UX.extraSuggestionsSpacing) + } + } + .padding(.horizontal, UX.contentHorizontalPadding) + .padding(.top, UX.contentTopSpacing) + .padding(.bottom, UX.contentBottomPadding) + + // Close button + Button(action: onClose) { + Image(uiImage: closeButtonImage) + .renderingMode(.template) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: UX.closeButtonImageSize, height: UX.closeButtonImageSize) + .foregroundColor(theme.buttonContentSecondary) + .frame(width: UX.closeButtonSize, height: UX.closeButtonSize) + .contentShape(Rectangle()) + } + .buttonStyle(EcosiaButtonStyle(theme: themeManager.getCurrentTheme(for: windowUUID), style: .bare, cornerRadius: UX.closeButtonSize / 2)) + .accessibilityLabel(Text(verbatim: .localized(.close))) + .padding(.top, UX.closeButtonMargin) + .padding(.trailing, UX.closeButtonMargin) + } + .background( + RoundedRectangle(cornerRadius: UX.cornerRadius) + .fill(theme.backgroundElevation1) + ) + } + + // Icon circle (overlapping the top) + VStack(spacing: 0) { + Circle() + .fill(theme.backgroundElevation1) + .frame(width: UX.iconCircleSize, height: UX.iconCircleSize) + .overlay( + Image(uiImage: iconImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: UX.iconSize, height: UX.iconSize) + ) + .accessibilityHidden(true) + + Spacer() + } + } + .ecosiaThemed(windowUUID, $theme) + } + + // MARK: - Helper Properties + + private var iconImage: UIImage { + UIImage(named: "plantedSeedling") ?? UIImage() + } + + private var closeButtonImage: UIImage { + let config = UIImage.SymbolConfiguration(pointSize: UX.closeButtonImageSize, weight: .medium) + return UIImage(named: "close", in: .ecosia, with: config) ?? UIImage() + } +} + +// MARK: - Theme + +/// Theme configuration for NTPFirstSearchView +struct NTPFirstSearchViewTheme: EcosiaThemeable { + var textPrimary = Color.black + var textSecondary = Color.gray + var backgroundElevation1 = Color.white + var buttonContentSecondary = Color.gray + + mutating func applyTheme(theme: Theme) { + textPrimary = Color(theme.colors.ecosia.textPrimary) + textSecondary = Color(theme.colors.ecosia.textSecondary) + backgroundElevation1 = Color(theme.colors.ecosia.backgroundElevation1) + buttonContentSecondary = Color(theme.colors.ecosia.buttonContentSecondary) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchViewModel.swift new file mode 100644 index 0000000000000..13428a7db34c4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/NTPFirstSearchViewModel.swift @@ -0,0 +1,129 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common +import Ecosia + +protocol NTPFirstSearchCellViewModelDelegate: AnyObject { + func searchWithQuery(_ query: String) +} + +/// View model for the Product Tour NTP section that appears during onboarding +class NTPFirstSearchCellViewModel: HomepageViewModelProtocol, FeatureFlaggable { + + var sectionType: HomepageSectionType = .firstSearch + var headerViewModel = LabelButtonHeaderViewModel.emptyHeader + let isEnabled: Bool = true + + internal var theme: Theme + let windowUUID: WindowUUID + private var productTourManager: ProductTourManager + weak var delegate: NTPFirstSearchCellViewModelDelegate? + weak var dataModelDelegate: HomepageDataModelDelegate? + + var shouldShow: Bool { + return productTourManager.shouldShowProductTourHomepage + && !productTourManager.isSignInFlowActive + } + + init(theme: Theme, + windowUUID: WindowUUID, + productTourManager: ProductTourManager = ProductTourManager.shared) { + self.theme = theme + self.windowUUID = windowUUID + self.productTourManager = productTourManager + + productTourManager.addObserver(self) + } + + deinit { + productTourManager.removeObserver(self) + } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1), + heightDimension: .estimated(300) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1), + heightDimension: .estimated(300) + ) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + let leadingInset = HomepageViewModel.UX.leadingInset(traitCollection: traitCollection) + section.contentInsets = NSDirectionalEdgeInsets( + top: HomepageViewModel.UX.spacingBetweenSections, + leading: leadingInset, + bottom: 0, + trailing: leadingInset + ) + + return section + } + + func numberOfItemsInSection() -> Int { + return shouldShow ? 1 : 0 + } + + func setTheme(theme: Theme) { + self.theme = theme + } +} + +// MARK: - HomepageSectionHandler + +extension NTPFirstSearchCellViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, + at indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = cell as? NTPFirstSearchCell else { return cell } + cell.configure( + title: .localized(.ntpFirstSearchTitle), + description: .localized(.ntpFirstSearchDescription), + suggestions: LocalizedSearchSuggestions.suggestions(), + windowUUID: windowUUID + ) + cell.onCloseButtonTapped = { [weak self] in + self?.handleCloseAction() + } + cell.onSearchSuggestionTapped = { [weak self] suggestion in + self?.handleSearchSuggestion(suggestion) + } + cell.applyTheme(theme: theme) + return cell + } + + // MARK: - Private Action Handlers + + private func handleCloseAction() { + Analytics.shared.firstSearchCardDismiss() + productTourManager.completeTour() + } + + private func handleSearchSuggestion(_ suggestion: String) { + // Find the index of the suggestion to track as pill number + let suggestions = LocalizedSearchSuggestions.suggestions() + if let pillIndex = suggestions.firstIndex(of: suggestion) { + let languageRegionIdentifier = LocalizedSearchSuggestions.currentRegionLanguageAnalyticsIdentifier() + Analytics.shared.firstSearchCardSuggestionClick( + pillNumber: pillIndex + 1, // 1-based indexing for analytics + languageRegionIdentifier: languageRegionIdentifier + ) + } + delegate?.searchWithQuery(suggestion) + } +} + +// MARK: - ProductTourObserver + +extension NTPFirstSearchCellViewModel: ProductTourObserver { + func productTour(didReceiveEvent event: ProductTourEvent) { + dataModelDelegate?.reloadView() + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/SearchSuggestionFlowLayout.swift b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/SearchSuggestionFlowLayout.swift new file mode 100644 index 0000000000000..308209a9870d5 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/FirstSearch/SearchSuggestionFlowLayout.swift @@ -0,0 +1,165 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import UIKit +import Common +import Ecosia + +/// SwiftUI view that provides a stable flow layout for search suggestion pills +struct SearchSuggestionFlowLayout: View { + let suggestions: [String] + let onSuggestionTapped: (String) -> Void + let theme: Theme + let availableWidth: CGFloat? + + var body: some View { + FlowLayoutContainer( + items: suggestions, + spacing: 8, + theme: theme, + availableWidth: availableWidth, + onTap: onSuggestionTapped + ) + } +} + +/// Container that handles the flow layout +struct FlowLayoutContainer: View { + let items: [String] + let spacing: CGFloat + let theme: Theme + let availableWidth: CGFloat? + let onTap: (String) -> Void + + @State private var measuredWidth: CGFloat = 0 + + private var effectiveWidth: CGFloat { + if let width = availableWidth, width > 0 { return width } + // Use the measured width once available; fall back to screen width only for the + // very first render before GeometryReader fires, to avoid an empty layout. + return measuredWidth > 0 ? measuredWidth : UIScreen.main.bounds.width + } + + var body: some View { + let rows = computeRows(availableWidth: effectiveWidth) + + VStack(alignment: .center, spacing: spacing) { + ForEach(Array(rows.enumerated()), id: \.offset) { _, row in + HStack(spacing: spacing) { + ForEach(row, id: \.self) { item in + SearchSuggestionPill( + text: item, + theme: theme, + onTap: { onTap(item) } + ) + } + } + } + } + .frame(maxWidth: .infinity) + .background( + GeometryReader { geometry in + Color.clear + .onAppear { measuredWidth = geometry.size.width } + .onChange(of: geometry.size.width) { measuredWidth = $0 } + } + ) + } + + private func computeRows(availableWidth: CGFloat) -> [[String]] { + var rows: [[String]] = [] + var currentRow: [String] = [] + var currentRowWidth: CGFloat = 0 + + for item in items { + let estimatedPillWidth = estimatePillWidth(for: item) + let requiredWidth = currentRowWidth + estimatedPillWidth + (currentRow.isEmpty ? 0 : spacing) + + if requiredWidth <= availableWidth || currentRow.isEmpty { + currentRow.append(item) + currentRowWidth = requiredWidth + } else { + if !currentRow.isEmpty { + rows.append(currentRow) + } + currentRow = [item] + currentRowWidth = estimatedPillWidth + } + } + + if !currentRow.isEmpty { + rows.append(currentRow) + } + + return rows + } + + private func estimatePillWidth(for text: String) -> CGFloat { + let textSize = measureText(text, font: SearchSuggestionPill.UX.font) + return textSize.width + SearchSuggestionPill.UX.totalNonTextWidth + } + + private func measureText(_ text: String, font: UIFont) -> CGSize { + let attributes: [NSAttributedString.Key: Any] = [.font: font] + let size = (text as NSString).size(withAttributes: attributes) + return CGSize(width: ceil(size.width), height: ceil(size.height)) + } +} + +/// Individual pill for search suggestions +struct SearchSuggestionPill: View { + let text: String + let theme: Theme + let onTap: () -> Void + + struct UX { + static let fontSize: CGFloat = 15 // subheadline equivalent + static let fontWeight: UIFont.Weight = .regular + static let iconSize: CGFloat = 16 + static let iconTextSpacing: CGFloat = 4 + static let horizontalPadding: CGFloat = 12 + static let verticalPadding: CGFloat = 12 + static let height: CGFloat = 44 + static let cornerRadius: CGFloat = 22 + static let borderWidth: CGFloat = 1 + + static var totalHorizontalPadding: CGFloat { + horizontalPadding * 2 + } + + static var totalNonTextWidth: CGFloat { + iconSize + iconTextSpacing + totalHorizontalPadding + borderWidth + } + + static var font: UIFont { + UIFont.systemFont(ofSize: fontSize, weight: fontWeight) + } + } + + var body: some View { + Button(action: onTap) { + HStack(spacing: UX.iconTextSpacing) { + Text(text) + .preferredBodyFont(size: UX.fontSize) + .foregroundColor(Color(theme.colors.ecosia.buttonContentSecondary)) + .lineLimit(1) + .fixedSize() + + if let searchIcon = UIImage(named: "searchUrl") { + Image(uiImage: searchIcon) + .resizable() + .aspectRatio(contentMode: .fit) + .foregroundColor(Color(theme.colors.ecosia.iconDecorative)) + .frame(width: UX.iconSize, height: UX.iconSize) + .accessibilityHidden(true) + } + } + .padding(.horizontal, UX.horizontalPadding) + .padding(.vertical, UX.verticalPadding) + .frame(height: UX.height) + } + .buttonStyle(EcosiaButtonStyle(theme: theme, style: .outline, cornerRadius: UX.cornerRadius, borderWidth: UX.borderWidth)) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Header/NTPHeader.swift b/firefox-ios/Client/Ecosia/UI/NTP/Header/NTPHeader.swift new file mode 100644 index 0000000000000..62566f2c676dd --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Header/NTPHeader.swift @@ -0,0 +1,137 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SwiftUI +import Common +import Ecosia + +protocol NTPHeaderDelegate: AnyObject { + func headerOpenAISearch() +} + +/// NTP header cell containing multiple Ecosia-specific actions like AI search +@available(iOS 16.0, *) +final class NTPHeader: UICollectionViewCell, ReusableCell { + + // MARK: - Properties + private var hostingController: UIHostingController? + private var viewModel: NTPHeaderViewModel? + + // MARK: - Init + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + // MARK: - Setup + + private func setup() { + // Create a placeholder hosting controller - will be configured later + let hostingController = UIHostingController(rootView: AnyView(EmptyView())) + hostingController.view.backgroundColor = UIColor.clear + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + + contentView.addSubview(hostingController.view) + self.hostingController = hostingController + + NSLayoutConstraint.activate([ + hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor), + hostingController.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + ]) + } + + // MARK: - Public Methods + + func configure(with viewModel: NTPHeaderViewModel, + windowUUID: WindowUUID) { + self.viewModel = viewModel + + // Update the SwiftUI view with the new view model + let swiftUIView = NTPHeaderView( + viewModel: viewModel, + windowUUID: windowUUID + ) + + hostingController?.rootView = AnyView(swiftUIView) + } +} + +// MARK: - SwiftUI Multi-Purpose Header View +@available(iOS 16.0, *) +struct NTPHeaderView: View { + @ObservedObject var viewModel: NTPHeaderViewModel + let windowUUID: WindowUUID + @SwiftUI.Environment(\.themeManager) var themeManager: any ThemeManager + @SwiftUI.Environment(\.accessibilityReduceMotion) var reduceMotion: Bool + @State private var showAccountImpactView = false + + var body: some View { + HStack(spacing: .ecosia.space._1s) { + Spacer() + if AISearchMVPExperiment.isEnabled { + EcosiaAISearchButton( + windowUUID: windowUUID, + onTap: handleAISearchTap + ) + } + ZStack(alignment: .topLeading) { + EcosiaAccountNavButton( + seedCount: viewModel.seedCount, + avatarURL: viewModel.userAvatarURL, + enableAnimation: !reduceMotion && viewModel.shouldAnimateSeed, + showSeedSparkles: viewModel.showSeedSparkles, + windowUUID: windowUUID, + onTap: handleTap + ) + .sheet(isPresented: $showAccountImpactView) { + EcosiaAccountImpactView( + viewModel: EcosiaAccountImpactViewModel( + onLogin: { + viewModel.performLogin() + }, + onDismiss: { + showAccountImpactView = false + } + ), + windowUUID: windowUUID + ) + .padding(.horizontal, .ecosia.space._m) + .dynamicHeightPresentationDetent() + } + if let increment = viewModel.balanceIncrement { + BalanceIncrementAnimationView( + increment: increment, + windowUUID: windowUUID + ) + .offset(x: 18, y: -8) + } + } + } + .padding(.leading, .ecosia.space._m) + .padding(.trailing, .ecosia.space._m) + .onAppear { + viewModel.refreshSeedState() + } + .onReceive(NotificationCenter.default.publisher(for: UIScene.willEnterForegroundNotification)) { _ in + viewModel.refreshSeedState() + } + } + + private func handleAISearchTap() { + viewModel.openAISearch() + } + + private func handleTap() { + showAccountImpactView = true + Analytics.shared.accountHeaderClicked() + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Header/NTPHeaderViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/Header/NTPHeaderViewModel.swift new file mode 100644 index 0000000000000..77bbdea35d9d4 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Header/NTPHeaderViewModel.swift @@ -0,0 +1,178 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import Foundation +import Shared +import SwiftUI +import Ecosia +import Combine + +final class NTPHeaderViewModel: ObservableObject { + struct UX { + static let topInset: CGFloat = 24 + } + + // MARK: - Properties + internal weak var delegate: NTPHeaderDelegate? + internal var theme: Theme + private let windowUUID: WindowUUID + let profile: Profile + private(set) var auth: EcosiaAuth + var onTapAction: ((UIButton) -> Void)? + private let authStateProvider = EcosiaAuthUIStateProvider.shared + private var cancellables = Set() + + var seedCount: Int { authStateProvider.seedCount } + var isLoggedIn: Bool { authStateProvider.isLoggedIn } + var userAvatarURL: URL? { authStateProvider.avatarURL } + var balanceIncrement: Int? { authStateProvider.balanceIncrement } + var shouldAnimateSeed: Bool { balanceIncrement != nil } + @Published var showSeedSparkles: Bool = false + + private var levelUpObserver: NSObjectProtocol? + + // MARK: - Initialization + init(profile: Profile, + theme: Theme, + windowUUID: WindowUUID, + auth: EcosiaAuth, + delegate: NTPHeaderDelegate? = nil) { + self.profile = profile + self.theme = theme + self.windowUUID = windowUUID + self.auth = auth + self.delegate = delegate + + // Forward objectWillChange notifications from authStateProvider + // This ensures SwiftUI knows to update the view when auth state changes + authStateProvider.objectWillChange + .sink { [weak self] _ in + self?.objectWillChange.send() + } + .store(in: &cancellables) + + setupLevelUpObserver() + } + + deinit { + if let observer = levelUpObserver { + NotificationCenter.default.removeObserver(observer) + } + } + + var shouldShow: Bool { + // If on product tour homepage, show only if logged in + if ProductTourManager.shared.shouldShowProductTourHomepage { + return EcosiaAuthUIStateProvider.shared.isLoggedIn + } + return true + } + + private func setupLevelUpObserver() { + levelUpObserver = NotificationCenter.default.addObserver( + forName: .EcosiaAccountLevelUp, + object: nil, + queue: .main + ) { [weak self] _ in + self?.triggerSeedSparkles() + } + } + + private func triggerSeedSparkles() { + showSeedSparkles = true + + // Turn off sparkles after animation completes + DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { + self.showSeedSparkles = false + } + } + + // MARK: - Public Methods + + func openAISearch() { + delegate?.headerOpenAISearch() + Analytics.shared.aiSearchNTPButtonTapped() + } + + func performLogin() { + auth.login() + } + + func performLogout() { + EcosiaLogger.auth.info("Performing immediate logout without confirmation") + auth.logout() + } + + @MainActor + func refreshSeedState() { + authStateProvider.refreshSeedState() + } +} + +// MARK: HomeViewModelProtocol +extension NTPHeaderViewModel: HomepageViewModelProtocol, FeatureFlaggable { + var sectionType: HomepageSectionType { + return .header + } + + var headerViewModel: LabelButtonHeaderViewModel { + return .emptyHeader + } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(64)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(64)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1) + + let section = NSCollectionLayoutSection(group: group) + + section.contentInsets = NSDirectionalEdgeInsets( + top: UX.topInset, + leading: 0, + bottom: 0, + trailing: 0) + + return section + } + + func numberOfItemsInSection() -> Int { + return 1 + } + + var isEnabled: Bool { + if #available(iOS 16.0, *) { + return true + } + return false + } + + func setTheme(theme: Theme) { + self.theme = theme + } + + func refreshData(for traitCollection: UITraitCollection, size: CGSize, isPortrait: Bool, device: UIUserInterfaceIdiom) { + // No data refresh needed for multi-purpose header + } +} + +extension NTPHeaderViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + if #available(iOS 16.0, *) { + guard let headerCell = cell as? NTPHeader else { return cell } + headerCell.configure(with: self, windowUUID: windowUUID) + return headerCell + } + return cell + } + + func didSelectItem(at indexPath: IndexPath, homePanelDelegate: HomePanelDelegate?, libraryPanelDelegate: LibraryPanelDelegate?) { + // This cell handles its own button actions, no cell selection needed + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Impact/ClimateImpactInfo.swift b/firefox-ios/Client/Ecosia/UI/NTP/Impact/ClimateImpactInfo.swift new file mode 100644 index 0000000000000..a47ba2da164e3 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Impact/ClimateImpactInfo.swift @@ -0,0 +1,121 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +enum ClimateImpactInfo: Equatable { + case referral(value: Int) + case totalTrees(value: Int) + case totalInvested(value: Int) + + var title: String { + switch self { + case .referral(let value): + return "\(value)" + case .totalTrees(let value): + return NumberFormatter.ecosiaCurrency(withoutEuroSymbol: true) + .string(from: .init(integerLiteral: value)) ?? "" + case .totalInvested(let value): + return NumberFormatter.ecosiaCurrency() + .string(from: .init(integerLiteral: value)) ?? "" + } + } + + var subtitle: String { + switch self { + case .referral(let value): + return .localizedPlural(.acceptedInvites, num: value) + case .totalTrees: + return .localized(.treesPlantedByEcosia) + case .totalInvested: + return .localized(.dedicatedToClimateAction) + } + } + + var accessibilityLabel: String { + switch self { + case .referral(let value): + return accessiblityLabelTreesPlanted(value: value) + .localizedPlural(.acceptedInvites, num: value) + case .totalTrees(let value): + return value.spelledOutString + " " + .localized(.treesPlantedByEcosia) + case .totalInvested(let value): + return value.spelledOutString + " " + .localized(.dedicatedToClimateAction) + } + } + + var accessibilityIdentifier: String? { + switch self { + case .referral: + "friends_and_trees_invites_counter" + case .totalTrees: + "total_trees_count" + case .totalInvested: + "total_invested_count" + } + } + + var image: UIImage? { + switch self { + case .referral: + return .init(named: "referral", in: .ecosia, with: nil) + case .totalTrees: + return .init(named: "tree", in: .ecosia, with: nil) + case .totalInvested: + return .init(named: "banknote", in: .ecosia, with: nil) + } + } + + var buttonTitle: String? { + switch self { + case .referral: + return .localized(.inviteFriends) + case .totalTrees, .totalInvested: + return nil + } + } + + var accessibilityHint: String? { + switch self { + case .referral: + return .localized(.inviteFriends) + case .totalTrees, .totalInvested: + return nil + } + } + + var imageAccessibilityIdentifier: String? { + switch self { + case .referral: + "referral_image" + case .totalTrees: + "total_trees_image" + case .totalInvested: + "total_invested_image" + } + } + + /// Created to be used for comparison without taking the associated types arguments into consideration. + var rawValue: Int { + switch self { + case .referral: + return 0 + case .totalTrees: + return 1 + case .totalInvested: + return 2 + } + } + + private func accessiblityLabelTreesPlanted(value: Int) -> String { + value.spelledOutString + " " + .localizedPlural(.treesPlanted, num: value) + ";" + } +} + +extension Int { + fileprivate var spelledOutString: String { + let formatter = NumberFormatter() + formatter.numberStyle = .spellOut + return formatter.string(from: .init(integerLiteral: self)) ?? "" + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactCell.swift new file mode 100644 index 0000000000000..1364fddf19584 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactCell.swift @@ -0,0 +1,88 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class NTPImpactCell: UICollectionViewCell, ThemeApplicable, ReusableCell { + struct UX { + static let cellsSpacing: CGFloat = 12 + } + + private(set) weak var delegate: NTPImpactCellDelegate? { + didSet { + impactRows.forEach { $0.delegate = delegate } + } + } + + private lazy var containerStack: UIStackView = { + let stack = UIStackView() + stack.translatesAutoresizingMaskIntoConstraints = false + stack.axis = .vertical + stack.alignment = .fill + return stack + }() + private var impactRows: [NTPImpactRowView] { + containerStack.arrangedSubviews.compactMap { $0 as? NTPImpactRowView } + } + + // MARK: - Init + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + override func layoutSubviews() { + super.layoutSubviews() + setupConstraints() + } + + private func setup() { + contentView.addSubview(containerStack) + setupConstraints() + } + + private func setupConstraints() { + NSLayoutConstraint.activate([ + containerStack.topAnchor.constraint(equalTo: contentView.topAnchor), + containerStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + containerStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + containerStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -UX.cellsSpacing) + ]) + } + + func applyTheme(theme: Theme) { + containerStack.arrangedSubviews.forEach { view in + (view as? Themeable)?.applyTheme() + (view as? ThemeApplicable)?.applyTheme(theme: theme) + } + } + + func configure(items: [ClimateImpactInfo], delegate: NTPImpactCellDelegate?, theme: Theme) { + self.delegate = delegate + containerStack.removeAllArrangedViews() // Remove existing view upon reuse + + for (index, info) in items.enumerated() { + let row = NTPImpactRowView(info: info) + row.position = (index, items.count) + row.delegate = delegate + containerStack.addArrangedSubview(row) + } + applyTheme(theme: theme) + } + + func refresh(items: [ClimateImpactInfo]) { + impactRows.forEach { row in + let matchingInfo = items.first { $0.rawValue == row.info.rawValue } + guard let info = matchingInfo else { return } + row.info = info + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactCellViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactCellViewModel.swift new file mode 100644 index 0000000000000..fbbd80c770cf6 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactCellViewModel.swift @@ -0,0 +1,149 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Ecosia +import Common + +protocol NTPImpactCellDelegate: AnyObject { + func impactCellButtonClickedWithInfo(_ info: ClimateImpactInfo) +} + +final class NTPImpactCellViewModel { + weak var delegate: NTPImpactCellDelegate? + var infoItemSections: [[ClimateImpactInfo]] { + let firstSection: [ClimateImpactInfo] = [totalTreesInfo, totalInvestedInfo] + let secondSection: [ClimateImpactInfo] = [referralInfo] + return [firstSection, secondSection] + } + var referralInfo: ClimateImpactInfo { + .referral(value: User.shared.referrals.count) + } + var totalTreesInfo: ClimateImpactInfo { + .totalTrees(value: TreesProjection.shared.treesAt(.init())) + } + var totalInvestedInfo: ClimateImpactInfo { + .totalInvested(value: InvestmentsProjection.shared.totalInvestedAt(.init())) + } + + private var cells = [Int: NTPImpactCell]() + private let referrals: Referrals + + var theme: Theme + + init(referrals: Referrals, theme: Theme) { + self.referrals = referrals + self.theme = theme + + referrals.subscribe(self) { [weak self] _ in + guard let self = self else { return } + self.refreshCell(withInfo: self.referralInfo) + } + } + + deinit { + referrals.unsubscribe(self) + } + + func subscribeToProjections() { + guard !UIAccessibility.isReduceMotionEnabled else { + refreshCell(withInfo: totalTreesInfo) + refreshCell(withInfo: totalInvestedInfo) + return + } + + TreesProjection.shared.subscribe(self) { [weak self] _ in + guard let self = self else { return } + self.refreshCell(withInfo: self.totalTreesInfo) + } + + InvestmentsProjection.shared.subscribe(self) { [weak self] _ in + guard let self = self else { return } + self.refreshCell(withInfo: self.totalInvestedInfo) + } + } + + func unsubscribeToProjections() { + TreesProjection.shared.unsubscribe(self) + InvestmentsProjection.shared.unsubscribe(self) + } + + func refreshCell(withInfo info: ClimateImpactInfo) { + let indexForInfo = infoItemSections.firstIndex { $0.contains(where: { $0 == info }) } + guard let index = indexForInfo else { return } + cells[index]?.refresh(items: infoItemSections[index]) + } +} + +// MARK: HomeViewModelProtocol +extension NTPImpactCellViewModel: HomepageViewModelProtocol { + + func setTheme(theme: Theme) { + self.theme = theme + } + + var sectionType: HomepageSectionType { + .impact + } + + var headerViewModel: LabelButtonHeaderViewModel { + .emptyHeader + } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem( + layoutSize: .init(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(200)) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: .init(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(200)), + subitem: item, + count: 1 + ) + let section = NSCollectionLayoutSection(group: group) + + section.contentInsets = sectionType.sectionInsets(traitCollection, bottomSpacing: 0) + + var supplementaryItems = [NSCollectionLayoutBoundarySupplementaryItem]() + if NTPTooltip.highlight() != nil { + supplementaryItems.append( + .init(layoutSize: .init(widthDimension: .fractionalWidth(1), + heightDimension: .absolute(1)), + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .top) + ) + } + + supplementaryItems.append( + .init(layoutSize: .init(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(NTPImpactDividerFooter.UX.estimatedHeight)), + elementKind: UICollectionView.elementKindSectionFooter, + alignment: .bottom) + ) + section.boundarySupplementaryItems = supplementaryItems + + return section + } + + func numberOfItemsInSection() -> Int { + infoItemSections.count + } + + var isEnabled: Bool { + User.shared.showClimateImpact + } +} + +extension NTPImpactCellViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = cell as? NTPImpactCell else { return UICollectionViewCell() } + let items = infoItemSections[indexPath.row] + cell.configure(items: items, delegate: delegate, theme: theme) + cells[indexPath.row] = cell + return cell + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactDividerFooter.swift b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactDividerFooter.swift new file mode 100644 index 0000000000000..02bc53dc1e08d --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactDividerFooter.swift @@ -0,0 +1,43 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +final class NTPImpactDividerFooter: UICollectionReusableView, ReusableCell, ThemeApplicable { + struct UX { + static let dividerHeight: CGFloat = 1 + static let dividerTop: CGFloat = 20 + static let dividerBottom: CGFloat = 32 + static let dividerInset: CGFloat = 16 + static let estimatedHeight = dividerHeight + dividerTop + dividerBottom + } + + private lazy var dividerView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + // MARK: - Init + + required init?(coder: NSCoder) { nil } + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(dividerView) + + NSLayoutConstraint.activate([ + dividerView.heightAnchor.constraint(equalToConstant: 1), + dividerView.topAnchor.constraint(equalTo: topAnchor, constant: UX.dividerTop), + dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: UX.dividerInset), + dividerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -UX.dividerInset), + dividerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -UX.dividerBottom) + ]) + } + + func applyTheme(theme: Theme) { + dividerView.backgroundColor = theme.colors.ecosia.borderDecorative + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactRowView.swift b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactRowView.swift new file mode 100644 index 0000000000000..d23d945839abc --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Impact/NTPImpactRowView.swift @@ -0,0 +1,213 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common +import ComponentLibrary +import Ecosia + +/// A view representing an individual impact row, used in the New Tab Page to display environmental impact information. +final class NTPImpactRowView: UIView, ThemeApplicable { + + // MARK: - UX Constants + + /// Contains constants used for layout and sizing within the `NTPImpactRowView`. + struct UX { + static let horizontalSpacing: CGFloat = 8 + static let padding: CGFloat = 16 + static let imageHeight: CGFloat = 24 + } + + // MARK: - UI Elements + + /// Stack view to arrange title and subtitle labels vertically. + private let titleAndSubtitleContainerView = UIStackView() + + /// Main horizontal stack view that arranges the image, title, subtitle, and action button. + private let mainContainerView = UIStackView() + + /// A container view for the image. + private lazy var imageContainer: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + /// The image view representing the icon in the row. + private lazy var imageView: UIImageView = { + let image = UIImageView() + image.translatesAutoresizingMaskIntoConstraints = false + image.contentMode = .scaleAspectFit + return image + }() + + /// A label for displaying the title of the row. + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .ecosiaFamilyBrand(size: .ecosia.font._3l) + label.adjustsFontSizeToFitWidth = true + label.adjustsFontForContentSizeCategory = true + return label + }() + + /// A label for displaying the subtitle of the row. + private lazy var subtitleLabel: UILabel = { + let label = UILabel() + label.font = .preferredFont(forTextStyle: .footnote) + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + return label + }() + + /// A resizable button for performing actions related to the row. + private lazy var actionButton: ResizableButton = { + let button = ResizableButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.customFont = .preferredFont(forTextStyle: .footnote).semibold() + button.titleLabel?.textAlignment = .right + button.contentHorizontalAlignment = .right + button.contentVerticalAlignment = .center + button.buttonEdgeInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0) + button.setContentCompressionResistancePriority(.required, for: .horizontal) + button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside) + button.clipsToBounds = true + return button + }() + + /// A divider view separating rows visually. + private lazy var dividerView: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + // MARK: - Properties + + /// Delegate for handling user interactions with the row. + weak var delegate: NTPImpactCellDelegate? + + /// The information to display in this row, including title, subtitle, and button information. + var info: ClimateImpactInfo { + didSet { + imageView.image = info.image + imageView.accessibilityIdentifier = info.imageAccessibilityIdentifier + titleLabel.text = info.title + subtitleLabel.text = info.subtitle + actionButton.isHidden = forceHideActionButton ? true : info.buttonTitle == nil + actionButton.setTitle(info.buttonTitle, for: .normal) + } + } + + /// The current position of this row in the overall list (used for layout adjustments like masking). + var position: (row: Int, totalCount: Int) = (0, 0) { + didSet { + let (row, count) = position + dividerView.isHidden = row == (count - 1) + setMaskedCornersUsingPosition(row: row, totalCount: count) + } + } + + /// Whether to forcefully hide the action button in this row. + var forceHideActionButton: Bool = false { + didSet { + actionButton.isHidden = forceHideActionButton + } + } + + /// Optional background color for the row. + var customBackgroundColor: UIColor? + + // MARK: - Initialization + + /// Initializes a new `NTPImpactRowView` with the provided `ClimateImpactInfo`. + /// + /// - Parameter info: The `ClimateImpactInfo` object containing the data to display in the row. + init(info: ClimateImpactInfo) { + self.info = info + super.init(frame: .zero) + defer { + // Ensure info setup is completed after initialization + self.info = info + } + setupView() + setupConstraints() + } + + /// Not supported, as `NTPImpactRowView` requires `ClimateImpactInfo` during initialization. + required init?(coder: NSCoder) { nil } + + // MARK: - Setup Methods + + /// Configures and adds subviews to the view. + private func setupView() { + translatesAutoresizingMaskIntoConstraints = false + layer.cornerRadius = .ecosia.borderRadius._l + + mainContainerView.translatesAutoresizingMaskIntoConstraints = false + mainContainerView.axis = .horizontal + mainContainerView.alignment = .center + mainContainerView.spacing = UX.horizontalSpacing + mainContainerView.addArrangedSubview(imageContainer) + imageContainer.addSubview(imageView) + addSubview(mainContainerView) + addSubview(dividerView) + + titleAndSubtitleContainerView.translatesAutoresizingMaskIntoConstraints = false + titleAndSubtitleContainerView.axis = .vertical + titleAndSubtitleContainerView.alignment = .leading + titleAndSubtitleContainerView.addArrangedSubview(titleLabel) + titleAndSubtitleContainerView.addArrangedSubview(subtitleLabel) + titleAndSubtitleContainerView.isAccessibilityElement = true + titleAndSubtitleContainerView.shouldGroupAccessibilityChildren = true + titleAndSubtitleContainerView.accessibilityLabel = info.accessibilityLabel + titleAndSubtitleContainerView.accessibilityIdentifier = info.accessibilityIdentifier + + mainContainerView.addArrangedSubview(titleAndSubtitleContainerView) + mainContainerView.addArrangedSubview(actionButton) + } + + /// Sets up the layout constraints for the view's subviews. + private func setupConstraints() { + + NSLayoutConstraint.activate([ + mainContainerView.topAnchor.constraint(equalTo: topAnchor, constant: UX.padding), + mainContainerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: UX.padding), + mainContainerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -UX.padding), + mainContainerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -UX.padding), + dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: UX.padding), + dividerView.trailingAnchor.constraint(equalTo: trailingAnchor), + dividerView.bottomAnchor.constraint(equalTo: bottomAnchor), + dividerView.heightAnchor.constraint(equalToConstant: 1), + imageContainer.heightAnchor.constraint(equalToConstant: UX.imageHeight), + imageContainer.widthAnchor.constraint(equalTo: imageContainer.heightAnchor), + actionButton.topAnchor.constraint(equalTo: titleAndSubtitleContainerView.topAnchor), + actionButton.bottomAnchor.constraint(equalTo: titleAndSubtitleContainerView.bottomAnchor), + actionButton.widthAnchor.constraint(equalTo: mainContainerView.widthAnchor, multiplier: 1/3) + ]) + + NSLayoutConstraint.activate([ + imageView.topAnchor.constraint(equalTo: imageContainer.topAnchor), + imageView.leadingAnchor.constraint(equalTo: imageContainer.leadingAnchor), + imageView.trailingAnchor.constraint(equalTo: imageContainer.trailingAnchor), + imageView.bottomAnchor.constraint(equalTo: imageContainer.bottomAnchor), + ]) + } + + // MARK: - ThemeApplicable + + func applyTheme(theme: Theme) { + backgroundColor = customBackgroundColor ?? theme.colors.ecosia.backgroundElevation1 + titleLabel.textColor = theme.colors.ecosia.textPrimary + subtitleLabel.textColor = theme.colors.ecosia.textSecondary + actionButton.setTitleColor(theme.colors.ecosia.buttonBackgroundPrimary, for: .normal) + dividerView.backgroundColor = theme.colors.ecosia.borderDecorative + } + + // MARK: - Actions + + /// Handles the action button tap event, notifying the delegate. + @objc private func buttonAction() { + delegate?.impactCellButtonClickedWithInfo(info) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Impact/ProgressView.swift b/firefox-ios/Client/Ecosia/UI/NTP/Impact/ProgressView.swift new file mode 100644 index 0000000000000..17f3f9ef9e960 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Impact/ProgressView.swift @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +final class ProgressView: UIView { + var value = Double(1) { + didSet { + (layer as! CAShapeLayer).strokeEnd = value + } + } + var color: UIColor = .clear { + didSet { + (layer as! CAShapeLayer).strokeColor = color.cgColor + } + } + + override class var layerClass: AnyClass { + CAShapeLayer.self + } + + required init?(coder: NSCoder) { nil } + + init(size: CGSize, lineWidth: CGFloat) { + super.init(frame: .init(size: size)) + isUserInteractionEnabled = false + translatesAutoresizingMaskIntoConstraints = false + widthAnchor.constraint(equalToConstant: size.width).isActive = true + heightAnchor.constraint(equalToConstant: size.height).isActive = true + (layer as! CAShapeLayer).fillColor = UIColor.clear.cgColor + (layer as! CAShapeLayer).lineWidth = lineWidth + (layer as! CAShapeLayer).lineCap = .round + layer.masksToBounds = true + + (layer as! CAShapeLayer).path = { path in + path + .addArc(center: .init(x: size.width/2, y: size.width/2), + radius: size.width/2 - lineWidth, + startAngle: .pi - 0.2, + endAngle: 0.2, + clockwise: false) + return path + }(CGMutablePath()) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibaryCellViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibaryCellViewModel.swift new file mode 100644 index 0000000000000..c0efc65221963 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibaryCellViewModel.swift @@ -0,0 +1,78 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Common + +protocol NTPLibraryDelegate: AnyObject { + func libraryCellOpenBookmarks() + func libraryCellOpenHistory() + func libraryCellOpenReadlist() + func libraryCellOpenDownloads() +} + +final class NTPLibraryCellViewModel { + weak var delegate: NTPLibraryDelegate? + var theme: Theme + + init(delegate: NTPLibraryDelegate? = nil, theme: Theme) { + self.delegate = delegate + self.theme = theme + } +} + +// MARK: HomeViewModelProtocol +extension NTPLibraryCellViewModel: HomepageViewModelProtocol { + + func setTheme(theme: Theme) { + self.theme = theme + } + + var sectionType: HomepageSectionType { + return .libraryShortcuts + } + + var headerViewModel: LabelButtonHeaderViewModel { + return .emptyHeader + } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .fractionalHeight(1.0)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(100.0)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1) + + let section = NSCollectionLayoutSection(group: group) + + section.contentInsets = sectionType.sectionInsets(traitCollection, bottomSpacing: 8) + + return section + } + + func numberOfItemsInSection() -> Int { + return 1 + } + + var isEnabled: Bool { + true + } +} + +extension NTPLibraryCellViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = cell as? NTPLibraryCell else { + return UICollectionViewCell() + } + cell.delegate = delegate + cell.applyTheme(theme: theme) + return cell + } + + func didSelectItem(at indexPath: IndexPath, homePanelDelegate: HomePanelDelegate?, libraryPanelDelegate: LibraryPanelDelegate?) {} +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibraryCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibraryCell.swift new file mode 100644 index 0000000000000..8021ec38cb012 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibraryCell.swift @@ -0,0 +1,119 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Shared +import UIKit +import Common +import Ecosia + +class NTPLibraryCell: UICollectionViewCell, ThemeApplicable, ReusableCell { + + var mainView = UIStackView() + weak var widthConstraint: NSLayoutConstraint! + weak var heightConstraint: NSLayoutConstraint! + weak var delegate: NTPLibraryDelegate? + + enum Item: Int, CaseIterable { + case bookmarks + case history + case readingList + case downloads + + var title: String { + switch self { + case .bookmarks: return .LegacyAppMenu.AppMenuBookmarksTitleString + case .history: return .LegacyAppMenu.AppMenuHistoryTitleString + case .readingList: return .LegacyAppMenu.AppMenuReadingListTitleString + case .downloads: return .LegacyAppMenu.AppMenuDownloadsTitleString + } + } + + var image: UIImage? { + switch self { + case .bookmarks: return .init(named: "libraryFavorites") + case .history: return .init(named: "libraryHistory") + case .readingList: return .init(named: "libraryReading") + case .downloads: return .init(named: "libraryDownloads") + } + } + + var analyticsProperty: Analytics.Property.Library { + switch self { + case .bookmarks: return .bookmarks + case .history: return .history + case .readingList: return .readingList + case .downloads: return .downloads + } + } + } + + var shortcuts: [NTPLibraryShortcutView] = [] + + // MARK: - Init + + override init(frame: CGRect) { + super.init(frame: frame) + mainView.distribution = .fillEqually + mainView.spacing = 0 + mainView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(mainView) + + mainView.snp.makeConstraints { make in + make.edges.equalTo(contentView) + } + + let widthConstraint = mainView.widthAnchor.constraint(equalToConstant: 100) + widthConstraint.priority = .defaultHigh + widthConstraint.isActive = true + self.widthConstraint = widthConstraint + + let heightConstraint = mainView.heightAnchor.constraint(equalToConstant: 100) + heightConstraint.priority = .defaultHigh + heightConstraint.isActive = true + self.heightConstraint = heightConstraint + + // Ecosia: Show history instead of synced tabs + Item.allCases.forEach { item in + let view = NTPLibraryShortcutView() + view.button.setImage(item.image, for: .normal) + view.button.tag = item.rawValue + view.button.addTarget(self, action: #selector(tapped), for: .primaryActionTriggered) + view.title.text = item.title + let words = view.title.text?.components(separatedBy: NSCharacterSet.whitespacesAndNewlines).count + view.title.numberOfLines = words == 1 ? 1 : 2 + view.accessibilityLabel = item.title + view.isAccessibilityElement = true + view.shouldGroupAccessibilityChildren = true + view.accessibilityTraits = .button + view.accessibilityRespondsToUserInteraction = true + mainView.addArrangedSubview(view) + shortcuts.append(view) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func applyTheme(theme: Theme) { + shortcuts.forEach { item in + item.applyTheme(theme: theme) + } + } + + @objc func tapped(_ sender: UIButton) { + guard let item = Item(rawValue: sender.tag) else { return } + Analytics.shared.ntpLibraryItem(.click, property: item.analyticsProperty) + switch item { + case .bookmarks: + delegate?.libraryCellOpenBookmarks() + case .history: + delegate?.libraryCellOpenHistory() + case .readingList: + delegate?.libraryCellOpenReadlist() + case .downloads: + delegate?.libraryCellOpenDownloads() + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibraryShortcutView.swift b/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibraryShortcutView.swift new file mode 100644 index 0000000000000..d3e17fab0f3b8 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Library/NTPLibraryShortcutView.swift @@ -0,0 +1,61 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ +import UIKit +import Common + +class NTPLibraryShortcutView: UIView, ThemeApplicable { + static let spacing: CGFloat = 14 + static let iconSize: CGFloat = 52 + static let titleMargin: CGFloat = 2 + static let margin: CGFloat = 10 + static let titleButtonMargin: CGFloat = 4 + + var button = UIButton() + var title = UILabel() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(button) + addSubview(title) + + title.allowsDefaultTighteningForTruncation = true + title.lineBreakMode = .byTruncatingTail + title.font = .preferredFont(forTextStyle: .footnote) + title.adjustsFontForContentSizeCategory = true + title.textAlignment = .center + title.numberOfLines = 2 + title.setContentHuggingPriority(.required, for: .vertical) + title.setContentCompressionResistancePriority(.required, for: .vertical) + title.translatesAutoresizingMaskIntoConstraints = false + + button.imageView?.contentMode = .scaleToFill + button.layer.cornerRadius = Self.iconSize/2.0 + button.contentVerticalAlignment = .fill + button.contentHorizontalAlignment = .fill + button.imageEdgeInsets = UIEdgeInsets(equalInset: Self.spacing) + button.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + button.topAnchor.constraint(equalTo: topAnchor, constant: Self.margin), + button.centerXAnchor.constraint(equalTo: centerXAnchor), + button.widthAnchor.constraint(equalToConstant: Self.iconSize), + button.heightAnchor.constraint(equalToConstant: Self.iconSize), + title.topAnchor.constraint(equalTo: button.bottomAnchor, constant: Self.titleButtonMargin), + title.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -Self.margin), + title.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Self.titleMargin), + title.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Self.titleMargin), + title.heightAnchor.constraint(equalToConstant: 10).priority(.defaultHigh) + ]) + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func applyTheme(theme: Theme) { + title.textColor = theme.colors.ecosia.textPrimary + button.tintColor = theme.colors.ecosia.buttonContentSecondary + button.backgroundColor = theme.colors.ecosia.backgroundElevation1 + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/Logo/NTPLogoCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/Logo/NTPLogoCell.swift new file mode 100644 index 0000000000000..1142bf6067e5f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/Logo/NTPLogoCell.swift @@ -0,0 +1,52 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +final class NTPLogoCell: UICollectionViewCell, ReusableCell, ThemeApplicable { + static let bottomMargin: CGFloat = 6 + static let width: CGFloat = 144 + + private weak var logo: UIImageView! + + // MARK: - Init + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + private func setup() { + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + + let logo = UIImageView(image: .init(named: "ecosiaLogoLaunch")?.withRenderingMode(.alwaysTemplate)) + logo.translatesAutoresizingMaskIntoConstraints = false + logo.clipsToBounds = true + logo.contentMode = .scaleAspectFit + logo.isAccessibilityElement = true + logo.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.logo + logo.accessibilityLabel = .localized(.ecosiaLogoAccessibilityLabel) + contentView.addSubview(logo) + self.logo = logo + + let bottom = logo.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Self.bottomMargin) + bottom.priority = .defaultHigh + bottom.isActive = true + + logo.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true + logo.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true + logo.widthAnchor.constraint(equalToConstant: Self.width).isActive = true + } + + func applyTheme(theme: Theme) { + logo.tintColor = theme.colors.ecosia.brandPrimary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/NTPLayout.swift b/firefox-ios/Client/Ecosia/UI/NTP/NTPLayout.swift new file mode 100644 index 0000000000000..81fd8dcb2335a --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/NTPLayout.swift @@ -0,0 +1,61 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Shared +import Ecosia + +protocol NTPLayoutHighlightDataSource: AnyObject { + func getSectionViewModel(shownSection: Int) -> HomepageViewModelProtocol? +} + +class NTPLayout: UICollectionViewCompositionalLayout { + weak var highlightDataSource: NTPLayoutHighlightDataSource? + + override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { + let attr = super.layoutAttributesForElements(in: rect) + adjustImpactTooltipFrame(attr: attr) + return attr + } + + private func adjustImpactTooltipFrame(attr: [UICollectionViewLayoutAttributes]?) { + guard let highlight = NTPTooltip.highlight(), let impact = attr?.first(where: { + $0.isCell && $0.isImpactSection(dataSource: highlightDataSource) + }), let tooltip = attr?.first(where: { + $0.isHeader && $0.isImpactSection(dataSource: highlightDataSource) + }) else { return } + + let font = UIFont.preferredFont(forTextStyle: .callout) + let width = impact.bounds.width - 4 * NTPTooltip.UX.margin + let height = highlight.text.height(constrainedTo: width, using: font) + 2 * NTPTooltip.UX.containerMargin + NTPTooltip.UX.margin + + tooltip.frame = impact.frame + tooltip.frame.size.height = height + tooltip.frame.origin.y -= (height) + tooltip.alpha = 1 + } +} + +extension UICollectionViewLayoutAttributes { + fileprivate var isCell: Bool { + self.representedElementCategory == .cell + } + + fileprivate var isHeader: Bool { + self.representedElementCategory == .supplementaryView && + self.representedElementKind == UICollectionView.elementKindSectionHeader + } + + fileprivate func isImpactSection(dataSource: NTPLayoutHighlightDataSource?) -> Bool { + dataSource?.getSectionViewModel(shownSection: self.indexPath.section)?.sectionType == .impact + } +} + +extension String { + fileprivate func height(constrainedTo width: CGFloat, using font: UIFont) -> CGFloat { + let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude) + let boundingBox = self.boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedString.Key.font: font], context: nil) + return boundingBox.height + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltip.Highlight.swift b/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltip.Highlight.swift new file mode 100644 index 0000000000000..0e7a2bf6114ee --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltip.Highlight.swift @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Ecosia + +extension NTPTooltip { + enum Highlight { + case gotClaimed + case successfulInvite + case referralSpotlight + case collectiveImpactIntro + + var text: String { + switch self { + case .gotClaimed: + return .localized(.youveContributed) + case .successfulInvite: + let highlight: String + let count = User.shared.referrals.newClaims + if count <= 1 { + highlight = .localized(.referralAccepted) + } else { + highlight = .init(format: .localized(.referralsAccepted), "\(count)", "\(count)") + } + return highlight + case .referralSpotlight: + return .localized(.togetherWeCan) + case .collectiveImpactIntro: + return .localized(.seeTheCollectiveImpact) + } + } + } + + class func highlight(for user: User = User.shared) -> NTPTooltip.Highlight? { + guard !user.firstTime else { return nil } + + if user.referrals.isNewClaim { + return .gotClaimed + } + + if user.referrals.newClaims > 0 { + return .successfulInvite + } + + if user.showsReferralSpotlight { + return .referralSpotlight + } + + if user.shouldShowImpactIntro { + return .collectiveImpactIntro + } + + return nil + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltip.swift b/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltip.swift new file mode 100644 index 0000000000000..c3433aadd7f36 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltip.swift @@ -0,0 +1,197 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class NTPTooltip: UICollectionReusableView, ThemeApplicable { + enum TailPosition { + case leading, center + } + struct UX { + static let margin: CGFloat = 16 + static let containerMargin: CGFloat = 12 + } + + static let key = String(describing: NTPTooltip.self) + private weak var textLabel: UILabel! + private weak var tail: UIImageView! + private weak var closeButton: UIButton! + private weak var background: UIView! + weak var delegate: NTPTooltipDelegate? + + private var tailLeadingConstraint: NSLayoutConstraint! + private var tailCenterConstraint: NSLayoutConstraint! + var tailPosition: TailPosition = .center { + didSet { + updateTailPosition() + } + } + + private let linkButton: UIButton = { + let button = UIButton() + button.titleLabel?.font = .preferredFont(forTextStyle: .callout) + button.isHidden = true + return button + }() + + // MARK: - Themeable Properties + + var themeManager: ThemeManager { AppContainer.shared.resolve() } + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + + // MARK: - Init + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { + // Empty override required to prevent UICollectionViewRecursion on NTPLayout.adjustImpactTooltipFrame + return layoutAttributes + } + + private func commonInit() { + translatesAutoresizingMaskIntoConstraints = false + let background = UIView() + background.translatesAutoresizingMaskIntoConstraints = false + addSubview(background) + self.background = background + + background.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true + background.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true + background.topAnchor.constraint(equalTo: topAnchor).isActive = true + background.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -UX.margin, priority: .init(999)).isActive = true + + let stack = UIStackView() + stack.translatesAutoresizingMaskIntoConstraints = false + stack.alignment = .center + stack.axis = .horizontal + stack.spacing = 12 + stack.distribution = .fill + + let verticalStack = UIStackView() + verticalStack.axis = .vertical + verticalStack.alignment = .leading + + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.font = .preferredFont(forTextStyle: .callout) + label.adjustsFontForContentSizeCategory = true + label.setContentCompressionResistancePriority(.defaultHigh, for: .vertical) + label.numberOfLines = 0 + verticalStack.addArrangedSubview(label) + self.textLabel = label + + linkButton.addTarget(self, action: #selector(linkButtonTapped), for: .touchDown) + verticalStack.addArrangedSubview(linkButton) + + stack.addArrangedSubview(verticalStack) + + let closeButton = UIButton() + closeButton.setImage(.templateImageNamed("crossLarge"), for: .normal) + closeButton.heightAnchor.constraint(equalToConstant: 24).isActive = true + closeButton.widthAnchor.constraint(equalToConstant: 24).isActive = true + closeButton.addTarget(self, action: #selector(closeTapped), for: .touchDown) + stack.addArrangedSubview(closeButton) + self.closeButton = closeButton + + addSubview(stack) + stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: UX.margin).isActive = true + let trailing = stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -UX.margin) + trailing.priority = .init(rawValue: 999) + trailing.isActive = true + stack.topAnchor.constraint(equalTo: topAnchor, constant: UX.containerMargin).isActive = true + stack.bottomAnchor.constraint(equalTo: background.bottomAnchor, constant: -UX.containerMargin).isActive = true + + let tail = UIImageView(image: .init(named: "tail")) + tail.translatesAutoresizingMaskIntoConstraints = false + tail.contentMode = .scaleAspectFit + tail.setContentHuggingPriority(.required, for: .horizontal) + tail.setContentHuggingPriority(.required, for: .vertical) + addSubview(tail) + self.tail = tail + + tailCenterConstraint = tail.centerXAnchor.constraint(equalTo: centerXAnchor) + tailLeadingConstraint = tail.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16) + updateTailPosition() + + tail.topAnchor.constraint(equalTo: background.bottomAnchor, constant: -0.5).isActive = true + tail.widthAnchor.constraint(equalToConstant: 28).isActive = true + + let tap = UITapGestureRecognizer(target: self, action: #selector(tapped)) + addGestureRecognizer(tap) + + addShadows() + } + + func setText(_ text: String) { + textLabel.text = text + accessibilityLabel = text + accessibilityTraits = .button + isAccessibilityElement = true + } + + func removeLink() { + linkButton.setTitle(nil, for: .normal) + linkButton.isHidden = true + } + + func setLinkTitle(_ text: String, theme: Theme) { + let titleString = NSMutableAttributedString(string: text) + titleString.addAttributes([ + .font: UIFont.preferredFont(forTextStyle: .callout).bold(), + .foregroundColor: theme.colors.ecosia.textInversePrimary + ], range: NSRange(location: 0, length: text.count)) + linkButton.setAttributedTitle(titleString, for: .normal) + linkButton.isHidden = false + } + + private func addShadows() { + [background, tail].forEach { + $0?.layer.cornerRadius = 8 + $0?.layer.shadowColor = UIColor(red: 0.059, green: 0.059, blue: 0.059, alpha: 0.18).cgColor + $0?.layer.shadowOpacity = 1 + $0?.layer.shadowRadius = 2 + $0?.layer.shadowOffset = CGSize(width: 0, height: 1) + } + } + + func applyTheme(theme: Theme) { + tail.tintColor = theme.colors.ecosia.buttonBackgroundPrimaryActive + background.backgroundColor = theme.colors.ecosia.buttonBackgroundPrimaryActive + textLabel.textColor = theme.colors.ecosia.textInversePrimary + closeButton.tintColor = theme.colors.ecosia.textInversePrimary + } + + @objc func tapped() { + delegate?.ntpTooltipTapped(self) + } + + @objc private func closeTapped() { + delegate?.ntpTooltipCloseTapped(self) + } + + @objc private func linkButtonTapped() { + delegate?.ntpTooltipLinkTapped(self) + } + + private func updateTailPosition() { + switch tailPosition { + case .center: + tailLeadingConstraint.isActive = false + tailCenterConstraint.isActive = true + case .leading: + tailCenterConstraint.isActive = false + tailLeadingConstraint.isActive = true + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltipDelegate.swift b/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltipDelegate.swift new file mode 100644 index 0000000000000..3843492ddc148 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/NTPTooltipDelegate.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol NTPTooltipDelegate: AnyObject { + func ntpTooltipTapped(_ tooltip: NTPTooltip?) + func ntpTooltipCloseTapped(_ tooltip: NTPTooltip?) + func ntpTooltipLinkTapped(_ tooltip: NTPTooltip?) + func reloadTooltip() +} + +extension NTPTooltipDelegate { + func reloadTooltip() {} + func ntpTooltipLinkTapped(_ tooltip: NTPTooltip?) {} +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/News/NTPNewsCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/News/NTPNewsCell.swift new file mode 100644 index 0000000000000..770800c69a01d --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/News/NTPNewsCell.swift @@ -0,0 +1,225 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Ecosia +import UIKit +import Common + +final class NTPNewsCell: UICollectionViewCell, ThemeApplicable, ReusableCell { + private var imageUrl: URL? + private lazy var background: UIView = { + let background = UIView() + background.translatesAutoresizingMaskIntoConstraints = false + background.layer.cornerRadius = 10 + return background + }() + private lazy var border: UIView = { + let border = UIView() + border.translatesAutoresizingMaskIntoConstraints = false + border.isUserInteractionEnabled = false + return border + }() + private lazy var placeholder: UIImageView = { + let placeholder = UIImageView() + placeholder.translatesAutoresizingMaskIntoConstraints = false + placeholder.contentMode = .scaleAspectFill + placeholder.clipsToBounds = true + placeholder.image = UIImage(named: "image_placeholder")! + placeholder.layer.cornerRadius = 10 + return placeholder + }() + private lazy var image: UIImageView = { + let image = UIImageView() + image.translatesAutoresizingMaskIntoConstraints = false + image.contentMode = .scaleAspectFill + image.clipsToBounds = true + image.alpha = 0 + image.layer.cornerRadius = 10 + return image + }() + private lazy var title: UILabel = { + let title = UILabel() + title.translatesAutoresizingMaskIntoConstraints = false + title.numberOfLines = 4 + title.lineBreakMode = .byTruncatingTail + title.font = .preferredFont(forTextStyle: .body) + title.setContentHuggingPriority(.defaultHigh, for: .vertical) + title.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + title.adjustsFontForContentSizeCategory = true + title.adjustsFontSizeToFitWidth = true + return title + }() + private lazy var bottomLine: UIStackView = { + let bottomLine = UIStackView() + bottomLine.translatesAutoresizingMaskIntoConstraints = false + bottomLine.distribution = .fill + bottomLine.axis = .horizontal + bottomLine.spacing = 4 + bottomLine.setContentHuggingPriority(.defaultLow, for: .horizontal) + return bottomLine + }() + private lazy var bottomIcon: UIImageView = { + let bottomIcon = UIImageView() + bottomIcon.translatesAutoresizingMaskIntoConstraints = false + bottomIcon.contentMode = .scaleAspectFill + bottomIcon.clipsToBounds = true + return bottomIcon + }() + private lazy var highlightLabel: UILabel = { + let highlightLabel = UILabel() + highlightLabel.translatesAutoresizingMaskIntoConstraints = false + highlightLabel.font = .preferredFont(forTextStyle: .footnote).bold() + highlightLabel.adjustsFontForContentSizeCategory = true + highlightLabel.numberOfLines = 1 + highlightLabel.textAlignment = .left + highlightLabel.setContentCompressionResistancePriority(.required, for: .vertical) + highlightLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) + highlightLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal) + return highlightLabel + }() + private lazy var bottomLabel: UILabel = { + let bottomLabel = UILabel() + bottomLabel.translatesAutoresizingMaskIntoConstraints = false + bottomLabel.font = .preferredFont(forTextStyle: .footnote) + bottomLabel.adjustsFontForContentSizeCategory = true + bottomLabel.numberOfLines = 1 + bottomLabel.textAlignment = .left + bottomLabel.setContentCompressionResistancePriority(.required, for: .vertical) + bottomLabel.setContentHuggingPriority(.defaultHigh, for: .vertical) + bottomLabel.setContentHuggingPriority(.defaultLow, for: .horizontal) + return bottomLabel + }() + var defaultBackgroundColor: UIColor = .clear + var selectedBackgroundColor: UIColor = .clear + + // MARK: - Init + + required init?(coder: NSCoder) { nil } + + override init(frame: CGRect) { + super.init(frame: frame) + let container = UIView() + container.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(container) + contentView.addSubview(background) + background.addSubview(border) + background.addSubview(placeholder) + background.addSubview(image) + background.addSubview(title) + background.addSubview(bottomLine) + bottomLine.addArrangedSubview(bottomIcon) + bottomLine.addArrangedSubview(highlightLabel) + bottomLine.addArrangedSubview(bottomLabel) + + container.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true + container.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true + container.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true + container.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true + + background.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor).isActive = true + background.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor).isActive = true + background.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true + background.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true + + placeholder.topAnchor.constraint(equalTo: image.topAnchor).isActive = true + placeholder.bottomAnchor.constraint(equalTo: image.bottomAnchor).isActive = true + placeholder.leftAnchor.constraint(equalTo: image.leftAnchor).isActive = true + placeholder.rightAnchor.constraint(equalTo: image.rightAnchor).isActive = true + + image.rightAnchor.constraint(equalTo: background.rightAnchor, constant: -16).isActive = true + + let imageHeight = image.widthAnchor.constraint(equalToConstant: 80) + imageHeight.priority = .init(999) + imageHeight.isActive = true + + image.heightAnchor.constraint(equalTo: image.widthAnchor).isActive = true + image.topAnchor.constraint(equalTo: background.topAnchor, constant: 16).isActive = true + image.bottomAnchor.constraint(lessThanOrEqualTo: background.bottomAnchor, constant: -16).isActive = true + + title.leftAnchor.constraint(equalTo: background.leftAnchor, constant: 16).isActive = true + title.rightAnchor.constraint(lessThanOrEqualTo: image.leftAnchor, constant: -16).isActive = true + title.topAnchor.constraint(equalTo: background.topAnchor, constant: 16).isActive = true + title.bottomAnchor.constraint(lessThanOrEqualTo: bottomLine.topAnchor, constant: -12).isActive = true + + let squeeze = title.bottomAnchor.constraint(equalTo: bottomLine.topAnchor, constant: -12) + squeeze.priority = .init(700) + squeeze.isActive = true + + bottomLine.leftAnchor.constraint(equalTo: background.leftAnchor, constant: 16).isActive = true + bottomLine.bottomAnchor.constraint(equalTo: background.bottomAnchor, constant: -16).isActive = true + bottomLine.rightAnchor.constraint(equalTo: background.rightAnchor, constant: -16).isActive = true + + bottomIcon.heightAnchor.constraint(equalTo: bottomIcon.widthAnchor).isActive = true + bottomIcon.widthAnchor.constraint(equalToConstant: 24).isActive = true + + border.leftAnchor.constraint(equalTo: background.leftAnchor, constant: 16).isActive = true + border.rightAnchor.constraint(equalTo: background.rightAnchor, constant: -16).isActive = true + border.bottomAnchor.constraint(equalTo: background.bottomAnchor).isActive = true + border.heightAnchor.constraint(equalToConstant: 1).isActive = true + } + + override var isSelected: Bool { + didSet { + hover() + } + } + + override var isHighlighted: Bool { + didSet { + hover() + } + } + + func configure(_ model: NewsModel, images: Images, row: Int, totalCount: Int) { + let titleString = model.text.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression) + title.text = titleString + title.accessibilityIdentifier = "news_item_title_\(row)" + let publishDateString = RelativeDateTimeFormatter().localizedString(for: model.publishDate, relativeTo: .init()) + bottomLabel.text = publishDateString + bottomLabel.accessibilityIdentifier = "news_item_published_date_\(row)" + bottomIcon.isHidden = true + highlightLabel.isHidden = true + + imageUrl = model.imageUrl + image.image = nil + image.accessibilityIdentifier = "news_item_image_\(row)" + images.load(self, url: model.imageUrl) { [weak self] in + guard self?.imageUrl == $0.url else { return } + self?.updateImage($0.data) + } + + border.isHidden = row == totalCount - 1 + + background.setMaskedCornersUsingPosition(row: row, totalCount: totalCount) + + isAccessibilityElement = true + accessibilityIdentifier = "news_item" + accessibilityLabel = "\(titleString); \(publishDateString)" + accessibilityTraits = .link + shouldGroupAccessibilityChildren = true + } + + private func updateImage(_ data: Data) { + image.image = .init(data: data) + UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: { [weak self] in + self?.image.alpha = 1 + }) + } + + private func hover() { + background.backgroundColor = isSelected || isHighlighted ? selectedBackgroundColor : defaultBackgroundColor + } + + func applyTheme(theme: Theme) { + defaultBackgroundColor = theme.colors.ecosia.backgroundElevation1 + selectedBackgroundColor = theme.colors.ecosia.backgroundQuaternary + background.backgroundColor = defaultBackgroundColor + placeholder.tintColor = theme.colors.ecosia.iconDecorative + placeholder.backgroundColor = theme.colors.ecosia.backgroundQuaternary + border.backgroundColor = theme.colors.ecosia.borderDecorative + title.textColor = theme.colors.ecosia.textPrimary + bottomLabel.textColor = theme.colors.ecosia.textSecondary + highlightLabel.textColor = theme.colors.ecosia.textSecondary + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/News/NTPNewsCellViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/News/NTPNewsCellViewModel.swift new file mode 100644 index 0000000000000..390d81c207540 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/News/NTPNewsCellViewModel.swift @@ -0,0 +1,111 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Common +import Ecosia + +protocol NTPNewsCellDelegate: AnyObject { + func openSeeAllNews() +} + +final class NTPNewsCellViewModel { + private let news = News() + private(set) var items = [NewsModel]() + private let images = Images(.init(configuration: .ephemeral)) + weak var delegate: NTPNewsCellDelegate? + weak var dataModelDelegate: HomepageDataModelDelegate? + var theme: Theme + + init(theme: Theme) { + self.theme = theme + news.subscribeAndReceive(self) { [weak self] in + guard let self = self else { return } + self.items = $0 + self.dataModelDelegate?.reloadView() + } + } +} + +// MARK: HomeViewModelProtocol +extension NTPNewsCellViewModel: HomepageViewModelProtocol { + + func setTheme(theme: Theme) { + self.theme = theme + } + + var isEnabled: Bool { + User.shared.showEcosiaNews + } + + var sectionType: HomepageSectionType { + return .news + } + + var headerViewModel: LabelButtonHeaderViewModel { + .init(title: .localized(.ecosiaNews), + titleA11yIdentifier: "ecosia_news", + isButtonHidden: false, + buttonTitle: .localized(.seeAll), + buttonAction: { [weak self] _ in self?.delegate?.openSeeAllNews() + }, + buttonA11yIdentifier: "see_all") + } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(100.0)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(300.0)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1) + + let section = NSCollectionLayoutSection(group: group) + + section.contentInsets = sectionType.sectionInsets(traitCollection) + + let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(100.0)) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: size, + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .top) + section.boundarySupplementaryItems = [header] + return section + } + + func numberOfItemsInSection() -> Int { + return min(3, items.count) + } + + var hasData: Bool { + numberOfItemsInSection() > 0 + } + + func refreshData(for traitCollection: UITraitCollection, size: CGSize, isPortrait: Bool, device: UIUserInterfaceIdiom) { + news.load(session: .shared, force: !hasData) + } +} + +extension NTPNewsCellViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = cell as? NTPNewsCell else { return UICollectionViewCell() } + let itemCount = numberOfItemsInSection() + cell.configure(items[indexPath.row], images: images, row: indexPath.row, totalCount: itemCount) + cell.applyTheme(theme: theme) + return cell + } + + func didSelectItem(at indexPath: IndexPath, homePanelDelegate: HomePanelDelegate?, libraryPanelDelegate: LibraryPanelDelegate?) { + + let index = indexPath.row + guard index >= 0, items.count > index else { return } + let item = items[index] + homePanelDelegate?.homePanel(didSelectURL: item.targetUrl, visitType: .link, isGoogleTopSite: false) + Analytics.shared.navigationOpenNews(item.trackingName) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/News/NewsController.swift b/firefox-ios/Client/Ecosia/UI/NTP/News/NewsController.swift new file mode 100644 index 0000000000000..31b0f148a247f --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/News/NewsController.swift @@ -0,0 +1,229 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import Ecosia + +final class NewsController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, + UICollectionViewDelegateFlowLayout, Themeable { + + private(set) weak var collection: UICollectionView! + private var items = [NewsModel]() + private let images = Images(.init(configuration: .ephemeral)) + private let news = News() + private let identifier = "news" + var delegate: SharedHomepageCellDelegate? + + // MARK: - Themeable Properties + + let windowUUID: WindowUUID + var currentWindowUUID: Common.WindowUUID? { return windowUUID } + var themeManager: ThemeManager + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + + // MARK: - Init + + required init?(coder: NSCoder) { nil } + + init(items: [NewsModel], + windowUUID: WindowUUID, + themeManager: ThemeManager = AppContainer.shared.resolve()) { + self.windowUUID = windowUUID + self.themeManager = themeManager + super.init(nibName: nil, bundle: nil) + self.items = items + title = .localized(.ecosiaNews) + navigationItem.largeTitleDisplayMode = .always + } + + override func loadView() { + let indicator = UIActivityIndicatorView(style: .medium) + indicator.startAnimating() + + let collection = UICollectionView(frame: .zero, collectionViewLayout: createLayout()) + collection.delegate = self + collection.dataSource = self + collection.register(NTPNewsCell.self, forCellWithReuseIdentifier: identifier) + collection.register(NewsSubHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: identifier) + collection.backgroundView = indicator + collection.contentInsetAdjustmentBehavior = .scrollableAxes + self.collection = collection + view = collection + } + + func createLayout() -> UICollectionViewLayout { + + let layout = UICollectionViewCompositionalLayout { [weak self] (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in + + guard let self = self else { return nil } + + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(100)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), + heightDimension: .estimated(100)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1) + + let section = NSCollectionLayoutSection(group: group) + + let horizontal = (self.collection.bounds.width - self.collection.maxWidth) / 2 + section.contentInsets = NSDirectionalEdgeInsets( + top: 0, + leading: horizontal, + bottom: 0, + trailing: horizontal) + + let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(100.0)) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: size, + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .top) + section.boundarySupplementaryItems = [header] + return section + } + return layout + } + + override func viewDidLoad() { + super.viewDidLoad() + news.subscribeAndReceive(self) { [weak self] in + self?.items = $0 + self?.collection.reloadData() + self?.collection.backgroundView = nil + } + + let done = UIBarButtonItem(barButtonSystemItem: .done) { [ weak self ] _ in + self?.dismiss(animated: true, completion: nil) + } + navigationItem.rightBarButtonItem = done + + applyTheme() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + news.load(session: .shared) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Analytics.shared.navigation(.view, label: .news) + } + + override func viewWillTransition(to: CGSize, with: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: to, with: with) + collection?.reloadData() + collection?.collectionViewLayout.invalidateLayout() + } + + func collectionView(_: UICollectionView, numberOfItemsInSection: Int) -> Int { + items.count + } + + func collectionView(_: UICollectionView, viewForSupplementaryElementOfKind kind: String, at: IndexPath) -> UICollectionReusableView { + let header = collection.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: identifier, for: at) + return header + } + + func collectionView(_: UICollectionView, cellForItemAt: IndexPath) -> UICollectionViewCell { + let cell = collection.dequeueReusableCell(withReuseIdentifier: identifier, for: cellForItemAt) as! NTPNewsCell + cell.configure(items[cellForItemAt.row], images: images, row: cellForItemAt.item, totalCount: items.count) + cell.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + return cell + } + + func collectionView(_: UICollectionView, didSelectItemAt: IndexPath) { + let item = items[didSelectItemAt.row] + delegate?.openLink(url: item.targetUrl) + dismiss(animated: true, completion: nil) + Analytics.shared.navigationOpenNews(item.trackingName) + } + + func applyTheme() { + let theme = themeManager.getCurrentTheme(for: windowUUID) + collection.visibleSupplementaryViews(ofKind: UICollectionView.elementKindSectionHeader).forEach({ + ($0 as? Themeable)?.applyTheme() + ($0 as? ThemeApplicable)?.applyTheme(theme: theme) + }) + collection.visibleCells.forEach({ + ($0 as? Themeable)?.applyTheme() + ($0 as? ThemeApplicable)?.applyTheme(theme: theme) + }) + collection.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + updateBarAppearance() + + if traitCollection.userInterfaceIdiom == .pad { + let margin = max((view.bounds.width - 544) / 2.0, 0) + additionalSafeAreaInsets = .init(top: 0, left: margin, bottom: 0, right: margin) + } + } + + private func updateBarAppearance() { + guard let appearance = navigationController?.navigationBar.standardAppearance else { return } + let theme = themeManager.getCurrentTheme(for: windowUUID) + appearance.largeTitleTextAttributes = [.foregroundColor: theme.colors.ecosia.textPrimary] + appearance.titleTextAttributes = [.foregroundColor: theme.colors.ecosia.textPrimary] + appearance.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + navigationItem.standardAppearance = appearance + navigationController?.navigationBar.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + navigationController?.navigationBar.tintColor = theme.colors.ecosia.brandPrimary + } + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + applyTheme() + } +} + +private final class NewsSubHeader: UICollectionReusableView, ThemeApplicable { + + // MARK: - Properties + + private weak var subtitle: UILabel! + + required init?(coder: NSCoder) { nil } + + override init(frame: CGRect) { + super.init(frame: frame) + isUserInteractionEnabled = false + + let subtitle = UILabel() + subtitle.translatesAutoresizingMaskIntoConstraints = false + subtitle.font = .preferredFont(forTextStyle: .body) + subtitle.adjustsFontForContentSizeCategory = true + subtitle.numberOfLines = 0 + subtitle.text = .localized(.keepUpToDate) + addSubview(subtitle) + self.subtitle = subtitle + + subtitle.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true + subtitle.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16).isActive = true + subtitle.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + subtitle.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + } + + func applyTheme(theme: Theme) { + backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + subtitle.textColor = theme.colors.ecosia.textSecondary + } +} + +extension UICollectionView { + fileprivate var maxWidth: CGFloat { + let insets = max(max(safeAreaInsets.left, safeAreaInsets.right), 16) * 2 + let maxWidth = bounds.width - insets + + if traitCollection.userInterfaceIdiom == .pad { + return min(maxWidth, 544) + } else if traitCollection.verticalSizeClass == .compact { + return min(maxWidth, 375) + } else { + return maxWidth + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/NudgeCards/NTPConfigurableNudgeCardCell.swift b/firefox-ios/Client/Ecosia/UI/NTP/NudgeCards/NTPConfigurableNudgeCardCell.swift new file mode 100644 index 0000000000000..8f98b118cfe58 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/NudgeCards/NTPConfigurableNudgeCardCell.swift @@ -0,0 +1,92 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SwiftUI +import Common +import Ecosia + +/// Reusable Nudge Card Cell that can be configured with any view model. +class NTPConfigurableNudgeCardCell: UICollectionViewCell, ThemeApplicable, ReusableCell { + + // MARK: - Properties + + private var viewModel: NTPConfigurableNudgeCardCellViewModel? + weak var delegate: NTPConfigurableNudgeCardCellDelegate? + var theme: Theme! + private var hostingController: UIHostingController? + + // MARK: - Initializer + + override init(frame: CGRect) { + super.init(frame: frame) + setupHostingController() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setup + + private func setupHostingController() { + let view = ConfigurableNudgeCardView() + let controller = UIHostingController(rootView: view) + controller.view.translatesAutoresizingMaskIntoConstraints = false + controller.view.backgroundColor = .clear + + contentView.addSubview(controller.view) + + NSLayoutConstraint.activate([ + controller.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + controller.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + controller.view.topAnchor.constraint(equalTo: contentView.topAnchor), + controller.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + ]) + + hostingController = controller + } + + // MARK: - Configuration Method + + /// Configures the Nudge Card Cell using the ViewModel. + func configure(with viewModel: NTPConfigurableNudgeCardCellViewModel, theme: Theme?) { + self.viewModel = viewModel + self.theme = theme + delegate = viewModel.delegate + guard let theme else { return } + let nudgeCardStyle = NudgeCardStyle(backgroundColor: theme.colors.ecosia.backgroundSecondary.color, + textPrimaryColor: theme.colors.ecosia.textPrimary.color, + textSecondaryColor: theme.colors.ecosia.textSecondary.color, + closeButtonTextColor: theme.colors.ecosia.buttonContentSecondary.color, + actionButtonTextColor: theme.colors.ecosia.buttonBackgroundPrimary.color) + let configurableCardViewModel = NudgeCardViewModel(title: viewModel.title, + description: viewModel.description, + buttonText: viewModel.buttonText, + image: viewModel.image, + style: nudgeCardStyle) + hostingController?.rootView = ConfigurableNudgeCardView(viewModel: configurableCardViewModel, delegate: self) + } + + // MARK: - Theming + func applyTheme(theme: Theme) { + guard let viewModel else { return } + configure(with: viewModel, theme: theme) + } +} + +extension NTPConfigurableNudgeCardCell: ConfigurableNudgeCardActionDelegate { + + func nudgeCardRequestToPerformAction() { + guard let sectionType = viewModel?.sectionType else { return } + delegate?.nudgeCardRequestToPerformAction(for: sectionType) + } + + func nudgeCardRequestToDimiss() { + guard let sectionType = viewModel?.sectionType else { return } + delegate?.nudgeCardRequestToDimiss(for: sectionType) + } + + func nudgeCardTapped() {} +} diff --git a/firefox-ios/Client/Ecosia/UI/NTP/NudgeCards/NTPConfigurableNudgeCardCellViewModel.swift b/firefox-ios/Client/Ecosia/UI/NTP/NudgeCards/NTPConfigurableNudgeCardCellViewModel.swift new file mode 100644 index 0000000000000..b645d64a0ed46 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/NTP/NudgeCards/NTPConfigurableNudgeCardCellViewModel.swift @@ -0,0 +1,95 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common + +/// Delegate that forwards events to the Cell to let perform its appropriate actions. +/// The `cardType` corresponds to the section type we will always need to define for each card. +protocol NTPConfigurableNudgeCardCellDelegate: AnyObject { + func nudgeCardRequestToDimiss(for cardType: HomepageSectionType) + func nudgeCardRequestToPerformAction(for cardType: HomepageSectionType) +} + +/// ViewModel for configuring a Nudge Card Cell. +class NTPConfigurableNudgeCardCellViewModel: HomepageViewModelProtocol { + var title: String { + fatalError("Must be overridden") + } + var description: String { + fatalError("Must be overridden") + } + var buttonText: String { + fatalError("Must be overridden") + } + var cardSectionType: HomepageSectionType { + fatalError("Must be overridden") + } + var image: UIImage? { + nil + } + var showsCloseButton: Bool { + true + } + + var theme: Theme + weak var delegate: NTPConfigurableNudgeCardCellDelegate? + + /// Initializes the ViewModel with a theme. Some properties must be overriden by subclasses. + /// - Parameters: + /// - theme: The current theme for styling the card. + init(theme: Theme) { + self.theme = theme + } + + func setTheme(theme: Theme) { + self.theme = theme + } + + var sectionType: HomepageSectionType { + cardSectionType + } + + var headerViewModel: LabelButtonHeaderViewModel { + return .emptyHeader + } + + func section(for traitCollection: UITraitCollection, size: CGSize) -> NSCollectionLayoutSection { + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(200)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(200)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1) + + let section = NSCollectionLayoutSection(group: group) + + section.contentInsets = sectionType.sectionInsets(traitCollection, topSpacing: 24) + + return section + } + + func numberOfItemsInSection() -> Int { + return 1 + } + + var isEnabled: Bool { + fatalError("Needs to be implemented") + } + + func screenWasShown() { + fatalError("Needs to be implemented. Implement empty if not needed") + } +} + +extension NTPConfigurableNudgeCardCellViewModel: HomepageSectionHandler { + + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = cell as? NTPConfigurableNudgeCardCell else { + return UICollectionViewCell() + } + cell.configure(with: self, theme: theme) + return cell + } +} diff --git a/firefox-ios/Client/Ecosia/UI/PageAction/PageActionMenu.swift b/firefox-ios/Client/Ecosia/UI/PageAction/PageActionMenu.swift new file mode 100644 index 0000000000000..9369e5adcb0dc --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/PageAction/PageActionMenu.swift @@ -0,0 +1,237 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class PageActionMenu: UIViewController, UIGestureRecognizerDelegate { + + // MARK: - UX + + struct UX { + static let estimatedSectionHeaderHeight: CGFloat = 16 + static let shortcuts = "Shortcuts" + static let rowHeight: CGFloat = 50 + } + + // MARK: - Themeable Properties + + let windowUUID: WindowUUID + var currentWindowUUID: WindowUUID? { return windowUUID } + var themeManager: ThemeManager + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol = NotificationCenter.default + var currentTheme: Theme { themeManager.getCurrentTheme(for: windowUUID) } + + // MARK: - Properties + + private var tableView = UITableView(frame: .zero, style: .plain) + private var knob = UIView() + private var contentSizeObserver: NSKeyValueObservation? + private lazy var swipeDown: UISwipeGestureRecognizer = { + let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(close)) + swipeDown.direction = .down + swipeDown.isEnabled = false + swipeDown.delegate = self + view.addGestureRecognizer(swipeDown) + return swipeDown + }() + + private let viewModel: PhotonActionSheetViewModel + private weak var delegate: PageActionsShortcutsDelegate? + + // MARK: - Init + + init(viewModel: PhotonActionSheetViewModel, + delegate: PageActionsShortcutsDelegate, + windowUUID: WindowUUID, + themeManager: ThemeManager = AppContainer.shared.resolve()) { + self.viewModel = viewModel + self.delegate = delegate + self.windowUUID = windowUUID + self.themeManager = themeManager + super.init(nibName: nil, bundle: nil) + + title = viewModel.title + modalPresentationStyle = viewModel.modalStyle + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - View cycle + + override func viewDidLoad() { + super.viewDidLoad() + setupTableView() + setupKnob() + setupConstraints() + applyTheme() + listenForThemeChange(view) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + checkSwipeDown() + guard traitCollection.userInterfaceIdiom == .pad else { return } + contentSizeObserver = tableView.observe(\.contentSize) { [weak self] tableView, _ in + // Ecosia: Update height for iPad + // self?.preferredContentSize = CGSize(width: 350, height: tableView.contentSize.height) + var height = tableView.contentSize.height + if UIDevice.current.userInterfaceIdiom == .pad { + height += PhotonActionSheet.UX.bigSpacing + } + self?.preferredContentSize = CGSize(width: 350, height: height) + } + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + contentSizeObserver?.invalidate() + contentSizeObserver = nil + } +} + +// MARK: Swipe down to close in iPhone Landscape + +extension PageActionMenu { + + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + checkSwipeDown() + } + + @objc func close() { + dismiss(animated: true, completion: nil) + } +} + +// MARK: - Gestures + +extension PageActionMenu { + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + guard gestureRecognizer === swipeDown else { return false } + return tableView.contentOffset.y <= 0 + } + + private func checkSwipeDown() { + guard traitCollection.userInterfaceIdiom == .phone, + let window = UIApplication.shared.windows.first(where: \.isKeyWindow), + let orientation = window.windowScene?.interfaceOrientation else { return } + + swipeDown.isEnabled = orientation.isLandscape + } +} + +// MARK: - Setup PageActionMenu + +extension PageActionMenu { + + private func setupTableView() { + view.addSubview(tableView) + tableView.estimatedRowHeight = UX.rowHeight + tableView.translatesAutoresizingMaskIntoConstraints = false + tableView.delegate = self + tableView.dataSource = self + tableView.register(PageActionMenuCell.self, forCellReuseIdentifier: PageActionMenuCell.UX.cellIdentifier) + tableView.register(PageActionsShortcutsHeader.self, forHeaderFooterViewReuseIdentifier: UX.shortcuts) + tableView.estimatedSectionHeaderHeight = UX.estimatedSectionHeaderHeight + tableView.sectionFooterHeight = 0 + tableView.separatorStyle = .none + tableView.translatesAutoresizingMaskIntoConstraints = false + } + + private func setupKnob() { + guard traitCollection.userInterfaceIdiom == .phone else { return } + view.addSubview(knob) + knob.translatesAutoresizingMaskIntoConstraints = false + knob.layer.cornerRadius = 2 + } + + private func setupConstraints() { + NSLayoutConstraint.activate([ + tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + tableView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor), + tableView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor), + tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + ]) + + guard traitCollection.userInterfaceIdiom == .phone else { return } + + NSLayoutConstraint.activate([ + knob.topAnchor.constraint(equalTo: view.topAnchor, constant: 8), + knob.centerXAnchor.constraint(equalTo: view.centerXAnchor), + knob.widthAnchor.constraint(equalToConstant: 32), + knob.heightAnchor.constraint(equalToConstant: 4) + ]) + } +} + +// MARK: - UITableViewDataSource, UITableViewDelegate + +extension PageActionMenu: UITableViewDataSource, UITableViewDelegate { + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return UITableView.automaticDimension + } + + func numberOfSections(in tableView: UITableView) -> Int { + return viewModel.actions.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModel.actions[section].count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: PageActionMenuCell.UX.cellIdentifier, for: indexPath) as! PageActionMenuCell + cell.determineTableViewCellPositionAt(indexPath, forActions: viewModel.actions) + cell.configure(with: viewModel, at: indexPath, theme: currentTheme) + return cell + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + guard section == 0 else { + return 0 + } + return UITableView.automaticDimension + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard section == 0 else { return nil } + + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: UX.shortcuts) as! PageActionsShortcutsHeader + header.delegate = delegate + header.applyTheme(theme: currentTheme) + return header + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let actions = viewModel.actions[indexPath.section][indexPath.row] + let item = actions.items.first + dismiss(animated: true) { + if let item, let handler = item.tapHandler { + handler(item) + } + } + } +} + +// MARK: - Themeable + +extension PageActionMenu: Themeable { + + func applyTheme() { + tableView.reloadData() + view.backgroundColor = currentTheme.colors.ecosia.backgroundPrimaryDecorative + tableView.backgroundColor = currentTheme.colors.ecosia.backgroundPrimaryDecorative + tableView.separatorColor = currentTheme.colors.ecosia.borderDecorative + knob.backgroundColor = currentTheme.colors.ecosia.textSecondary + tableView.visibleCells.forEach { + ($0 as? ThemeApplicable)?.applyTheme(theme: currentTheme) + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/PageAction/PageActionMenuCell.swift b/firefox-ios/Client/Ecosia/UI/PageAction/PageActionMenuCell.swift new file mode 100644 index 0000000000000..777ae125f8709 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/PageAction/PageActionMenuCell.swift @@ -0,0 +1,246 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +final class PageActionMenuCell: UITableViewCell, ThemeApplicable { + + struct UX { + + static let cellIdentifier = String(describing: PageActionMenuCell.self) + + /// The corner radius to apply to the cells + /// depending on their position + static let cornerRadius: CGFloat = 10.0 + + /// The cell's left / right padding + static let padding: CGFloat = 16.0 + + /// The separator height of the `PageActionMenuCell`'s `separatorView` + static let separatorHeight: CGFloat = 1.0 + + /// This `enum` serves the purpose of checking the cells' position + /// within a section + enum Position { + /// Only one cell is rendered within a section + case solo + /// The cell is the first of its section + case first + /// The cell is between the first and last of its section + case middle + /// The cell is the last of its section + case last + } + } + + private weak var badge: UIView? + private weak var badgeLabel: UILabel? + private var separatorView: UIView? + + /// The cell's position, starting from `middle` as preferred value + private var position: UX.Position = .middle + + /// The eligible cell positions within the Table View + /// to add the separator view + private let separatorCellsPositions: [UX.Position] = [.middle, .first] + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + adjustPadding() + setTableViewCellCorners() + } + + override func prepareForReuse() { + super.prepareForReuse() + separatorView?.removeFromSuperview() + separatorView = nil + textLabel?.text = nil + imageView?.image = nil + } + + func applyTheme(theme: Theme) { + badge?.backgroundColor = theme.colors.ecosia.brandPrimary + } +} + +extension PageActionMenuCell { + + /// Determines the TableView Cell's position based at a given Index Path + /// + /// - Parameters: + /// - indexPath: The Table View's index path + /// - actions: The array of `PhotonRowActions` utilized as Data Source + @discardableResult + func determineTableViewCellPositionAt(_ indexPath: IndexPath, forActions actions: [[PhotonRowActions]]) -> UX.Position { + + if actions[indexPath.section].count == 1 { + position = .solo + } else if indexPath.row == 0 { + position = .first + } else if indexPath.row == actions[indexPath.section].count - 1 { + position = .last + } else { + position = .middle + } + + return position + } +} + +extension PageActionMenuCell { + + /// Sets the left/right padding of the Table View Cell's content view + private func adjustPadding() { + + guard let superview else { return } + + let frameWithPadding = CGRect(x: UX.padding, + y: frame.minY, + width: superview.frame.width - (UX.padding * 2), + height: frame.height) + frame = frameWithPadding + let insets = NSDirectionalEdgeInsets(top: 0, + leading: UX.padding, + bottom: 0, + trailing: UX.padding) + contentView.directionalLayoutMargins = insets + } + + /// Sets the table view cell's corners based on the `position` + private func setTableViewCellCorners() { + switch position { + case .solo: addRoundedCorners(.allCorners, radius: UX.cornerRadius) + case .first: addRoundedCorners([.topLeft, .topRight], radius: UX.cornerRadius) + case .last: addRoundedCorners([.bottomLeft, .bottomRight], radius: UX.cornerRadius) + default: noCornerMask() + } + } +} + +extension PageActionMenuCell { + + /// Reset the Corner Mask of a given `UIView` (`PageActionMenuCell`) + /// + /// Perhaps moved into a different `UIView` extension + /// Although we use this function purely to solve this Table View Cell's UI behaviour + private func noCornerMask() { + layer.mask = nil + } + + /// Adds the custom separator view + private func addCustomGroupedStyleLikeSeparator(theme: Theme) { + + if separatorView == nil { + separatorView = UIView() + } + + guard let separatorView else { return } + + separatorView.translatesAutoresizingMaskIntoConstraints = false + separatorView.backgroundColor = theme.colors.ecosia.borderDecorative + + contentView.addSubview(separatorView) + contentView.bringSubviewToFront(separatorView) + + updateSeparatorViewConstraints(separatorView) + } + + private func updateSeparatorViewConstraints(_ separatorView: UIView) { + separatorView.heightAnchor.constraint(equalToConstant: UX.separatorHeight).isActive = true + separatorView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -UX.separatorHeight).isActive = true + separatorView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -UX.padding).isActive = true + if let imageView { + separatorView.leadingAnchor.constraint(equalTo: imageView.leadingAnchor).isActive = true + } else if let textLabel { + separatorView.leadingAnchor.constraint(equalTo: textLabel.leadingAnchor).isActive = true + } + } +} + +extension PageActionMenuCell { + + /// Configures the TableView's cell + /// + /// - Parameters: + /// - viewModel: The`PhotonActionSheetViewModel`'s View Model + /// - indexPath: The TableView's index path + func configure(with viewModel: PhotonActionSheetViewModel, + at indexPath: IndexPath, + theme: Theme) { + + backgroundColor = theme.colors.ecosia.backgroundElevation1 + let actions = viewModel.actions[indexPath.section][indexPath.row] + guard let item = actions.items.first else { return } + + textLabel?.text = item.currentTitle + textLabel?.textColor = theme.colors.ecosia.textPrimary + detailTextLabel?.text = item.text + detailTextLabel?.textColor = theme.colors.ecosia.textSecondary + + accessibilityIdentifier = item.iconString ?? item.accessibilityId + accessibilityLabel = item.currentTitle + + if let iconName = item.iconString { + imageView?.image = UIImage(named: iconName)?.withRenderingMode(.alwaysTemplate) + imageView?.tintColor = theme.colors.ecosia.textSecondary + } else { + imageView?.image = nil + } + + isNew(actions.items.first?.isNew == true, theme: theme) + + if separatorCellsPositions.contains(position) { + addCustomGroupedStyleLikeSeparator(theme: theme) + } + + applyTheme(theme: theme) + } +} + +extension PageActionMenuCell { + + /// Creates the TableView Cell's `badge` based on a condition + /// + /// - Parameters: + /// - isNew: A boolean value based on which we create the `badge` view + private func isNew(_ isNew: Bool, theme: Theme) { + if isNew { + if badge == nil { + let badge = UIView() + badge.isUserInteractionEnabled = false + accessoryView = badge + + let badgeLabel = UILabel() + badgeLabel.translatesAutoresizingMaskIntoConstraints = false + badgeLabel.font = .preferredFont(forTextStyle: .footnote).bold() + badgeLabel.adjustsFontForContentSizeCategory = true + badgeLabel.text = .localized(.new) + badge.addSubview(badgeLabel) + + badgeLabel.centerXAnchor.constraint(equalTo: badge.centerXAnchor).isActive = true + badgeLabel.centerYAnchor.constraint(equalTo: badge.centerYAnchor).isActive = true + + self.badge = badge + self.badgeLabel = badgeLabel + } + + let size = badgeLabel?.sizeThatFits(.init(width: CGFloat.greatestFiniteMagnitude, height: .greatestFiniteMagnitude)) ?? .zero + let height = size.height + 5 + badge?.layer.cornerRadius = height / 2 + badge?.frame.size = .init(width: size.width + 16, height: height) + badgeLabel?.textColor = theme.colors.ecosia.textInversePrimary + } else { + accessoryView = nil + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/PageAction/PageActionsShortcutsHeader.swift b/firefox-ios/Client/Ecosia/UI/PageAction/PageActionsShortcutsHeader.swift new file mode 100644 index 0000000000000..0aa2e05198f27 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/PageAction/PageActionsShortcutsHeader.swift @@ -0,0 +1,102 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +protocol PageActionsShortcutsDelegate: AnyObject { + func pageOptionsOpenHome() + func pageOptionsNewTab() + func pageOptionsShare() + func pageOptionsSettings() +} + +class PageActionsShortcutsHeader: UITableViewHeaderFooterView, ThemeApplicable { + + var mainView = UIStackView() + weak var delegate: PageActionsShortcutsDelegate? + + struct Panel { + let title: String + let image: UIImage? + let tag: Int + } + + var shortcuts: [NTPLibraryShortcutView] = [] + + let home = Panel(title: .localized(.home), + image: UIImage(named: StandardImageIdentifiers.Large.home), + tag: 0) + let newTab = Panel(title: .LegacyAppMenu.NewTab, + image: UIImage(named: StandardImageIdentifiers.Large.plus), + tag: 1) + let share = Panel(title: .LegacyAppMenu.Share, + image: UIImage(named: StandardImageIdentifiers.Large.share), + tag: 2) + let settings = Panel(title: .LegacyAppMenu.AppMenuSettingsTitleString, + image: UIImage(named: StandardImageIdentifiers.Large.settings), + tag: 3) + + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + mainView.distribution = .fillEqually + mainView.alignment = .center + mainView.spacing = 8 + mainView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(mainView) + + let height = mainView.heightAnchor.constraint(equalToConstant: 100) + height.priority = .defaultHigh + height.isActive = true + + NSLayoutConstraint.activate([ + mainView.topAnchor.constraint(equalTo: contentView.topAnchor), + mainView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8, priority: .defaultHigh), + mainView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), + mainView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), + ]) + + // Ecosia: Custom options + [home, newTab, share, settings].forEach { item in + let view = NTPLibraryShortcutView() + view.button.layer.cornerRadius = 10 + view.button.setImage(item.image?.withRenderingMode(.alwaysTemplate), for: .normal) + view.button.tag = item.tag + view.button.addTarget(self, action: #selector(tapped), for: .primaryActionTriggered) + view.title.text = item.title + let words = view.title.text?.components(separatedBy: NSCharacterSet.whitespacesAndNewlines).count + view.title.numberOfLines = words == 1 ? 1 : 2 + view.accessibilityLabel = item.title + mainView.addArrangedSubview(view) + shortcuts.append(view) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func applyTheme(theme: Theme) { + backgroundView?.backgroundColor = .clear + + shortcuts.forEach { item in + item.applyTheme(theme: theme) + } + } + + @objc func tapped(_ sender: UIButton) { + switch sender.tag { + case 0: + delegate?.pageOptionsOpenHome() + case 1: + delegate?.pageOptionsNewTab() + case 2: + delegate?.pageOptionsShare() + case 3: + delegate?.pageOptionsSettings() + default: + break + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/ProductTour/BrowserViewController+ProductTourSpotlight.swift b/firefox-ios/Client/Ecosia/UI/ProductTour/BrowserViewController+ProductTourSpotlight.swift new file mode 100644 index 0000000000000..e7803cde7291b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/ProductTour/BrowserViewController+ProductTourSpotlight.swift @@ -0,0 +1,65 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SafariServices +import Common +import Ecosia + +// MARK: - Product Tour Spotlight Integration +extension BrowserViewController { + + // MARK: - Setup + + /// Sets up the product tour spotlight coordinator + /// Call this in viewDidLoad or similar lifecycle method + func setupProductTourSpotlightIfNeeded() { + guard ProductTourManager.shared.isInProductTour else { + return + } + + // Only create coordinator if it doesn't exist + guard spotlightCoordinator == nil else { + return + } + + makeSpotlightCoordinator() + } + + /// Updates the spotlight coordinator's theme when theme changes + /// Call this when theme changes (e.g., dark mode toggle) + func updateSpotlightThemeIfNeeded() { + guard ProductTourManager.shared.isInProductTour else { + return + } + + if let coordinator = spotlightCoordinator { + // Update theme on existing coordinator + let theme = themeManager.getCurrentTheme(for: windowUUID) + coordinator.updateTheme(theme) + } else { + // Create coordinator if it doesn't exist yet + makeSpotlightCoordinator() + } + } + + // MARK: - Private Helpers + + @discardableResult + private func makeSpotlightCoordinator() -> ProductTourSpotlightCoordinator { + let theme = themeManager.getCurrentTheme(for: windowUUID) + let coordinator = ProductTourSpotlightCoordinator( + viewController: self, + bottomContentView: bottomContentStackView, + theme: theme + ) + coordinator.openURL = { [weak self] url in + let safariVC = SFSafariViewController(url: url) + safariVC.modalPresentationStyle = .pageSheet + self?.present(safariVC, animated: true) + } + spotlightCoordinator = coordinator + return coordinator + } +} diff --git a/firefox-ios/Client/Ecosia/UI/ProductTour/BrowserViewController+WelcomeTransition.swift b/firefox-ios/Client/Ecosia/UI/ProductTour/BrowserViewController+WelcomeTransition.swift new file mode 100644 index 0000000000000..4cd5f05a2f788 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/ProductTour/BrowserViewController+WelcomeTransition.swift @@ -0,0 +1,51 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Shared + +// MARK: - Welcome Transition Animation +extension BrowserViewController { + + /// Prepares the toolbars to be animated in after welcome dismissal + /// This should be called early in the view lifecycle + func prepareToolbarsForWelcomeTransition() { + // Hide toolbars initially to prevent flash + header.alpha = 0 + bottomContainer.alpha = 0 + + // Hide content and set background color to match NTP + contentStackView.alpha = 0 + let theme = themeManager.getCurrentTheme(for: windowUUID) + // TODO: Investigate colors behind toolbars before transition + // TODO: Smooth out color transitions + view.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + } + + /// Animates the top and bottom toolbars sliding in from the edges + /// This is called after the welcome screen fades out + func animateToolbarsIn() { + let margin: CGFloat = 20 // Additional margin to ensure views are fully hidden + let topOffset = -(header.frame.height + view.safeAreaInsets.top + margin) + let bottomOffset = bottomContainer.frame.height + view.safeAreaInsets.bottom + margin + + // Set initial off-screen positions and make visible + header.alpha = 1.0 + bottomContainer.alpha = 1.0 + header.transform = CGAffineTransform(translationX: 0, y: topOffset) + bottomContainer.transform = CGAffineTransform(translationX: 0, y: bottomOffset) + + // Animate to final positions + UIView.animate( + withDuration: 0.35, + delay: 0, + options: .curveEaseInOut, + animations: { [weak self] in + self?.header.transform = .identity + self?.bottomContainer.transform = .identity + self?.contentStackView.alpha = 1 + } + ) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/ProductTour/ProductTourSpotlightCoordinator.swift b/firefox-ios/Client/Ecosia/UI/ProductTour/ProductTourSpotlightCoordinator.swift new file mode 100644 index 0000000000000..e79d1a7bc899e --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/ProductTour/ProductTourSpotlightCoordinator.swift @@ -0,0 +1,266 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import Foundation +import UIKit +import Ecosia + +/// Coordinates the display of spotlight toasts during the product tour +final class ProductTourSpotlightCoordinator: ProductTourObserver { + + // MARK: - Types + + /// Describes how the secondary button should behave (and be tracked) in a spotlight. + private enum SecondaryAction { + /// Navigate back to the previous step (multi-step spotlights). + case back + /// Skip the entire spotlight sequence. + case skip + /// Open a URL (e.g. "Read More"). + case openURL(URL) + } + + /// Groups everything the coordinator needs to present and track a spotlight sequence. + private struct SpotlightConfiguration { + /// Ordered steps in this spotlight sequence. + let steps: [SpotlightToastViewModel] + /// Analytics label used for every event in this sequence. + let analyticsLabel: Analytics.Label.Onboarding + /// Returns the secondary action for a given step index. + /// For multi-step spotlights the action typically alternates between skip/back. + let secondaryAction: (_ stepIndex: Int) -> SecondaryAction + /// Called once the spotlight sequence finishes (completed, skipped, or dismissed). + let onComplete: () -> Void + } + + // MARK: - Properties + + private weak var viewController: UIViewController? + private weak var bottomContentView: UIView? + private var currentSpotlight: SpotlightToast? + private var currentStepIndex: Int = 0 + private var currentConfiguration: SpotlightConfiguration? + private var theme: Theme + private let tourManager: ProductTourManager + + /// Closure called when the coordinator needs to open a URL in a new tab + var openURL: ((URL) -> Void)? + + /// Whether a spotlight is currently being displayed + var isShowingSpotlight: Bool { + return currentSpotlight != nil + } + + // MARK: - Spotlight Configurations + + private lazy var searchConfiguration: SpotlightConfiguration = { + SpotlightConfiguration( + steps: [ + SpotlightToastViewModel( + image: UIImage(named: "spotlightCleanEnergy"), + titleText: .localized(.serpSpotlightStep1Title), + descriptionText: .localized(.serpSpotlightStep1Description), + currentStep: 1, + totalSteps: 2, + primaryButtonText: .localized(.next), + secondaryButtonText: .localized(.skip) + ), + SpotlightToastViewModel( + image: UIImage(named: "spotlightPlanetProfits"), + titleText: .localized(.serpSpotlightStep2Title), + descriptionText: .localized(.serpSpotlightStep2Description), + currentStep: 2, + totalSteps: 2, + primaryButtonText: .localized(.gotIt), + secondaryButtonText: .localized(.goBack) + ) + ], + analyticsLabel: .serpTour, + secondaryAction: { stepIndex in + stepIndex > 0 ? .back : .skip + }, + onComplete: { [weak self] in + self?.tourManager.completeSearchSpotlight() + } + ) + }() + + private lazy var externalWebsiteConfiguration: SpotlightConfiguration = { + SpotlightConfiguration( + steps: [ + SpotlightToastViewModel( + image: nil, + titleText: .localized(.protectionSpotlightTitle), + descriptionText: .localized(.protectionSpotlightDescription), + currentStep: 1, + totalSteps: 1, + primaryButtonText: .localized(.gotIt), + secondaryButtonText: .localized(.readMore), + secondaryButtonIcon: UIImage(named: "openLink") + ) + ], + analyticsLabel: .privacyTour, + secondaryAction: { _ in + .openURL(EcosiaEnvironment.current.urlProvider.trackingProtectionHelpPage) + }, + onComplete: { [weak self] in + self?.tourManager.completeExternalWebsiteSpotlight() + } + ) + }() + + // MARK: - Initialization + + init(viewController: UIViewController, bottomContentView: UIView, theme: Theme, tourManager: ProductTourManager = .shared) { + self.viewController = viewController + self.bottomContentView = bottomContentView + self.theme = theme + self.tourManager = tourManager + + // Register as observer + tourManager.addObserver(self) + } + + deinit { + tourManager.removeObserver(self) + } + + // MARK: - ProductTourObserver + + func productTour(didReceiveEvent event: ProductTourEvent) { + switch event { + case .searchCompleted: + currentStepIndex = 0 + showSpotlight(with: searchConfiguration) + case .externalWebsiteVisited: + showSpotlight(with: externalWebsiteConfiguration) + case .tourCompleted, .tourStarted: + dismissCurrentSpotlight() + case .searchTrackCompleted, .signInFlowStarted, .signInFlowEnded: + break + } + } + + // MARK: - Public Methods + + /// Updates the theme for the coordinator and any currently displayed spotlight + func updateTheme(_ newTheme: Theme) { + theme = newTheme + currentSpotlight?.applyTheme(theme: newTheme) + } + + /// Manually trigger spotlight display (useful for testing or manual triggers) + func showSpotlightIfNeeded() { + if tourManager.shouldShowSearchSpotlight { + currentStepIndex = 0 + showSpotlight(with: searchConfiguration) + } else if tourManager.shouldShowExternalWebsiteSpotlight { + showSpotlight(with: externalWebsiteConfiguration) + } + } + + // MARK: - Private Methods – Presentation + + private func showSpotlight(with configuration: SpotlightConfiguration) { + guard let viewController = viewController, + let bottomContentView = bottomContentView else { return } + + // Dismiss any existing spotlight before showing a new one + dismissCurrentSpotlight() + + currentStepIndex = 0 + currentConfiguration = configuration + presentStep(in: viewController, bottomAnchorView: bottomContentView) + } + + /// Creates and shows the `SpotlightToast` for the current step. + private func presentStep(in viewController: UIViewController, bottomAnchorView: UIView) { + guard let configuration = currentConfiguration, + currentStepIndex < configuration.steps.count else { return } + + let step = configuration.steps[currentStepIndex] + + let spotlight = SpotlightToast( + viewModel: step, + theme: theme, + primaryAction: { [weak self] in + self?.handlePrimaryAction() + }, + secondaryAction: { [weak self] in + self?.handleSecondaryAction() + } + ) + + // Dismiss by tapping outside completes the spotlight + spotlight.completionHandler = { [weak self] _ in + self?.completeCurrentSpotlight() + } + + currentSpotlight = spotlight + spotlight.show(in: viewController, bottomAnchorView: bottomAnchorView) + Analytics.shared.spotlightTourDisplay(label: configuration.analyticsLabel, step: step.currentStep) + } + + // MARK: - Private Methods – Actions + + func handlePrimaryAction() { + guard let configuration = currentConfiguration else { return } + + let currentStep = configuration.steps[currentStepIndex] + currentStepIndex += 1 + + if currentStepIndex < configuration.steps.count { + // Transition to next spotlight with forward animation + let nextStep = configuration.steps[currentStepIndex] + currentSpotlight?.transition(to: nextStep, direction: .forward) + Analytics.shared.spotlightTourClick(label: configuration.analyticsLabel, action: .next, step: currentStep.currentStep) + Analytics.shared.spotlightTourDisplay(label: configuration.analyticsLabel, step: nextStep.currentStep) + } else { + // All steps completed + Analytics.shared.spotlightTourClick(label: configuration.analyticsLabel, action: .complete, step: currentStep.currentStep) + dismissCurrentSpotlight() + completeCurrentSpotlight() + } + } + + func handleSecondaryAction() { + guard let configuration = currentConfiguration else { return } + + let currentStep = configuration.steps[currentStepIndex] + + switch configuration.secondaryAction(currentStepIndex) { + case .back: + currentStepIndex -= 1 + let previousStep = configuration.steps[currentStepIndex] + currentSpotlight?.transition(to: previousStep, direction: .backward) + Analytics.shared.spotlightTourClick(label: configuration.analyticsLabel, action: .back, step: currentStep.currentStep) + Analytics.shared.spotlightTourDisplay(label: configuration.analyticsLabel, step: previousStep.currentStep) + + case .skip: + Analytics.shared.spotlightTourClick(label: configuration.analyticsLabel, action: .skip, step: currentStep.currentStep) + dismissCurrentSpotlight() + completeCurrentSpotlight() + + case .openURL(let url): + Analytics.shared.spotlightTourClick(label: configuration.analyticsLabel, action: .readMore, step: currentStep.currentStep) + dismissCurrentSpotlight() + completeCurrentSpotlight() + openURL?(url) + } + } + + // MARK: - Private Methods – Lifecycle + + private func dismissCurrentSpotlight() { + currentSpotlight?.dismiss(false) + currentSpotlight = nil + } + + private func completeCurrentSpotlight() { + currentSpotlight = nil + currentConfiguration?.onComplete() + currentConfiguration = nil + } +} diff --git a/firefox-ios/Client/Ecosia/UI/ProductTour/SpotlightToast.swift b/firefox-ios/Client/Ecosia/UI/ProductTour/SpotlightToast.swift new file mode 100644 index 0000000000000..923bea0354031 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/ProductTour/SpotlightToast.swift @@ -0,0 +1,614 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import Foundation +import Shared +import UIKit + +// MARK: - View Model + +struct SpotlightToastViewModel { + let image: UIImage? + let titleText: String + let descriptionText: String + let currentStep: Int + let totalSteps: Int + let primaryButtonText: String + let secondaryButtonText: String? + /// Optional trailing icon displayed to the right of the secondary button text. + let secondaryButtonIcon: UIImage? + + init( + image: UIImage?, + titleText: String, + descriptionText: String, + currentStep: Int, + totalSteps: Int, + primaryButtonText: String, + secondaryButtonText: String?, + secondaryButtonIcon: UIImage? = nil + ) { + self.image = image + self.titleText = titleText + self.descriptionText = descriptionText + self.currentStep = currentStep + self.totalSteps = totalSteps + self.primaryButtonText = primaryButtonText + self.secondaryButtonText = secondaryButtonText + self.secondaryButtonIcon = secondaryButtonIcon + } +} + +// MARK: - SpotlightToast + +class SpotlightToast: Toast, UIGestureRecognizerDelegate { + struct UX { + static let cornerRadius: CGFloat = 10 + static let cardPadding: CGFloat = 8 + static let contentPadding: CGFloat = 8 + static let verticalSpacing: CGFloat = 16 + static let buttonSpacing: CGFloat = 8 + + static let subheadlineFontSize: CGFloat = 15 + + static let buttonHeight: CGFloat = 40 + static let buttonCornerRadius: CGFloat = 20 + static let buttonHorizontalPadding: CGFloat = 16 + static let buttonInternalSpacing: CGFloat = 16 + static let secondaryButtonIconPadding: CGFloat = 4 + static let secondaryButtonIconSize: CGFloat = 16 + + static let showAnimationDelay: TimeInterval = 0.5 + static let transitionAnimationDuration: TimeInterval = 0.5 + static let verticalAnimationOffset: CGFloat = 50 + + /// Maximum width of the toast on wide layouts such as iPad. + /// Matches the preferred form sheet width used by other modal screens (e.g. DefaultBrowserViewController). + static let maxWidth: CGFloat = 544 + static let imageAspectRatio: CGFloat = 9/16 + static let imageCornerRadius: CGFloat = 8 + } + + // MARK: - Gradient Overlay + + /// Transparent-to-solid scrim behind the toast covering only the web view area. + private final class GradientOverlayView: UIView { + override class var layerClass: AnyClass { CAGradientLayer.self } + + override init(frame: CGRect) { + super.init(frame: frame) + let gradient = layer as! CAGradientLayer + gradient.startPoint = CGPoint(x: 0.5, y: 0) + gradient.endPoint = CGPoint(x: 0.5, y: 1) + } + + required init?(coder: NSCoder) { fatalError() } + + func apply(color: UIColor) { + (layer as! CAGradientLayer).colors = [color.withAlphaComponent(0).cgColor, color.cgColor] + } + } + + private var gradientOverlay: GradientOverlayView? + private var gradientColor: UIColor? + + // MARK: - Properties + + private var viewModel: SpotlightToastViewModel + private var primaryButtonAction: (() -> Void)? + private var secondaryButtonAction: (() -> Void)? + + // Reusable constraints for optional views + private lazy var secondaryButtonHeightConstraint: NSLayoutConstraint = { + secondaryButton.heightAnchor.constraint(equalToConstant: UX.buttonHeight) + }() + + /// Pixel-aligned height set by `updateImageHeightIfNeeded()` once width is known. + private lazy var imageHeightConstraint: NSLayoutConstraint = { + spotlightImageView.heightAnchor.constraint(equalToConstant: 0) + }() + + // MARK: - UI Components + + private lazy var containerStackView: UIStackView = .build { stackView in + stackView.axis = .vertical + stackView.spacing = UX.cardPadding + stackView.alignment = .fill + stackView.distribution = .fill + stackView.layoutMargins = .init(equalInset: UX.cardPadding) + stackView.isLayoutMarginsRelativeArrangement = true + stackView.layer.cornerRadius = UX.cornerRadius + stackView.clipsToBounds = true + } + + private lazy var textContentStackView: UIStackView = .build { stackView in + stackView.axis = .vertical + stackView.spacing = UX.verticalSpacing + stackView.alignment = .fill + stackView.distribution = .fill + stackView.layoutMargins = .init(equalInset: UX.contentPadding) + stackView.isLayoutMarginsRelativeArrangement = true + } + + private lazy var spotlightImageView: UIImageView = .build { imageView in + imageView.contentMode = .scaleAspectFill + imageView.clipsToBounds = true + imageView.layer.cornerRadius = UX.imageCornerRadius + } + + private lazy var titleLabel: UILabel = .build { label in + label.font = DefaultDynamicFontHelper.preferredBoldFont( + withTextStyle: .subheadline, + size: UX.subheadlineFontSize + ) + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private lazy var descriptionLabel: UILabel = .build { label in + label.font = DefaultDynamicFontHelper.preferredFont( + withTextStyle: .subheadline, + size: UX.subheadlineFontSize + ) + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private lazy var bottomRowStackView: UIStackView = .build { stackView in + stackView.axis = .horizontal + stackView.spacing = UX.buttonSpacing + stackView.alignment = .center + stackView.distribution = .fill + } + + private lazy var stepCounterLabel: UILabel = .build { label in + label.font = DefaultDynamicFontHelper.preferredFont( + withTextStyle: .subheadline, + size: UX.subheadlineFontSize + ) + label.setContentHuggingPriority(.required, for: .horizontal) + label.setContentCompressionResistancePriority(.required, for: .horizontal) + } + + private lazy var buttonStackView: UIStackView = .build { stackView in + stackView.axis = .horizontal + stackView.spacing = UX.buttonInternalSpacing + stackView.alignment = .fill + stackView.distribution = .fill + } + + private lazy var secondaryButton: UIButton = .build { button in + var config = UIButton.Configuration.plain() + config.contentInsets = NSDirectionalEdgeInsets( + top: 0, + leading: UX.buttonHorizontalPadding, + bottom: 0, + trailing: UX.buttonHorizontalPadding + ) + config.imagePlacement = .trailing + config.imagePadding = UX.secondaryButtonIconPadding + config.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in + var outgoing = incoming + outgoing.font = DefaultDynamicFontHelper.preferredFont( + withTextStyle: .subheadline, + size: UX.subheadlineFontSize + ) + return outgoing + } + button.configuration = config + button.setContentHuggingPriority(.required, for: .horizontal) + button.setContentCompressionResistancePriority(.required, for: .horizontal) + } + + private lazy var primaryButton: UIButton = .build { button in + var config = UIButton.Configuration.plain() + config.contentInsets = NSDirectionalEdgeInsets( + top: 0, + leading: UX.buttonHorizontalPadding, + bottom: 0, + trailing: UX.buttonHorizontalPadding + ) + config.cornerStyle = .capsule + config.background.strokeWidth = 1 + config.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in + var outgoing = incoming + outgoing.font = DefaultDynamicFontHelper.preferredFont( + withTextStyle: .subheadline, + size: UX.subheadlineFontSize + ) + return outgoing + } + button.configuration = config + button.setContentHuggingPriority(.required, for: .horizontal) + button.setContentCompressionResistancePriority(.required, for: .horizontal) + } + + // MARK: - Initialization + + init( + viewModel: SpotlightToastViewModel, + theme: Theme?, + primaryAction: @escaping () -> Void, + secondaryAction: (() -> Void)? = nil + ) { + self.viewModel = viewModel + self.primaryButtonAction = primaryAction + self.secondaryButtonAction = secondaryAction + + super.init(frame: .zero) + + setupView() + configureContent() + + if let theme = theme { + applyTheme(theme: theme) + } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setup + + private func setupView() { + self.clipsToBounds = true + self.addSubview(toastView) + toastView.addSubview(containerStackView) + + // Configure gesture recognizer for custom tap handling + gestureRecognizer.isEnabled = true + gestureRecognizer.delegate = self + + // Build view hierarchy + configureImageView(for: viewModel) + textContentStackView.addArrangedSubview(titleLabel) + textContentStackView.addArrangedSubview(descriptionLabel) + textContentStackView.addArrangedSubview(bottomRowStackView) + containerStackView.addArrangedSubview(textContentStackView) + bottomRowStackView.addArrangedSubview(stepCounterLabel) + bottomRowStackView.addArrangedSubview(UIView()) // Flexible spacer to right-align buttons + bottomRowStackView.addArrangedSubview(buttonStackView) + + // Add buttons to button stack view + configureSecondaryButton(for: viewModel) + primaryButton.addTarget(self, action: #selector(primaryButtonTapped), for: .touchUpInside) + buttonStackView.addArrangedSubview(primaryButton) + + NSLayoutConstraint.activate([ + containerStackView.leadingAnchor.constraint(equalTo: toastView.leadingAnchor, constant: Toast.UX.toastOffset), + containerStackView.trailingAnchor.constraint(equalTo: toastView.trailingAnchor, constant: -Toast.UX.toastOffset), + containerStackView.topAnchor.constraint(equalTo: toastView.topAnchor), + containerStackView.bottomAnchor.constraint(equalTo: toastView.bottomAnchor, constant: -Toast.UX.toastOffset), + + toastView.leadingAnchor.constraint(equalTo: leadingAnchor), + toastView.trailingAnchor.constraint(equalTo: trailingAnchor), + toastView.topAnchor.constraint(equalTo: topAnchor), + toastView.bottomAnchor.constraint(equalTo: bottomAnchor), + + primaryButton.heightAnchor.constraint(equalToConstant: UX.buttonHeight) + ]) + } + + /// Adds or removes the image view from the container stack based on the view model. + /// The image view always uses the same fixed aspect ratio so all steps look consistent. + private func configureImageView(for viewModel: SpotlightToastViewModel) { + if viewModel.image != nil { + if spotlightImageView.superview == nil { + containerStackView.insertArrangedSubview(spotlightImageView, at: 0) + imageHeightConstraint.isActive = true + } + } else { + if spotlightImageView.superview != nil { + containerStackView.removeArrangedSubview(spotlightImageView) + spotlightImageView.removeFromSuperview() + imageHeightConstraint.isActive = false + } + } + } + + /// Updates the image height constraint to a pixel-aligned value based on the + /// current width and the desired aspect ratio. This avoids sub-pixel rounding + /// differences that occur when using a multiplier-based constraint, which can + /// cause visible shifts during snapshot-based transition animations. + private func updateImageHeightIfNeeded() { + let width = spotlightImageView.bounds.width + guard width > 0 else { return } + let scale = traitCollection.displayScale > 0 ? traitCollection.displayScale : UIScreen.main.scale + let exactHeight = width * UX.imageAspectRatio + let pixelAlignedHeight = ceil(exactHeight * scale) / scale + if imageHeightConstraint.constant != pixelAlignedHeight { + imageHeightConstraint.constant = pixelAlignedHeight + } + } + + override func layoutSubviews() { + super.layoutSubviews() + updateImageHeightIfNeeded() + } + + /// Adds or removes the secondary button from the button stack based on the view model + private func configureSecondaryButton(for viewModel: SpotlightToastViewModel) { + if viewModel.secondaryButtonText != nil { + if secondaryButton.superview == nil { + secondaryButton.addTarget(self, action: #selector(secondaryButtonTapped), for: .touchUpInside) + buttonStackView.insertArrangedSubview(secondaryButton, at: 0) + secondaryButtonHeightConstraint.isActive = true + } + } else { + if secondaryButton.superview != nil { + secondaryButton.removeTarget(self, action: #selector(secondaryButtonTapped), for: .touchUpInside) + buttonStackView.removeArrangedSubview(secondaryButton) + secondaryButton.removeFromSuperview() + secondaryButtonHeightConstraint.isActive = false + } + } + } + + /// Configures the secondary button's trailing icon based on the view model + private func configureSecondaryButtonIcon(for viewModel: SpotlightToastViewModel) { + guard var config = secondaryButton.configuration else { return } + if let icon = viewModel.secondaryButtonIcon { + let size = CGSize(width: UX.secondaryButtonIconSize, height: UX.secondaryButtonIconSize) + let renderer = UIGraphicsImageRenderer(size: size) + let resizedIcon = renderer.image { _ in + icon.draw(in: CGRect(origin: .zero, size: size)) + } + config.image = resizedIcon.withRenderingMode(.alwaysTemplate) + } else { + config.image = nil + } + secondaryButton.configuration = config + } + + private func configureContent() { + spotlightImageView.image = viewModel.image + titleLabel.text = viewModel.titleText + descriptionLabel.text = viewModel.descriptionText + + if viewModel.totalSteps > 1 { + stepCounterLabel.text = "\(viewModel.currentStep) / \(viewModel.totalSteps)" + stepCounterLabel.isHidden = false + } else { + stepCounterLabel.text = nil + stepCounterLabel.isHidden = true + } + + // Update button text + primaryButton.setTitle(viewModel.primaryButtonText, for: .normal) + if let secondaryButtonText = viewModel.secondaryButtonText { + secondaryButton.setTitle(secondaryButtonText, for: .normal) + } + + // Update secondary button icon + configureSecondaryButtonIcon(for: viewModel) + } + + // MARK: - Theme + + override func applyTheme(theme: Theme) { + super.applyTheme(theme: theme) + containerStackView.backgroundColor = theme.colors.ecosia.backgroundFeatured + titleLabel.textColor = theme.colors.ecosia.textPrimary + descriptionLabel.textColor = theme.colors.ecosia.textPrimary + stepCounterLabel.textColor = theme.colors.ecosia.textPrimary + + if secondaryButton.superview != nil { + var secondaryConfig = secondaryButton.configuration + secondaryConfig?.baseForegroundColor = theme.colors.ecosia.buttonContentSecondary + secondaryConfig?.baseBackgroundColor = .clear + secondaryButton.configuration = secondaryConfig + } + + var primaryConfig = primaryButton.configuration + primaryConfig?.baseForegroundColor = theme.colors.ecosia.textPrimary + primaryConfig?.baseBackgroundColor = .clear + primaryConfig?.background.strokeColor = theme.colors.ecosia.textPrimary + primaryButton.configuration = primaryConfig + + gradientColor = theme.colors.ecosia.backgroundGradient + gradientOverlay?.apply(color: theme.colors.ecosia.backgroundGradient) + } + + // MARK: - Actions + + @objc + private func primaryButtonTapped() { + primaryButtonAction?() + } + + @objc + private func secondaryButtonTapped() { + secondaryButtonAction?() + } + + // MARK: - Public Methods + + /// Show the spotlight toast with animation + func show( + in viewController: UIViewController, + bottomAnchorView: UIView, + delay: TimeInterval = UX.showAnimationDelay + ) { + self.viewController = viewController + translatesAutoresizingMaskIntoConstraints = false + + // Gradient overlay behind the toast (web view area only, not over toolbars) + let overlay = GradientOverlayView() + overlay.translatesAutoresizingMaskIntoConstraints = false + overlay.alpha = 0 + if let color = gradientColor { overlay.apply(color: color) } + viewController.view.insertSubview(overlay, belowSubview: bottomAnchorView) + gradientOverlay = overlay + + viewController.view.addSubview(self) + + // On iPhone portrait the toast fills the full view width (matching Toast behaviour). + // On iPad and landscape the width is capped at maxWidth and centred. + // The high-priority equality makes the toast prefer full width; the required + // lessThanOrEqualTo cap wins whenever the view is wider than maxWidth. + let preferFullWidth = widthAnchor.constraint(equalTo: viewController.view.widthAnchor) + preferFullWidth.priority = .defaultHigh + + NSLayoutConstraint.activate([ + preferFullWidth, + widthAnchor.constraint(lessThanOrEqualToConstant: UX.maxWidth), + centerXAnchor.constraint(equalTo: viewController.view.centerXAnchor), + leadingAnchor.constraint(greaterThanOrEqualTo: viewController.view.leadingAnchor), + trailingAnchor.constraint(lessThanOrEqualTo: viewController.view.trailingAnchor), + bottomAnchor.constraint(equalTo: bottomAnchorView.topAnchor), + + overlay.leadingAnchor.constraint(equalTo: viewController.view.leadingAnchor), + overlay.trailingAnchor.constraint(equalTo: viewController.view.trailingAnchor), + overlay.topAnchor.constraint(equalTo: topAnchor), + overlay.bottomAnchor.constraint(equalTo: viewController.view.bottomAnchor) + ]) + + // Set initial state: below and transparent + containerStackView.transform = CGAffineTransform(translationX: 0, y: UX.verticalAnimationOffset) + containerStackView.alpha = 0 + + // Compute the pixel-aligned height + updateImageHeightIfNeeded() + + layoutIfNeeded() + + // Animate to final position + UIView.animate( + withDuration: Toast.UX.toastAnimationDuration, + delay: delay, + options: [.curveEaseOut], + animations: { + self.containerStackView.transform = .identity + self.containerStackView.alpha = 1.0 + self.gradientOverlay?.alpha = 1.0 + } + ) + } + + /// Transition to a new view model with directional animation + /// - Parameters: + /// - newViewModel: The new spotlight step to display + /// - direction: The direction of transition (.forward or .backward) + /// - completion: Called when transition is complete + func transition( + to newViewModel: SpotlightToastViewModel, + direction: TransitionDirection, + completion: (() -> Void)? = nil + ) { + guard let snapshot = containerStackView.snapshotView(afterScreenUpdates: false) else { + // Fallback: just update content if snapshot fails + self.viewModel = newViewModel + configureImageView(for: newViewModel) + configureSecondaryButton(for: newViewModel) + configureContent() + completion?() + return + } + + // Calculate offsets for horizontal slide animation + let containerWidth = containerStackView.bounds.width + let spacing = Toast.UX.toastOffset * 2 + let animationOffset = containerWidth + spacing + let exitOffset: CGFloat = direction == .forward ? -animationOffset : animationOffset + let entryOffset: CGFloat = direction == .forward ? animationOffset : -animationOffset + + // Position snapshot at current location + snapshot.frame = containerStackView.frame + snapshot.alpha = containerStackView.alpha + snapshot.transform = containerStackView.transform + toastView.addSubview(snapshot) + + // Update content and reconfigure optional views + self.viewModel = newViewModel + configureImageView(for: newViewModel) + configureSecondaryButton(for: newViewModel) + configureContent() + containerStackView.transform = CGAffineTransform(translationX: entryOffset, y: 0) + containerStackView.alpha = 1.0 + + // Animate transition + UIView.animate( + withDuration: UX.transitionAnimationDuration, + delay: 0, + options: [.curveEaseInOut], + animations: { + snapshot.transform = CGAffineTransform(translationX: exitOffset, y: 0) + self.containerStackView.transform = .identity + }, + completion: { _ in + snapshot.removeFromSuperview() + completion?() + } + ) + } + + override func dismiss(_ buttonPressed: Bool) { + guard !dismissed else { return } + + dismissed = true + superview?.removeGestureRecognizer(gestureRecognizer) + + UIView.animate( + withDuration: Toast.UX.toastAnimationDuration, + animations: { + self.containerStackView.transform = CGAffineTransform(translationX: 0, y: UX.verticalAnimationOffset) + self.containerStackView.alpha = 0 + self.gradientOverlay?.alpha = 0 + }, + completion: { _ in + self.gradientOverlay?.removeFromSuperview() + self.gradientOverlay = nil + self.removeFromSuperview() + if !buttonPressed { + self.completionHandler?(false) + } + } + ) + } + + // MARK: - Gesture Handling + + /// Override tap handling to dismiss on taps outside the toast + /// Note: shouldReceive touch already filters out touches on the toast itself, + /// so this will only be called for touches outside the toast + override func handleTap(_ gestureRecognizer: UIGestureRecognizer) { + dismiss(false) + completionHandler?(false) + } + + /// Allow simultaneous recognition with other gestures, except for web view gestures + /// This ensures URL bar gestures still work while blocking web view interaction + func gestureRecognizer( + _ gestureRecognizer: UIGestureRecognizer, + shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer + ) -> Bool { + // Allow simultaneous recognition with other gestures (like URL text field) + return true + } + + /// Decide whether to handle each touch based on location + /// This filters out touches on the toast itself (so buttons work) + /// and touches on the web view (so web content remains interactive) + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + let touchLocation = touch.location(in: self) + + // If the touch is on the toast itself, don't handle it with our gesture + // (let buttons handle their own touches) + if toastView.frame.contains(touchLocation) { + return false + } + + // For other touches outside the toast (like URL bar), we want to handle them + return true + } +} + +// MARK: - TransitionDirection + +enum TransitionDirection { + case forward + case backward +} diff --git a/firefox-ios/Client/Ecosia/UI/Theme/EcosiaColor.swift b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaColor.swift new file mode 100644 index 0000000000000..0e6bc96ed18de --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaColor.swift @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +// This class contains all of Ecosia official primitive color tokens referenced in the link below. +// https://www.figma.com/design/8T2rTBVwynJKSdY6MQo5PQ/%E2%9A%9B%EF%B8%8F--Foundations?node-id=1239-9385&t=UKHtrxcc9UtOihsm-4 +// You should never call those colors directly, they should only be called from a theme within the theme manager. +// This is the equivalent to Firefox's `FXColors`. +struct EcosiaColor { + // MARK: - Black & White + static let Black = UIColor(rgb: 0x000000) + static let White = UIColor(rgb: 0xFFFFFF) + + // MARK: - Neutral + static let Gray10 = UIColor(rgb: 0xF8F8F6) + static let Gray20 = UIColor(rgb: 0xF0F0EB) + static let Gray30 = UIColor(rgb: 0xDEDED9) + static let Gray40 = UIColor(rgb: 0xBEBEB9) + static let Gray50 = UIColor(rgb: 0x6C6C6C) + static let Gray60 = UIColor(rgb: 0x4C4C4C) + static let Gray70 = UIColor(rgb: 0x333333) + static let Gray80 = UIColor(rgb: 0x252525) + static let Gray90 = UIColor(rgb: 0x1A1A1A) + + // MARK: - Grellow + static let Grellow100 = UIColor(rgb: 0xD7EB80) + static let Grellow200 = UIColor(rgb: 0xBBCF65) + static let Grellow300 = UIColor(rgb: 0xA1B353) + static let Grellow400 = UIColor(rgb: 0x889745) + static let Grellow500 = UIColor(rgb: 0x6F7D38) + static let Grellow600 = UIColor(rgb: 0x58632B) + static let Grellow700 = UIColor(rgb: 0x424A1E) + static let Grellow800 = UIColor(rgb: 0x2D3315) + static let Grellow900 = UIColor(rgb: 0x1B1D0F) + + // MARK: - Green + static let Green10 = UIColor(rgb: 0xCFF2D0) + static let Green20 = UIColor(rgb: 0xAFE9B0) + static let Green30 = UIColor(rgb: 0x5DD25E) + static let Green40 = UIColor(rgb: 0xA4D24F) + static let Green50 = UIColor(rgb: 0x008009) + static let Green60 = UIColor(rgb: 0x007508) + static let Green70 = UIColor(rgb: 0x006600) + + // MARK: - Dark Green + static let DarkGreen30 = UIColor(rgb: 0x668A7A) + static let DarkGreen50 = UIColor(rgb: 0x275243) + static let DarkGreen70 = UIColor(rgb: 0x09281D) + static let DarkGreen800 = UIColor(rgb: 0x18362B) + + // MARK: - Light Green + static let LightGreen20 = UIColor(rgb: 0xBACC80) + static let LightGreen30 = UIColor(rgb: 0xD8FF80) + static let LightGreen40 = UIColor(rgb: 0xA4D24F) + static let LightGreen50 = UIColor(rgb: 0x72A11A) + static let LightGreen60 = UIColor(rgb: 0x40521F) + + // MARK: - Red + static let Red20 = UIColor(rgb: 0xFFE8DA) + static let Red30 = UIColor(rgb: 0xFF8A8C) + static let Red40 = UIColor(rgb: 0xFD4256) + static let Red50 = UIColor(rgb: 0xAF1731) + + // MARK: - Yellow + static let Yellow40 = UIColor(rgb: 0xF7BC00) + static let Yellow50 = UIColor(rgb: 0xD6A300) + + // MARK: - Blue + static let Blue30 = UIColor(rgb: 0x96D6F8) + static let Blue40 = UIColor(rgb: 0x0094C7) + static let Blue50 = UIColor(rgb: 0x007EA8) + static let Blue60 = UIColor(rgb: 0x005D87) + static let Blue70 = UIColor(rgb: 0x004687) + static let Blue80 = UIColor(rgb: 0x002A3D) + + // MARK: - Peach + static let Peach30 = UIColor(rgb: 0xFFE6BF) + static let Peach40 = UIColor(rgb: 0xFFAF87) + static let Peach50 = UIColor(rgb: 0xCA8461) +} diff --git a/firefox-ios/Client/Ecosia/UI/Theme/EcosiaDarkTheme.swift b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaDarkTheme.swift new file mode 100644 index 0000000000000..e6e318fe15ee0 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaDarkTheme.swift @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit +import Ecosia + +public struct EcosiaDarkTheme: Theme { + public var type: ThemeType = .dark + public var colors: EcosiaThemeColourPalette = EcosiaDarkColourPalette() +} + +private class EcosiaDarkColourPalette: EcosiaLightColourPalette { + override var ecosia: EcosiaSemanticColors { + EcosiaDarkSemanticColors() + } + + override var fallbackTheme: Theme { + DarkTheme() + } +} + +private struct EcosiaDarkSemanticColors: EcosiaSemanticColors { + var backgroundNeutralInverse: UIColor = EcosiaColor.White + var backgroundFeatured: UIColor = EcosiaColor.Grellow700 + var backgroundGradient: UIColor = EcosiaColor.Gray90 + var backgroundPrimary: UIColor = EcosiaColor.Gray90 + var backgroundPrimaryDecorative: UIColor = EcosiaColor.Gray90 + var backgroundRoleNegative: UIColor = EcosiaColor.Claret800 + var backgroundSecondary: UIColor = EcosiaColor.Gray80 + var backgroundTertiary: UIColor = EcosiaColor.Gray70 + var backgroundQuaternary: UIColor = EcosiaColor.Gray70 + var backgroundElevation1: UIColor = EcosiaColor.Gray80 + var backgroundElevation2: UIColor = EcosiaColor.Gray70 + var borderDecorative: UIColor = EcosiaColor.Gray60 + var borderNegative: UIColor = EcosiaColor.Claret600 + var brandFeatured: UIColor = EcosiaColor.Grellow100 + var brandImpact: UIColor = EcosiaColor.Yellow40 + var brandPrimary: UIColor = EcosiaColor.White + var buttonBackgroundFeatured: UIColor = EcosiaColor.Grellow100 + var buttonBackgroundFeaturedActive: UIColor = EcosiaColor.Grellow300 + var buttonBackgroundFeaturedHover: UIColor = EcosiaColor.Grellow200 + var buttonBackgroundPrimary: UIColor = EcosiaColor.White + var buttonBackgroundPrimaryActive: UIColor = EcosiaColor.Gray40 + var buttonBackgroundSecondary: UIColor = EcosiaColor.Gray90 + var buttonBackgroundSecondaryActive: UIColor = EcosiaColor.Gray50 + var buttonBackgroundSecondaryHover: UIColor = EcosiaColor.Gray70 + var buttonBackgroundTransparentActive: UIColor = EcosiaColor.Gray70.withAlphaComponent(0.24) + var buttonContentSecondary: UIColor = EcosiaColor.White + var buttonContentSecondaryStatic: UIColor = EcosiaColor.Gray70 + var highlighter: UIColor = EcosiaColor.Grellow100.withAlphaComponent(0.32) + var linkPrimary: UIColor = EcosiaColor.White + var iconDecorative: UIColor = EcosiaColor.White + var iconInverseStrong: UIColor = EcosiaColor.Black + var segmentedControlBackgroundActive: UIColor = EcosiaColor.Gray60 + var segmentedControlBackgroundRest: UIColor = EcosiaColor.Gray80 + var stateDisabled: UIColor = EcosiaColor.Gray50 + var stateError: UIColor = EcosiaColor.Red30 + var switchKnobActive: UIColor = EcosiaColor.Gray70 + var switchKnobDisabled: UIColor = EcosiaColor.White + var textPrimary: UIColor = EcosiaColor.White + var textInversePrimary: UIColor = EcosiaColor.Gray90 + var textSecondary: UIColor = EcosiaColor.Gray30 + var textStaticDark: UIColor = EcosiaColor.Gray70 + var textStaticLight: UIColor = EcosiaColor.White +} diff --git a/firefox-ios/Client/Ecosia/UI/Theme/EcosiaLightTheme.swift b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaLightTheme.swift new file mode 100644 index 0000000000000..8b30547720758 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaLightTheme.swift @@ -0,0 +1,157 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit +import Ecosia + +struct EcosiaLightTheme: Theme { + public var type: ThemeType = .light + public var colors: EcosiaThemeColourPalette = EcosiaLightColourPalette() +} + +class EcosiaLightColourPalette: EcosiaThemeColourPalette { + var ecosia: EcosiaSemanticColors { + EcosiaLightSemanticColors() + } + + var fallbackTheme: Theme { + LightTheme() + } + + // MARK: - Layers + var layer1: UIColor { ecosia.backgroundPrimaryDecorative } + var layer2: UIColor { ecosia.backgroundElevation1 } + var layer3: UIColor { ecosia.backgroundPrimaryDecorative } + var layer4: UIColor { fallbackTheme.colors.layer4 } + var layer5: UIColor { ecosia.backgroundSecondary } + var layer5Hover: UIColor { ecosia.backgroundQuaternary } + var layerScrim: UIColor { fallbackTheme.colors.layerScrim } + var layerGradient: Gradient { fallbackTheme.colors.layerGradient } + var layerGradientOverlay: Gradient { fallbackTheme.colors.layerGradientOverlay } + var layerAccentNonOpaque: UIColor { ecosia.buttonBackgroundPrimary } + var layerAccentPrivate: UIColor { fallbackTheme.colors.layerAccentPrivate } + var layerAccentPrivateNonOpaque: UIColor { ecosia.textPrimary } + var layerSepia: UIColor { fallbackTheme.colors.layerSepia } + var layerHomepage: Gradient { fallbackTheme.colors.layerHomepage } + var layerInformation: UIColor { fallbackTheme.colors.layerInformation } + var layerSuccess: UIColor { fallbackTheme.colors.layerSuccess } + var layerWarning: UIColor { ecosia.stateError } + var layerCritical: UIColor { fallbackTheme.colors.layerCritical } + var layerSelectedText: UIColor { fallbackTheme.colors.layerSelectedText } + var layerAutofillText: UIColor { fallbackTheme.colors.layerAutofillText } + var layerSearch: UIColor { fallbackTheme.colors.layerSearch } + var layerGradientURL: Gradient { fallbackTheme.colors.layerGradientURL } + + // MARK: - Ratings + var layerRatingA: UIColor { fallbackTheme.colors.layerRatingA } + var layerRatingASubdued: UIColor { fallbackTheme.colors.layerRatingASubdued } + var layerRatingB: UIColor { fallbackTheme.colors.layerRatingB } + var layerRatingBSubdued: UIColor { fallbackTheme.colors.layerRatingBSubdued } + var layerRatingC: UIColor { fallbackTheme.colors.layerRatingC } + var layerRatingCSubdued: UIColor { fallbackTheme.colors.layerRatingCSubdued } + var layerRatingD: UIColor { fallbackTheme.colors.layerRatingD } + var layerRatingDSubdued: UIColor { fallbackTheme.colors.layerRatingDSubdued } + var layerRatingF: UIColor { fallbackTheme.colors.layerRatingF } + var layerRatingFSubdued: UIColor { fallbackTheme.colors.layerRatingFSubdued } + + // MARK: - Actions + var actionPrimary: UIColor { ecosia.buttonBackgroundPrimary } + var actionPrimaryHover: UIColor { ecosia.buttonBackgroundPrimaryActive } + var actionPrimaryDisabled: UIColor { fallbackTheme.colors.actionPrimaryDisabled } + var actionSecondary: UIColor { ecosia.buttonBackgroundSecondary } + var actionSecondaryHover: UIColor { fallbackTheme.colors.actionSecondaryHover } + var formSurfaceOff: UIColor { fallbackTheme.colors.formSurfaceOff } + var formKnob: UIColor { fallbackTheme.colors.formKnob } + var indicatorActive: UIColor { fallbackTheme.colors.indicatorActive } + var indicatorInactive: UIColor { fallbackTheme.colors.indicatorInactive } + var actionSuccess: UIColor { fallbackTheme.colors.actionSuccess } + var actionWarning: UIColor { ecosia.stateError } + var actionCritical: UIColor { fallbackTheme.colors.actionCritical } + var actionInformation: UIColor { fallbackTheme.colors.actionInformation } + var actionTabActive: UIColor { fallbackTheme.colors.actionTabActive } + var actionTabInactive: UIColor { fallbackTheme.colors.actionTabInactive } + + // MARK: - Text + var textPrimary: UIColor { ecosia.textPrimary } + var textSecondary: UIColor { ecosia.textSecondary } + var textDisabled: UIColor { fallbackTheme.colors.textDisabled } + var textCritical: UIColor { fallbackTheme.colors.textCritical } + var textAccent: UIColor { ecosia.buttonBackgroundPrimary } + var textOnDark: UIColor { fallbackTheme.colors.textOnDark } + var textOnLight: UIColor { fallbackTheme.colors.textOnLight } + var textInverted: UIColor { ecosia.textInversePrimary } + var textInvertedDisabled: UIColor { fallbackTheme.colors.textInvertedDisabled } + + // MARK: - Icons + var iconPrimary: UIColor { ecosia.buttonContentSecondary } + var iconSecondary: UIColor { ecosia.buttonContentSecondary } + var iconDisabled: UIColor { fallbackTheme.colors.iconDisabled } + var iconAccent: UIColor { ecosia.iconDecorative } + var iconOnColor: UIColor { fallbackTheme.colors.iconOnColor } + var iconCritical: UIColor { ecosia.stateError } + var iconSpinner: UIColor { fallbackTheme.colors.iconSpinner } + var iconAccentViolet: UIColor { fallbackTheme.colors.iconAccentViolet } + var iconAccentBlue: UIColor { fallbackTheme.colors.iconAccentBlue } + var iconAccentPink: UIColor { fallbackTheme.colors.iconAccentPink } + var iconAccentGreen: UIColor { fallbackTheme.colors.iconAccentGreen } + var iconAccentYellow: UIColor { fallbackTheme.colors.iconAccentYellow } + var iconRatingNeutral: UIColor { fallbackTheme.colors.iconRatingNeutral } + + // MARK: - Border + var borderPrimary: UIColor { ecosia.borderDecorative } + var borderAccent: UIColor { actionPrimary } + var borderAccentNonOpaque: UIColor { actionPrimary } + var borderAccentPrivate: UIColor { actionPrimary } + var borderInverted: UIColor { fallbackTheme.colors.borderInverted } + var borderToolbarDivider: UIColor { fallbackTheme.colors.borderToolbarDivider } + + // MARK: - Shadow + var shadowDefault: UIColor { fallbackTheme.colors.shadowDefault } +} + +private struct EcosiaLightSemanticColors: EcosiaSemanticColors { + var backgroundNeutralInverse: UIColor = EcosiaColor.Gray80 + var backgroundFeatured: UIColor = EcosiaColor.Grellow100 + var backgroundGradient: UIColor = EcosiaColor.White + var backgroundPrimary: UIColor = EcosiaColor.White + var backgroundPrimaryDecorative: UIColor = EcosiaColor.Gray10 + var backgroundRoleNegative: UIColor = EcosiaColor.Peach100 + var backgroundSecondary: UIColor = EcosiaColor.Gray10 + var backgroundTertiary: UIColor = EcosiaColor.Gray20 + var backgroundQuaternary: UIColor = EcosiaColor.Gray20 + var backgroundElevation1: UIColor = EcosiaColor.White + var backgroundElevation2: UIColor = EcosiaColor.White + var borderDecorative: UIColor = EcosiaColor.Gray30 + var borderNegative: UIColor = EcosiaColor.Claret300 + var brandFeatured: UIColor = EcosiaColor.Grellow100 + var brandImpact: UIColor = EcosiaColor.Yellow40 + var brandPrimary: UIColor = EcosiaColor.Gray70 + var buttonBackgroundFeatured: UIColor = EcosiaColor.Grellow100 + var buttonBackgroundFeaturedActive: UIColor = EcosiaColor.Grellow300 + var buttonBackgroundFeaturedHover: UIColor = EcosiaColor.Grellow200 + var buttonBackgroundPrimary: UIColor = EcosiaColor.Gray70 + var buttonBackgroundPrimaryActive: UIColor = EcosiaColor.Gray50 + var buttonBackgroundSecondary: UIColor = EcosiaColor.White + var buttonBackgroundSecondaryActive: UIColor = EcosiaColor.Gray40 + var buttonBackgroundSecondaryHover: UIColor = EcosiaColor.Gray30 + var buttonBackgroundTransparentActive: UIColor = EcosiaColor.Gray30.withAlphaComponent(0.32) + var buttonContentSecondary: UIColor = EcosiaColor.Gray70 + var buttonContentSecondaryStatic: UIColor = EcosiaColor.Gray70 + var highlighter: UIColor = EcosiaColor.Grellow100.withAlphaComponent(0.32) + var linkPrimary: UIColor = EcosiaColor.Gray70 + var iconDecorative: UIColor = EcosiaColor.Gray50 + var iconInverseStrong: UIColor = EcosiaColor.White + var segmentedControlBackgroundActive: UIColor = EcosiaColor.White + var segmentedControlBackgroundRest: UIColor = EcosiaColor.Gray30 + var stateDisabled: UIColor = EcosiaColor.Gray30 + var stateError: UIColor = EcosiaColor.Red50 + var switchKnobActive: UIColor = EcosiaColor.White + var switchKnobDisabled: UIColor = EcosiaColor.White + var textPrimary: UIColor = EcosiaColor.Gray70 + var textInversePrimary: UIColor = EcosiaColor.White + var textSecondary: UIColor = EcosiaColor.Gray50 + var textStaticDark: UIColor = EcosiaColor.Gray70 + var textStaticLight: UIColor = EcosiaColor.White +} diff --git a/firefox-ios/Client/Ecosia/UI/Theme/EcosiaThemeManager.swift b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaThemeManager.swift new file mode 100644 index 0000000000000..a7a608d570359 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/Theme/EcosiaThemeManager.swift @@ -0,0 +1,214 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit + +/// The `ThemeManager` will be responsible for providing the theme throughout the app +public final class EcosiaThemeManager: ThemeManager, Notifiable { + // These have been carried over from the legacy system to maintain backwards compatibility + private enum ThemeKeys { + static let themeName = "prefKeyThemeName" + static let systemThemeIsOn = "prefKeySystemThemeSwitchOnOff" + + enum AutomaticBrightness { + static let isOn = "prefKeyAutomaticSwitchOnOff" + static let thresholdValue = "prefKeyAutomaticSliderValue" + } + + enum NightMode { + static let isOn = "profile.NightModeStatus" + } + } + + // MARK: - Variables + + private var windows: [WindowUUID: UIWindow] = [:] + private var privateBrowsingState: [WindowUUID: Bool] = [:] + private var allWindowUUIDs: [WindowUUID] { return Array(windows.keys) } + public var notificationCenter: NotificationProtocol + + private var userDefaults: UserDefaultsInterface + private var mainQueue: DispatchQueueInterface + private var sharedContainerIdentifier: String + + private var nightModeIsOn: Bool { + return userDefaults.bool(forKey: ThemeKeys.NightMode.isOn) + } + + public var systemThemeIsOn: Bool { + return userDefaults.bool(forKey: ThemeKeys.systemThemeIsOn) + } + + public var automaticBrightnessIsOn: Bool { + return userDefaults.bool(forKey: ThemeKeys.AutomaticBrightness.isOn) + } + + public var automaticBrightnessValue: Float { + return userDefaults.float(forKey: ThemeKeys.AutomaticBrightness.thresholdValue) + } + + // MARK: - Init + + public init(userDefaults: UserDefaultsInterface = UserDefaults.standard, + notificationCenter: NotificationProtocol = NotificationCenter.default, + mainQueue: DispatchQueueInterface = DispatchQueue.main, + sharedContainerIdentifier: String) { + self.userDefaults = userDefaults + self.notificationCenter = notificationCenter + self.mainQueue = mainQueue + self.sharedContainerIdentifier = sharedContainerIdentifier + + self.userDefaults.register(defaults: [ + ThemeKeys.systemThemeIsOn: true, + ThemeKeys.NightMode.isOn: NSNumber(value: false) + ]) + + setupNotifications(forObserver: self, + observing: [UIScreen.brightnessDidChangeNotification, + UIApplication.didBecomeActiveNotification]) + } + + // MARK: - Themeing general functions + public func getCurrentTheme(for window: WindowUUID?) -> Theme { + guard let window else { + assertionFailure("Attempt to get the theme for a nil window UUID.") + return EcosiaDarkTheme() + } + + return getThemeFrom(type: determineThemeType(for: window)) + } + + public func applyThemeUpdatesToWindows() { + allWindowUUIDs.forEach { + applyThemeChanges(for: $0, using: determineThemeType(for: $0)) + } + } + + // MARK: - Manual theme functions + public func setManualTheme(to newTheme: ThemeType) { + updateSavedTheme(to: newTheme) + applyThemeUpdatesToWindows() + } + + public func getUserManualTheme() -> ThemeType { + guard let savedThemeDescription = userDefaults.string(forKey: ThemeKeys.themeName), + let savedTheme = ThemeType(rawValue: savedThemeDescription) + else { return getThemeTypeBasedOnSystem() } + + return savedTheme + } + + // MARK: - System theme functions + public func setSystemTheme(isOn: Bool) { + userDefaults.set(isOn, forKey: ThemeKeys.systemThemeIsOn) + applyThemeUpdatesToWindows() + } + + private func getThemeTypeBasedOnSystem() -> ThemeType { + return UIScreen.main.traitCollection.userInterfaceStyle == .dark ? ThemeType.dark : ThemeType.light + } + + // MARK: - Private theme functions + public func setPrivateTheme(isOn: Bool, for window: WindowUUID) { + guard getPrivateThemeIsOn(for: window) != isOn else { return } + privateBrowsingState[window] = isOn + applyThemeChanges(for: window, using: determineThemeType(for: window)) + } + + public func getPrivateThemeIsOn(for window: WindowUUID) -> Bool { + return privateBrowsingState[window] ?? false + } + + // MARK: - Automatic brightness theme functions + public func setAutomaticBrightness(isOn: Bool) { + guard automaticBrightnessIsOn != isOn else { return } + userDefaults.set(isOn, forKey: ThemeKeys.AutomaticBrightness.isOn) + applyThemeUpdatesToWindows() + } + + public func setAutomaticBrightnessValue(_ value: Float) { + userDefaults.set(value, forKey: ThemeKeys.AutomaticBrightness.thresholdValue) + applyThemeUpdatesToWindows() + } + + private func getThemeTypeBasedOnBrightness() -> ThemeType { + return Float(UIScreen.main.brightness) < automaticBrightnessValue ? .dark : .light + } + + // MARK: - Window specific functions + public func windowNonspecificTheme() -> Theme { + switch getUserManualTheme() { + case .dark, .nightMode, .privateMode: return EcosiaDarkTheme() + case .light: return EcosiaLightTheme() + } + } + + public func windowDidClose(uuid: WindowUUID) { + windows.removeValue(forKey: uuid) + } + + public func setWindow(_ window: UIWindow, for uuid: WindowUUID) { + windows[uuid] = window + updateSavedTheme(to: getUserManualTheme()) + applyThemeChanges(for: uuid, using: determineThemeType(for: uuid)) + } + + // MARK: - Private helper methods + + private func updateSavedTheme(to newTheme: ThemeType) { + userDefaults.set(newTheme.rawValue, forKey: ThemeKeys.themeName) + } + + private func applyThemeChanges(for window: WindowUUID, using newTheme: ThemeType) { + // Overwrite the user interface style on the window attached to our scene + // once we have multiple scenes we need to update all of them + let style = self.getCurrentTheme(for: window).type.getInterfaceStyle() + self.windows[window]?.overrideUserInterfaceStyle = style + notifyCurrentThemeDidChange(for: window) + } + + private func notifyCurrentThemeDidChange(for window: WindowUUID) { + mainQueue.ensureMainThread { [weak self] in + self?.notificationCenter.post( + name: .ThemeDidChange, + withUserInfo: window.userInfo + ) + } + } + + private func determineThemeType(for window: WindowUUID) -> ThemeType { + if getPrivateThemeIsOn(for: window) { return .privateMode } + if nightModeIsOn { return .nightMode } + if systemThemeIsOn { return getThemeTypeBasedOnSystem() } + if automaticBrightnessIsOn { return getThemeTypeBasedOnBrightness() } + + return getUserManualTheme() + } + + private func getThemeFrom(type: ThemeType) -> Theme { + switch type { + case .light: + return EcosiaLightTheme() + case .dark: + return EcosiaDarkTheme() + case .nightMode: + return EcosiaDarkTheme() + case .privateMode: + return EcosiaLightTheme() + } + } + + // MARK: - Notifiable + + public func handleNotifications(_ notification: Notification) { + switch notification.name { + case UIScreen.brightnessDidChangeNotification, + UIApplication.didBecomeActiveNotification: + applyThemeUpdatesToWindows() + default: + return + } + } +} diff --git a/firefox-ios/Client/Ecosia/UI/WhatsNew/DataProvider/WhatsNewDataProvider.swift b/firefox-ios/Client/Ecosia/UI/WhatsNew/DataProvider/WhatsNewDataProvider.swift new file mode 100644 index 0000000000000..206f0ac584845 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/WhatsNew/DataProvider/WhatsNewDataProvider.swift @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// A protocol defining the requirements for an object that provides What's New data. +/// +/// Types conforming to `WhatsNewDataProvider` are responsible for +/// fetching or generating an array of `WhatsNewItem` objects. These objects +/// represent individual features or updates that are new in a given app version. +/// +/// - Throws: An error if the data cannot be fetched or generated. +/// +protocol WhatsNewDataProvider { + /// Fetches or generates an array of `WhatsNewItem` objects. + /// + /// Implement this method to specify how What's New items should be + /// retrieved or generated. For example, this could involve fetching + /// data from a server or reading it from a local data store. + /// + /// - Returns: An array of `WhatsNewItem` objects that encapsulate individual features or updates. + /// - Throws: An error if data retrieval or generation fails. + func getWhatsNewItemsInRange() throws -> [WhatsNewItem] + + /// A Boolean value indicating whether the What's New page should be shown. + /// + /// This value can be used to determine if the user should be presented with the What's New page. + /// It's based on conditions such as whether there are new items since the last app update, + /// or user-specific criteria. + /// + /// - Returns: `true` if the What's New page should be shown; otherwise, `false`. + var shouldShowWhatsNewPage: Bool { get } +} diff --git a/firefox-ios/Client/Ecosia/UI/WhatsNew/DataProvider/WhatsNewLocalDataProvider.swift b/firefox-ios/Client/Ecosia/UI/WhatsNew/DataProvider/WhatsNewLocalDataProvider.swift new file mode 100644 index 0000000000000..d95474226d48b --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/WhatsNew/DataProvider/WhatsNewLocalDataProvider.swift @@ -0,0 +1,122 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +import Common +import Ecosia + +/// A local data provider for fetching What's New items based on app version updates. +final class WhatsNewLocalDataProvider: WhatsNewDataProvider { + + /// The version from which the app was last updated. + private var fromVersion: Version? { + Version(EcosiaInstallType.persistedCurrentVersion()) + } + + /// The current version of the app. + private var toVersion: Version { + Version(versionProvider.version)! + } + + /// A computed property to determine whether the "What's New" page should be displayed. + /// - Returns: `true` if the What's New page should be shown; otherwise, `false`. + var shouldShowWhatsNewPage: Bool { + guard EcosiaInstallType.get() == .upgrade else { + markPreviousVersionsAsSeen() + return false + } + + // Are there items to be shown in the range? + guard let items = try? getWhatsNewItemsInRange(), !items.isEmpty else { return false } + + let shownVersions = User.shared.whatsNewItemsVersionsShown + let versionsInRange = getVersionRange().map { $0.description } + + // Are all versions in the range contained in the shown versions? + let allVersionsShown = Set(versionsInRange).subtracting(shownVersions).isEmpty + + return !allVersionsShown + } + + /// The current app version provider from which the Ecosia App Version is retrieved + private(set) var versionProvider: AppVersionInfoProvider + + /// Default initializer. + /// - Parameters: + /// - versionProvider: The current app version provider. Defaults to `DefaultAppVersionInfoProvider` + /// - whatsNewItems: The items we would like to attempt to show in the update sheet, split by version + init(versionProvider: AppVersionInfoProvider = DefaultAppVersionInfoProvider(), + whatsNewItems: [Version: [WhatsNewItem]] = defaultWhatsNewItems) { + self.versionProvider = versionProvider + self.whatsNewItems = whatsNewItems + } + + /// The items we would like to attempt to show in the update sheet + private var whatsNewItems: [Version: [WhatsNewItem]] + + private static let defaultWhatsNewItems = [ + Version("9.0.0")!: [ + WhatsNewItem(image: UIImage(named: "tree"), + title: .localized(.whatsNewFirstItemTitle9_0_0), + subtitle: .localized(.whatsNewFirstItemDescription9_0_0)), + WhatsNewItem(image: UIImage(named: "customisation"), + title: .localized(.whatsNewSecondItemTitle9_0_0), + subtitle: .localized(.whatsNewSecondItemDescription9_0_0)) + ], + Version("10.0.0")!: [ + WhatsNewItem(image: UIImage(named: StandardImageIdentifiers.Large.pageZoom), + title: .localized(.whatsNewFirstItemTitle10_0_0), + subtitle: .localized(.whatsNewFirstItemDescription10_0_0)), + WhatsNewItem(image: UIImage(named: StandardImageIdentifiers.Large.lock), + title: .localized(.whatsNewSecondItemTitle10_0_0), + subtitle: .localized(.whatsNewSecondItemDescription10_0_0)) + ] + ] + + /// Fetches an array of What's New items to display. + /// + /// - Throws: An error if fetching fails. + /// + /// - Returns: An array of `WhatsNewItem` to display. + func getWhatsNewItemsInRange() throws -> [WhatsNewItem] { + // Get the version range and corresponding What's New items. + let versionRange = getVersionRange() + var items: [WhatsNewItem] = [] + for version in versionRange { + if let newItems = whatsNewItems[version] { + items.append(contentsOf: newItems) + } + } + return items + } + + /// Private helper to fetch version range. + /// + /// - Returns: An array of `Version` between from and to, inclusive. + func getVersionRange() -> [Version] { + + // Ensure `fromVersion` is available; otherwise, return an empty version range. + guard let fromVersion else { return [] } + + // Gather all versions + let allVersions = Array(whatsNewItems.keys).sorted() + + // Find the index of the version immediately after `fromVersion` + guard let fromIndex = allVersions.firstIndex(where: { $0 > fromVersion }) else { return [] } + + // Find the index of the version immediately before or equal to `toVersion` + guard let toIndex = allVersions.lastIndex(where: { $0 <= toVersion }) else { return [] } + + // Return the range between `fromIndex` (excluded) and `toIndex` (included) + return Array(allVersions[fromIndex.. Int { + return viewModel.items.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: WhatsNewCell.cellIdentifier, for: indexPath) as! WhatsNewCell + let item = viewModel.items[indexPath.row] + cell.configure(with: item) + cell.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + return cell + } +} + +// MARK: - Themeable + +extension WhatsNewViewController { + + func applyTheme() { + let theme = themeManager.getCurrentTheme(for: windowUUID) + view.backgroundColor = theme.colors.ecosia.backgroundPrimary + topContainerView.backgroundColor = theme.colors.ecosia.backgroundTertiary + tableView.backgroundColor = theme.colors.ecosia.backgroundPrimary + tableView.separatorColor = .clear + knob.backgroundColor = theme.colors.ecosia.textSecondary + closeButton.backgroundColor = theme.colors.ecosia.backgroundPrimary + closeButton.tintColor = theme.colors.ecosia.textPrimary + footerButton.backgroundColor = theme.colors.ecosia.brandPrimary + footerButton.setTitleColor(theme.colors.ecosia.textInversePrimary, for: .normal) + headerLabelContainerView.backgroundColor = theme.colors.ecosia.backgroundPrimary + secondImageView.tintColor = theme.colors.ecosia.backgroundPrimary + } +} + +// MARK: - Presentation + +extension WhatsNewViewController { + + static func presentOn(_ viewController: UIViewController, + viewModel: WhatsNewViewModel, + windowUUID: WindowUUID) { + + guard let whatsNewDelegateViewController = viewController as? WhatsNewViewDelegate else { return } + let sheet = WhatsNewViewController(viewModel: viewModel, + delegate: whatsNewDelegateViewController, + windowUUID: windowUUID) + sheet.modalPresentationStyle = .automatic + + // iPhone + if sheet.traitCollection.userInterfaceIdiom == .phone { + if #available(iOS 16.0, *), let sheet = sheet.sheetPresentationController { + let custom = UISheetPresentationController.Detent.custom { context in + return UX.PreferredContentSize.iPhoneCustomDetentHeight + } + sheet.detents = [custom, .large()] + } else if #available(iOS 15.0, *), let sheet = sheet.sheetPresentationController { + sheet.detents = [.large()] + } + } + + // iPad + if sheet.traitCollection.userInterfaceIdiom == .pad { + sheet.modalPresentationStyle = .formSheet + sheet.preferredContentSize = .init(width: UX.PreferredContentSize.iPadWidth, + height: UX.PreferredContentSize.iPadHeight) + } + + viewController.present(sheet, animated: true, completion: nil) + } +} diff --git a/firefox-ios/Client/Ecosia/UI/WhatsNew/WhatsNewViewModel.swift b/firefox-ios/Client/Ecosia/UI/WhatsNew/WhatsNewViewModel.swift new file mode 100644 index 0000000000000..6fd8caba875f3 --- /dev/null +++ b/firefox-ios/Client/Ecosia/UI/WhatsNew/WhatsNewViewModel.swift @@ -0,0 +1,14 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +final class WhatsNewViewModel { + var items: [WhatsNewItem] + + init(provider: WhatsNewDataProvider) { + let providerItems = try? provider.getWhatsNewItemsInRange() + self.items = providerItems ?? [] + } +} diff --git a/firefox-ios/Client/Experiments/initial_experiments.json b/firefox-ios/Client/Experiments/initial_experiments.json index 7d5eba4078c5e..e86fdee98a7eb 100644 --- a/firefox-ios/Client/Experiments/initial_experiments.json +++ b/firefox-ios/Client/Experiments/initial_experiments.json @@ -75,34 +75,58 @@ "featureValidationOptOut": false, "localizations": null, "locales": null, - "publishedDate": "2024-03-05T16:11:17.007782Z" + "publishedDate": "2024-03-05T16:11:17.007782Z", + "isFirefoxLabsOptIn": false, + "firefoxLabsTitle": null, + "firefoxLabsDescription": null }, { "schemaVersion": "1.12.0", - "slug": "ios-search-bar-placement-v2-treatment-a-rollout", - "id": "ios-search-bar-placement-v2-treatment-a-rollout", + "slug": "ios-optimize-onboarding-card-order", + "id": "ios-optimize-onboarding-card-order", "arguments": {}, "application": "org.mozilla.ios.Firefox", "appName": "firefox_ios", "appId": "org.mozilla.ios.Firefox", "channel": "release", - "userFacingName": "iOS Search Bar Placement v2 - Treatment A Rollout", - "userFacingDescription": "Search Bar Placement", + "userFacingName": "iOS Optimize Onboarding Card Order", + "userFacingDescription": "Experiment changing the order of onboarding cards", "isEnrollmentPaused": false, - "isRollout": true, + "isRollout": false, "bucketConfig": { "randomizationUnit": "nimbus_id", - "namespace": "ios-search-release-mobile_first_run-rollout-1", + "namespace": "ios-onboarding-framework-feature-release-19", "start": 0, "count": 10000, "total": 10000 }, "featureIds": [ - "search" + "onboarding-framework-feature" ], "probeSets": [], - "outcomes": [], + "outcomes": [ + { + "slug": "onboarding", + "priority": "primary" + } + ], "branches": [ + { + "slug": "control", + "ratio": 1, + "feature": { + "featureId": "this-is-included-for-mobile-pre-96-support", + "enabled": false, + "value": {} + }, + "features": [ + { + "featureId": "onboarding-framework-feature", + "enabled": true, + "value": {} + } + ] + }, { "slug": "treatment-a", "ratio": 1, @@ -113,14 +137,126 @@ }, "features": [ { - "featureId": "search", + "featureId": "onboarding-framework-feature", "enabled": true, "value": { - "awesome-bar": { - "position": { - "is-position-feature-enabled": true, - "is-bottom": false, - "is-toolbar-cfr-on": false + "cards": { + "welcome": { + "order": 10 + }, + "customization-theme": { + "order": 20 + }, + "customization-toolbar": { + "order": 30 + }, + "sign-to-sync": { + "order": 40 + }, + "notification-permissions": { + "order": 50 + } + } + } + } + ] + }, + { + "slug": "treatment-b", + "ratio": 1, + "feature": { + "featureId": "this-is-included-for-mobile-pre-96-support", + "enabled": false, + "value": {} + }, + "features": [ + { + "featureId": "onboarding-framework-feature", + "enabled": true, + "value": { + "cards": { + "welcome": { + "order": 10 + }, + "customization-toolbar": { + "order": 20 + }, + "customization-theme": { + "order": 30 + }, + "sign-to-sync": { + "order": 40 + }, + "notification-permissions": { + "order": 50 + } + } + } + } + ] + }, + { + "slug": "treatment-c", + "ratio": 1, + "feature": { + "featureId": "this-is-included-for-mobile-pre-96-support", + "enabled": false, + "value": {} + }, + "features": [ + { + "featureId": "onboarding-framework-feature", + "enabled": true, + "value": { + "cards": { + "welcome": { + "order": 10 + }, + "customization-toolbar": { + "order": 20 + }, + "customization-theme": { + "order": 30 + }, + "notification-permissions": { + "order": 40 + }, + "sign-to-sync": { + "order": 50 + } + } + } + } + ] + }, + { + "slug": "treatment-d", + "ratio": 1, + "feature": { + "featureId": "this-is-included-for-mobile-pre-96-support", + "enabled": false, + "value": {} + }, + "features": [ + { + "featureId": "onboarding-framework-feature", + "enabled": true, + "value": { + "cards": { + "welcome": { + "order": 10 + }, + "customization-theme": { + "order": 20 + }, + "customization-toolbar": { + "order": 30 + }, + "notification-permissions": { + "order": 40 + }, + "sign-to-sync": { + "order": 50 } } } @@ -128,69 +264,47 @@ ] } ], - "targeting": "((is_already_enrolled) || ((isFirstRun == 'true') && (app_version|versionCompare('113.!') >= 0)))", - "startDate": "2023-04-17", + "targeting": "((is_already_enrolled) || ((isFirstRun == 'true' && is_phone) && (app_version|versionCompare('131.!') >= 0) && (enrollments_map['long-term-holdback-2024-h2-velocity-ios'] == 'delivery')))", + "startDate": "2024-10-18", "enrollmentEndDate": null, "endDate": null, "proposedDuration": 28, - "proposedEnrollment": 7, - "referenceBranch": "treatment-a", + "proposedEnrollment": 14, + "referenceBranch": "control", "featureValidationOptOut": false, "localizations": null, "locales": null, - "publishedDate": null + "publishedDate": "2024-10-18T19:26:21.181472Z", + "isFirefoxLabsOptIn": false, + "firefoxLabsTitle": null, + "firefoxLabsDescription": null }, { "schemaVersion": "1.12.0", - "slug": "ios-set-as-default-browser-onboarding-optimization-for-1312", - "id": "ios-set-as-default-browser-onboarding-optimization-for-1312", + "slug": "ios-search-bar-placement-v2-treatment-a-rollout", + "id": "ios-search-bar-placement-v2-treatment-a-rollout", "arguments": {}, "application": "org.mozilla.ios.Firefox", "appName": "firefox_ios", "appId": "org.mozilla.ios.Firefox", "channel": "release", - "userFacingName": "iOS \"Set as Default Browser\" Onboarding Optimization for 131.2", - "userFacingDescription": "Experiment adding a direct link to settings and instructions in the Set to Default Card onboarding card", + "userFacingName": "iOS Search Bar Placement v2 - Treatment A Rollout", + "userFacingDescription": "Search Bar Placement", "isEnrollmentPaused": false, - "isRollout": false, + "isRollout": true, "bucketConfig": { "randomizationUnit": "nimbus_id", - "namespace": "ios-onboarding-framework-feature-release-18", + "namespace": "ios-search-release-mobile_first_run-rollout-1", "start": 0, "count": 10000, "total": 10000 }, "featureIds": [ - "onboarding-framework-feature" + "search" ], "probeSets": [], - "outcomes": [ - { - "slug": "default_browser", - "priority": "primary" - }, - { - "slug": "onboarding", - "priority": "primary" - } - ], + "outcomes": [], "branches": [ - { - "slug": "control", - "ratio": 1, - "feature": { - "featureId": "this-is-included-for-mobile-pre-96-support", - "enabled": false, - "value": {} - }, - "features": [ - { - "featureId": "onboarding-framework-feature", - "enabled": true, - "value": {} - } - ] - }, { "slug": "treatment-a", "ratio": 1, @@ -201,20 +315,14 @@ }, "features": [ { - "featureId": "onboarding-framework-feature", + "featureId": "search", "enabled": true, "value": { - "cards": { - "welcome": { - "title": "Make Firefox your default browser", - "body": "Follow the steps above to make Firefox your default browser.", - "image": "set-default-steps", - "buttons": { - "primary": { - "title": "Open Settings", - "action": "open-ios-fx-settings" - } - } + "awesome-bar": { + "position": { + "is-position-feature-enabled": true, + "is-bottom": false, + "is-toolbar-cfr-on": false } } } @@ -222,17 +330,20 @@ ] } ], - "targeting": "((is_already_enrolled) || ((isFirstRun == 'true') && (app_version|versionCompare('131.2.0') >= 0) && (language in ['en']) && (region in ['AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BF', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'DJ', 'DM', 'DO', 'DZ', 'EC', 'EG', 'EH', 'ER', 'ET', 'FJ', 'FK', 'FM', 'FO', 'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HT', 'ID', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LY', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PM', 'PN', 'PR', 'PS', 'PW', 'PY', 'QA', 'RE', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SG', 'SH', 'SJ', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SX', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW']) && (enrollments_map['long-term-holdback-2024-h2-velocity-ios'] == 'delivery')))", - "startDate": "2024-10-09", + "targeting": "((is_already_enrolled) || ((isFirstRun == 'true') && (app_version|versionCompare('113.!') >= 0)))", + "startDate": "2023-04-17", "enrollmentEndDate": null, "endDate": null, "proposedDuration": 28, - "proposedEnrollment": 14, - "referenceBranch": "control", + "proposedEnrollment": 7, + "referenceBranch": "treatment-a", "featureValidationOptOut": false, "localizations": null, "locales": null, - "publishedDate": "2024-10-09T19:01:25.915786Z" + "publishedDate": null, + "isFirefoxLabsOptIn": false, + "firefoxLabsTitle": null, + "firefoxLabsDescription": null }, { "schemaVersion": "1.12.0", @@ -303,7 +414,10 @@ "featureValidationOptOut": false, "localizations": null, "locales": null, - "publishedDate": "2024-09-19T17:57:04.754326Z" + "publishedDate": "2024-09-19T17:57:04.754326Z", + "isFirefoxLabsOptIn": false, + "firefoxLabsTitle": null, + "firefoxLabsDescription": null } ] } diff --git a/firefox-ios/Client/Extensions/String+Extension.swift b/firefox-ios/Client/Extensions/String+Extension.swift index 2043e8076f9da..275c6d1bc9a1f 100644 --- a/firefox-ios/Client/Extensions/String+Extension.swift +++ b/firefox-ios/Client/Extensions/String+Extension.swift @@ -3,7 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Common -import Foundation +import UIKit extension String { /// Returns an attributed string in which the first occurrence of the given diff --git a/firefox-ios/Client/Extensions/UIEdgeInsets+Extension.swift b/firefox-ios/Client/Extensions/UIEdgeInsets+Extension.swift index 4552424921b2e..e281738a6c0ec 100644 --- a/firefox-ios/Client/Extensions/UIEdgeInsets+Extension.swift +++ b/firefox-ios/Client/Extensions/UIEdgeInsets+Extension.swift @@ -8,4 +8,9 @@ extension UIEdgeInsets { init(equalInset inset: CGFloat) { self.init(top: inset, left: inset, bottom: inset, right: inset) } + + // Ecosia: Add extension + init(horizontal inset: CGFloat) { + self.init(top: 0, left: inset, bottom: 0, right: inset) + } } diff --git a/firefox-ios/Client/FeatureFlags/LegacyFeatureFlagsManager.swift b/firefox-ios/Client/FeatureFlags/LegacyFeatureFlagsManager.swift index 256f4bc1d820c..8e70279e66a9e 100644 --- a/firefox-ios/Client/FeatureFlags/LegacyFeatureFlagsManager.swift +++ b/firefox-ios/Client/FeatureFlags/LegacyFeatureFlagsManager.swift @@ -55,6 +55,14 @@ class LegacyFeatureFlagsManager: HasNimbusFeatureFlags { let nimbusSetting = getNimbusOrDebugSetting(with: feature) let userSetting = feature.isUserEnabled(using: nimbusFlags) + // Ecosia: Turn off specific feature flags + switch featureID { + case .splashScreen, .feltPrivacySimplifiedUI, .microsurvey: + return false + default: + break + } + switch channelsToCheck { case .buildOnly: return nimbusSetting diff --git a/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift b/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift index 9c9dc3fd5ae2f..3a0be07dfb398 100644 --- a/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift +++ b/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift @@ -50,7 +50,8 @@ enum NimbusFeatureFlagID: String, CaseIterable { // Add flags here if you want to toggle them in the `FeatureFlagsDebugViewController` var debugKey: String? { switch self { - case .closeRemoteTabs, + case .bookmarksRefactor, + .closeRemoteTabs, .microsurvey, .homepageRebuild, .menuRefactor, diff --git a/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift b/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift index c45d69eeae908..1f1e80a5e8783 100644 --- a/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift +++ b/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift @@ -35,7 +35,10 @@ final class AddressListViewModel: ObservableObject, FeatureFlaggable { private let logger: Logger - var isEditingFeatureEnabled: Bool { featureFlags.isFeatureEnabled(.addressAutofillEdit, checking: .buildOnly) } + var isEditingFeatureEnabled: Bool { + AddressLocaleFeatureValidator.isValidRegion() || + featureFlags.isFeatureEnabled(.addressAutofillEdit, checking: .buildOnly) + } var addressSelectionCallback: ((UnencryptedAddressFields) -> Void)? var saveAction: ((@escaping (UpdatableAddressFields) -> Void) -> Void)? diff --git a/firefox-ios/Client/Frontend/Browser/BackForwardTableViewCell.swift b/firefox-ios/Client/Frontend/Browser/BackForwardTableViewCell.swift index 53f540b21fcd3..091a74f5072e4 100644 --- a/firefox-ios/Client/Frontend/Browser/BackForwardTableViewCell.swift +++ b/firefox-ios/Client/Frontend/Browser/BackForwardTableViewCell.swift @@ -110,7 +110,9 @@ class BackForwardTableViewCell: UITableViewCell, ReusableCell, ThemeApplicable { if let url = URL(string: viewModel.site.url, invalidCharacters: false), InternalURL(url)?.isAboutHomeURL == true { - faviconView.manuallySetImage(UIImage(named: ImageIdentifiers.firefoxFavicon) ?? UIImage()) + // Ecosia: Update image with Ecosia icon + // faviconView.manuallySetImage(UIImage(named: ImageIdentifiers.firefoxFavicon) ?? UIImage()) + faviconView.manuallySetImage(.init(named: "iconLogo", in: .ecosia, with: nil) ?? .init()) } else { faviconView.setFavicon(FaviconImageViewModel(siteURLString: viewModel.site.url, faviconCornerRadius: UX.faviconCornerRadius)) diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift index d47e14bd0a890..f259c94f7ce2f 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift @@ -41,7 +41,10 @@ extension BrowserViewController { } private func setupFindInPage() { + /* Ecosia: Custom UI for FindInPageBar let findInPageBar = FindInPageBar() + */ + let findInPageBar = EcosiaFindInPageBar() self.findInPageBar = findInPageBar findInPageBar.delegate = self @@ -65,7 +68,10 @@ extension BrowserViewController { findInPageBar.layoutIfNeeded() } + /* Ecosia: Custom UI for FindInPageBar private func removeFindInPage(_ findInPageBar: FindInPageBar, tab: Tab? = nil) { + */ + private func removeFindInPage(_ findInPageBar: EcosiaFindInPageBar, tab: Tab? = nil) { findInPageBar.endEditing(true) let tab = tab ?? tabManager.selectedTab guard let webView = tab?.webView else { return } @@ -76,6 +82,7 @@ extension BrowserViewController { } } +/* Ecosia: Custom UI for FindInPageBar extension BrowserViewController: FindInPageBarDelegate, FindInPageHelperDelegate { func findInPage(_ findInPage: FindInPageBar, didTextChange text: String) { find(text, function: "find") @@ -109,3 +116,39 @@ extension BrowserViewController: FindInPageBarDelegate, FindInPageHelperDelegate findInPageBar?.totalResults = totalResults } } +*/ + +extension BrowserViewController: EcosiaFindInPageBarDelegate, FindInPageHelperDelegate { + + func findInPage(_ findInPage: EcosiaFindInPageBar, didTextChange text: String) { + find(text, function: "find") + } + + func findInPage(_ findInPage: EcosiaFindInPageBar, didFindNextWithText text: String) { + findInPageBar?.endEditing(true) + find(text, function: "findNext") + } + + func findInPage(_ findInPage: EcosiaFindInPageBar, didFindPreviousWithText text: String) { + findInPageBar?.endEditing(true) + find(text, function: "findPrevious") + } + + func findInPageDidPressClose(_ findInPage: EcosiaFindInPageBar) { + updateFindInPageVisibility(isVisible: false) + } + + fileprivate func find(_ text: String, function: String) { + guard let webView = tabManager.selectedTab?.webView else { return } + let escaped = text.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\"") + webView.evaluateJavascriptInDefaultContentWorld("__firefox__.\(function)(\"\(escaped)\")") + } + + func findInPageHelper(_ findInPageHelper: FindInPageHelper, didUpdateCurrentResult currentResult: Int) { + findInPageBar?.currentResult = currentResult + } + + func findInPageHelper(_ findInPageHelper: FindInPageHelper, didUpdateTotalResults totalResults: Int) { + findInPageBar?.totalResults = totalResults + } +} diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift index 849be800cf020..ddec80c08d98d 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift @@ -340,6 +340,15 @@ extension BrowserViewController: TabToolbarDelegate, PhotonActionSheetProtocol { // MARK: - ToolbarActionMenuDelegate extension BrowserViewController: ToolBarActionMenuDelegate, UIDocumentPickerDelegate { + /* + Ecosia: Add ToolBarActionMenuDelegate additional delegate function + to handle the opening of URLs as part of the current tab + */ + func openURLInCurrentTab(_ url: URL?) { + guard let url else { return } + tabManager.selectedTab?.loadRequest(URLRequest(url: url)) + } + func updateToolbarState() { updateToolbarStateForTraitCollection(view.traitCollection) } @@ -352,6 +361,8 @@ extension BrowserViewController: ToolBarActionMenuDelegate, UIDocumentPickerDele switch toastAction { case .bookmarkPage: let viewModel = ButtonToastViewModel(labelText: message, + // Ecosia: Re-add image so toast looks like v104 + imageName: StandardImageIdentifiers.Large.bookmarkSlash, buttonText: .BookmarksEdit, textAlignment: .left) let toast = ButtonToast(viewModel: viewModel, @@ -361,6 +372,8 @@ extension BrowserViewController: ToolBarActionMenuDelegate, UIDocumentPickerDele self.show(toast: toast) case .removeBookmark: let viewModel = ButtonToastViewModel(labelText: message, + // Ecosia: Re-add image so toast looks like v104 + imageName: "bookmarksEmpty", buttonText: .UndoString, textAlignment: .left) let toast = ButtonToast(viewModel: viewModel, diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift index 176aeba556535..241846cb50336 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift @@ -4,7 +4,9 @@ import Shared import Storage +/* Ecosia: Remove Glean import Glean + */ import Common import ComponentLibrary @@ -70,6 +72,8 @@ extension BrowserViewController: URLBarDelegate { !self.urlBar.inOverlayMode else { return } // We're not showing the top tabs; show a toast to quick switch to the fresh new tab. let viewModel = ButtonToastViewModel(labelText: .ContextMenuButtonToastNewTabOpenedLabelText, + // Ecosia: Re-add image so toast looks like v104 + imageName: "tabs", buttonText: .ContextMenuButtonToastNewTabOpenedButtonText) let toast = ButtonToast(viewModel: viewModel, theme: self.currentTheme(), @@ -267,9 +271,11 @@ extension BrowserViewController: URLBarDelegate { Experiments.events.recordEvent(BehavioralTargetingEvent.performedSearch) + /* Ecosia: Remove Glean GleanMetrics.Search .counts["\(engine.engineID ?? "custom").\(SearchLocation.actionBar.rawValue)"] .add() + */ searchTelemetry?.shouldSetUrlTypeSearch = true let searchData = LegacyTabGroupData(searchTerm: text, diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift index 7558ebb130ec4..4372336fc6ac9 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift @@ -8,6 +8,7 @@ import Common import Shared import UIKit import Photos +import Ecosia // MARK: - WKUIDelegate extension BrowserViewController: WKUIDelegate { @@ -30,6 +31,8 @@ extension BrowserViewController: WKUIDelegate { return nil } + guard !isPayPalPopUp(navigationAction) else { return nil } + if navigationAction.canOpenExternalApp, let url = navigationAction.request.url { UIApplication.shared.open(url) return nil @@ -147,51 +150,85 @@ extension BrowserViewController: WKUIDelegate { contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo, completionHandler: @escaping (UIContextMenuConfiguration?) -> Void ) { - guard let url = elementInfo.linkURL else { return } - - completionHandler( - UIContextMenuConfiguration( - identifier: nil, - previewProvider: { - guard self.profile.prefs.boolForKey(PrefsKeys.ContextMenuShowLinkPreviews) ?? true else { return nil } - - let previewViewController = UIViewController() - previewViewController.view.isUserInteractionEnabled = false - let clonedWebView = WKWebView(frame: webView.frame, configuration: webView.configuration) - - previewViewController.view.addSubview(clonedWebView) - NSLayoutConstraint.activate([ - clonedWebView.topAnchor.constraint(equalTo: previewViewController.view.topAnchor), - clonedWebView.leadingAnchor.constraint(equalTo: previewViewController.view.leadingAnchor), - clonedWebView.trailingAnchor.constraint(equalTo: previewViewController.view.trailingAnchor), - clonedWebView.bottomAnchor.constraint(equalTo: previewViewController.view.bottomAnchor) - ]) - clonedWebView.translatesAutoresizingMaskIntoConstraints = false - - clonedWebView.load(URLRequest(url: url)) - - return previewViewController - }, - actionProvider: { [self] (suggested) -> UIMenu? in - guard let currentTab = tabManager.selectedTab, - let contextHelper = currentTab.getContentScript( - name: ContextMenuHelper.name() - ) as? ContextMenuHelper, - let elements = contextHelper.elements - else { return nil } - - let isPrivate = currentTab.isPrivate - - let actions = createActions(isPrivate: isPrivate, - url: url, - addTab: self.addTab, - title: elements.title, - image: elements.image, - currentTab: currentTab, - webView: webView) - return UIMenu(title: url.absoluteString, children: actions) - }) - ) + guard let url = elementInfo.linkURL, + let currentTab = tabManager.selectedTab, + let contextHelper = currentTab.getContentScript( + name: ContextMenuHelper.name() + ) as? ContextMenuHelper, + let elements = contextHelper.elements + else { + completionHandler(nil) + return + } + completionHandler(contextMenuConfiguration(for: url, webView: webView, elements: elements)) + } + + func webView(_ webView: WKWebView, + requestMediaCapturePermissionFor origin: WKSecurityOrigin, + initiatedByFrame frame: WKFrameInfo, + type: WKMediaCaptureType, + decisionHandler: @escaping (WKPermissionDecision) -> Void) { + // If the tab isn't the selected one or we're on the homepage, do not show the media capture prompt + guard tabManager.selectedTab?.webView === webView, !contentContainer.hasAnyHomepage else { + decisionHandler(.deny) + return + } + + decisionHandler(.prompt) + } + + // MARK: - Helpers + private func contextMenuConfiguration(for url: URL, + webView: WKWebView, + elements: ContextMenuHelper.Elements) -> UIContextMenuConfiguration { + return UIContextMenuConfiguration(identifier: nil, + previewProvider: contextMenuPreviewProvider(for: url, webView: webView), + actionProvider: contextMenuActionProvider(for: url, + webView: webView, + elements: elements)) + } + + private func contextMenuActionProvider(for url: URL, + webView: WKWebView, + elements: ContextMenuHelper.Elements) -> UIContextMenuActionProvider { + return { [self] (suggested) -> UIMenu? in + guard let currentTab = tabManager.selectedTab else { return nil } + + let isPrivate = currentTab.isPrivate + + let actions = createActions(isPrivate: isPrivate, + url: url, + addTab: self.addTab, + title: elements.title, + image: elements.image, + currentTab: currentTab, + webView: webView) + return UIMenu(title: url.normalizedHost ?? url.absoluteString, children: actions) + } + } + + private func contextMenuPreviewProvider(for url: URL, webView: WKWebView) -> UIContextMenuContentPreviewProvider? { + let provider: UIContextMenuContentPreviewProvider = { + guard self.profile.prefs.boolForKey(PrefsKeys.ContextMenuShowLinkPreviews) ?? true else { return nil } + + let previewViewController = UIViewController() + previewViewController.view.isUserInteractionEnabled = false + let clonedWebView = WKWebView(frame: webView.frame, configuration: webView.configuration) + + previewViewController.view.addSubview(clonedWebView) + NSLayoutConstraint.activate([ + clonedWebView.topAnchor.constraint(equalTo: previewViewController.view.topAnchor), + clonedWebView.leadingAnchor.constraint(equalTo: previewViewController.view.leadingAnchor), + clonedWebView.trailingAnchor.constraint(equalTo: previewViewController.view.trailingAnchor), + clonedWebView.bottomAnchor.constraint(equalTo: previewViewController.view.bottomAnchor) + ]) + clonedWebView.translatesAutoresizingMaskIntoConstraints = false + + clonedWebView.load(URLRequest(url: url)) + + return previewViewController + } + return provider } func addTab(rURL: URL, isPrivate: Bool, currentTab: Tab) { @@ -262,6 +299,8 @@ extension BrowserViewController: WKUIDelegate { } let viewModel = ButtonToastViewModel(labelText: toastLabelText, + // Ecosia: Re-add image so toast looks like v104 + imageName: "tabs", buttonText: .ContextMenuButtonToastNewTabOpenedButtonText) let toast = ButtonToast(viewModel: viewModel, theme: self.currentTheme(), @@ -331,21 +370,6 @@ extension BrowserViewController: WKUIDelegate { pendingDownloadWebView = webView } - @available(iOS 15, *) - func webView(_ webView: WKWebView, - requestMediaCapturePermissionFor origin: WKSecurityOrigin, - initiatedByFrame frame: WKFrameInfo, - type: WKMediaCaptureType, - decisionHandler: @escaping (WKPermissionDecision) -> Void) { - // If the tab isn't the selected one or we're on the homepage, do not show the media capture prompt - guard tabManager.selectedTab?.webView == webView, !contentContainer.hasLegacyHomepage else { - decisionHandler(.deny) - return - } - - decisionHandler(.prompt) - } - func writeToPhotoAlbum(image: UIImage) { UIImageWriteToSavedPhotosAlbum(image, self, #selector(saveError), nil) } @@ -408,6 +432,7 @@ extension BrowserViewController: WKNavigationDelegate { decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { + guard let url = navigationAction.request.url, let tab = tabManager[webView] else { @@ -430,6 +455,12 @@ extension BrowserViewController: WKNavigationDelegate { } } + // Ecosia: Detect Ecosia-specific URLs (auth, profile, etc.) and trigger native flows + if detectAndHandleEcosiaURL(url, for: tab) { + decisionHandler(.cancel) + return + } + if InternalURL.isValid(url: url) { if navigationAction.navigationType != .backForward, navigationAction.isInternalUnprivileged, @@ -485,6 +516,7 @@ extension BrowserViewController: WKNavigationDelegate { DispatchQueue.main.asyncAfter(deadline: .now() + tabManager.delaySelectingNewPopupTab + 0.1) { // Show only if no other snack bar guard let tab = self.tabManager.selectedTab, tab.bars.isEmpty else { return } + /* Ecosia: Change to native alert controller (showSnackbar() contains the Ecosia change) TimerSnackBar.showAppStoreConfirmationBar( forTab: tab, appStoreURL: url, @@ -495,6 +527,16 @@ extension BrowserViewController: WKNavigationDelegate { self.tabManager.removeTab(tab) } } + */ + self.showSnackbar(forExternalUrl: url, tab: tab) { isOK in + if isOK { + UIApplication.shared.open(url, options: [:]) + } + // If a new window was opened for this URL (it will have no history), close it. + if tab.historyList.isEmpty { + self.tabManager.removeTab(tab) + } + } } return } @@ -564,6 +606,15 @@ extension BrowserViewController: WKNavigationDelegate { } } + // Ecosia: Handle navigation tracking + ecosiaHandleNavigationAction(url: url, navigationAction: navigationAction) + + decisionHandler(.allow) + return + } + + // Ecosia: Allow MarketplaceKit requests for iOS 17.4+ + if #available(iOS 17.4, *), url.scheme == "marketplace-kit" { decisionHandler(.allow) return } @@ -657,11 +708,10 @@ extension BrowserViewController: WKNavigationDelegate { } // Check if this response should be downloaded. - if let downloadHelper = DownloadHelper(request: request, - response: response, - cookieStore: cookieStore, - canShowInWebView: canShowInWebView, - forceDownload: forceDownload) { + if let downloadHelper = DownloadHelper(request: request, response: response, cookieStore: cookieStore), + downloadHelper.shouldDownloadFile(canShowInWebView: canShowInWebView, + forceDownload: forceDownload, + isForMainFrame: navigationResponse.isForMainFrame) { // Clear the pending download web view so that subsequent navigations from the same // web view don't invoke another download. pendingDownloadWebView = nil @@ -827,6 +877,11 @@ extension BrowserViewController: WKNavigationDelegate { let metadataManager = tab.metadataManager else { return } + // Ecosia: Fire in-app search event at commit time (closest to Vue's mounted on web) + if let url = webView.url { + ecosiaHandleDidCommit(url: url) + } + searchTelemetry?.trackTabAndTopSiteSAP(tab, webView: webView) webviewTelemetry.start() tab.url = webView.url @@ -873,6 +928,12 @@ extension BrowserViewController: WKNavigationDelegate { if let tab = tabManager[webView], let metadataManager = tab.metadataManager { + + // Ecosia: Handle navigation completion after URL finishes loading + if let url = webView.url { + ecosiaHandlePageLoadCompletion(url: url) + } + navigateInTab(tab: tab, to: navigation, webViewStatus: .finishedNavigation) // Only update search term data with valid search term data @@ -938,6 +999,7 @@ private extension BrowserViewController { // Use for sms and mailto links, which do not show a confirmation before opening. func showSnackbar(forExternalUrl url: URL, tab: Tab, completion: @escaping (Bool) -> Void) { + /* Ecosia: Change to native alert controller let snackBar = TimerSnackBar(text: .ExternalLinkGenericConfirmation + "\n\(url.absoluteString)", img: nil) let ok = SnackButton(title: .OKString, accessibilityIdentifier: "AppOpenExternal.button.ok") { bar in tab.removeSnackbar(bar) @@ -958,6 +1020,21 @@ private extension BrowserViewController { snackBar.addButton(ok) snackBar.addButton(cancel) tab.addSnackbar(snackBar) + */ + let alert = UIAlertController(title: .localized(.openExternalLinkTitle), + message: String(format: .localized(.openExternalLinkDescription), url.absoluteString), + preferredStyle: .alert) + alert.view.tintColor = currentTheme().colors.ecosia.buttonBackgroundPrimary + let cancelAction = UIAlertAction(title: .localized(.cancel), style: .default) { _ in + completion(false) + } + alert.addAction(cancelAction) + let openAction = UIAlertAction(title: .localized(.open), style: .default) { _ in + completion(true) + } + alert.addAction(openAction) + alert.preferredAction = openAction + present(alert, animated: true) } func shouldRequestBeOpenedAsPopup(_ request: URLRequest) -> Bool { @@ -976,6 +1053,13 @@ private extension BrowserViewController { return false } + // The WKNavigationAction request for Paypal popUp is empty which causes that we open a blank page in + // createWebViewWith. We will show Paypal popUp in page like mobile devices using the mobile User Agent + // so we will block the creation of a new Webview with this check + func isPayPalPopUp(_ navigationAction: WKNavigationAction) -> Bool { + return navigationAction.sourceFrame.request.url?.baseDomain == "paypal.com" + } + func shouldDisplayJSAlertForWebView(_ webView: WKWebView) -> Bool { // Only display a JS Alert if we are selected and there isn't anything being shown return ((tabManager.selectedTab == nil ? false : tabManager.selectedTab!.webView == webView)) diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift index bfd03cd817824..286e4a538a3ab 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift @@ -15,6 +15,7 @@ import Common import ComponentLibrary import Redux import ToolbarKit +import Ecosia import class MozillaAppServices.BookmarkFolderData import class MozillaAppServices.BookmarkItemData @@ -33,6 +34,12 @@ class BrowserViewController: UIViewController, NavigationToolbarContainerDelegate, AddressToolbarContainerDelegate, FeatureFlaggable { + + // Ecosia: Bridges eligibility (checked in decidePolicyFor, where WKNavigationAction + // and its navigationType are available) to the actual tracking call in didCommit. + // Set when eligible, cleared on commit or on the next navigation. + var pendingInappSearchUrl: URL? + private enum UX { static let ShowHeaderTapAreaHeight: CGFloat = 32 static let ActionSheetTitleMaxLength = 120 @@ -83,7 +90,10 @@ class BrowserViewController: UIViewController, var screenshotHelper: ScreenshotHelper! var searchTelemetry: SearchTelemetry? var searchLoader: SearchLoader? + /* Ecosia: Custom UI for FindInPageBar var findInPageBar: FindInPageBar? + */ + var findInPageBar: EcosiaFindInPageBar? var zoomPageBar: ZoomPageBar? var microsurvey: MicrosurveyPromptView? lazy var mailtoLinkHandler = MailtoLinkHandler() @@ -123,6 +133,13 @@ class BrowserViewController: UIViewController, let profile: Profile let tabManager: TabManager let ratingPromptManager: RatingPromptManager + + // Ecosia: Authentication manager for handling login/logout flows + var ecosiaAuth: EcosiaAuth? + + // Ecosia: Retain coordinator for spotlight product tours + var spotlightCoordinator: ProductTourSpotlightCoordinator? + lazy var isTabTrayRefactorEnabled: Bool = TabTrayFlagManager.isRefactorEnabled var isToolbarRefactorEnabled: Bool { return featureFlags.isFeatureEnabled(.toolbarRefactor, checking: .buildOnly) @@ -179,10 +196,8 @@ class BrowserViewController: UIViewController, var topTabsVisible: Bool { return topTabsViewController != nil } - // Backdrop used for displaying greyed background for private tabs - private lazy var webViewContainerBackdrop: UIView = .build { containerBackdrop in - containerBackdrop.alpha = 0 - } + // Window helper used for displaying an opaque background for private tabs. + private lazy var privacyWindowHelper = PrivacyWindowHelper() var keyboardBackdrop: UIView? lazy var scrollController = TabScrollingController(windowUUID: windowUUID) @@ -237,6 +252,13 @@ class BrowserViewController: UIViewController, return keyboardPressesHandlerValue } + // Ecosia: Add Referrals and WhatsNew + let whatsNewDataProvider = WhatsNewLocalDataProvider() + let referrals = Referrals() + + // Ecosia: Make `menuHelper` available at class level + var menuHelper: MainMenuActionHelper? + init( profile: Profile, tabManager: TabManager, @@ -287,6 +309,10 @@ class BrowserViewController: UIViewController, deinit { logger.log("BVC deallocating", level: .info, category: .lifecycle) + + // Ecosia: Unregister window from auth state management + EcosiaAuthWindowRegistry.shared.unregisterWindow(windowUUID) + unsubscribeFromRedux() observedWebViews.forEach({ stopObserving(webView: $0) }) } @@ -365,6 +391,8 @@ class BrowserViewController: UIViewController, isBottomSearchBar = newPositionIsBottom updateViewConstraints() updateHeaderConstraints() + // Ecosia: Update toolbar search button + toolbar.circleButton.config = isBottomSearchBar ? .newTab : .search toolbar.setNeedsDisplay() searchBarView.updateConstraints() updateMicrosurveyConstraints() @@ -379,7 +407,10 @@ class BrowserViewController: UIViewController, @objc fileprivate func appMenuBadgeUpdate() { + /* Ecosia: actionNeeded set to false let isActionNeeded = RustFirefoxAccounts.shared.isActionNeeded + */ + let isActionNeeded = false let showWarningBadge = isActionNeeded if isToolbarRefactorEnabled { @@ -414,7 +445,10 @@ class BrowserViewController: UIViewController, } else { urlBar.topTabsIsShowing = showTopTabs urlBar.setShowToolbar(!showNavToolbar) + /* Ecosia: Change to circle button toolbar.addNewTabButton.isHidden = showNavToolbar + */ + toolbar.circleButton.isHidden = false if showNavToolbar { toolbar.isHidden = false @@ -461,6 +495,9 @@ class BrowserViewController: UIViewController, navigationToolbar.updateBackStatus(webView.canGoBack) navigationToolbar.updateForwardStatus(webView.canGoForward) } + + // Ecosia: Update toolbar search/add button + toolbar.circleButton.config = isBottomSearchBar ? .newTab : .search } func dismissVisibleMenus() { @@ -508,9 +545,11 @@ class BrowserViewController: UIViewController, canShowPrivacyView else { return } - view.bringSubviewToFront(webViewContainerBackdrop) - webViewContainerBackdrop.alpha = 1 contentStackView.alpha = 0 + /* Ecosia: Update background overlay for private tabs + privacyWindowHelper.showWindow(withThemedColor: currentTheme().colors.layer3) + */ + privacyWindowHelper.showWindow(withThemedColor: currentTheme().colors.ecosia.backgroundPrimary) if isToolbarRefactorEnabled { addressToolbarContainer.alpha = 0 @@ -545,8 +584,7 @@ class BrowserViewController: UIViewController, self.presentedViewController?.popoverPresentationController?.containerView?.alpha = 1 self.presentedViewController?.view.alpha = 1 }, completion: { _ in - self.webViewContainerBackdrop.alpha = 0 - self.view.sendSubviewToBack(self.webViewContainerBackdrop) + self.privacyWindowHelper.removeWindow() }) if let tab = tabManager.selectedTab, !tab.isFindInPageMode { @@ -711,6 +749,13 @@ class BrowserViewController: UIViewController, override func viewDidLoad() { super.viewDidLoad() + + // Ecosia: Register window for auth state management + EcosiaAuthWindowRegistry.shared.registerWindow(windowUUID) + + // Ecosia + setupProductTourSpotlightIfNeeded() + KeyboardHelper.defaultHelper.addDelegate(self) trackTelemetry() setupNotifications() @@ -736,7 +781,9 @@ class BrowserViewController: UIViewController, let dropInteraction = UIDropInteraction(delegate: self) view.addInteraction(dropInteraction) + /* Ecosia: Remove SearchTelemetry searchTelemetry = SearchTelemetry(tabManager: tabManager) + */ // Awesomebar Location Telemetry SearchBarSettingsViewModel.recordLocationTelemetry(for: isBottomSearchBar ? .bottom : .top) @@ -752,6 +799,7 @@ class BrowserViewController: UIViewController, statusBarOverlay.hasTopTabs = ToolbarHelper().shouldShowTopTabs(for: traitCollection) statusBarOverlay.applyTheme(theme: theme) + /* Ecosia: Remove Credit Cards Save/Autofill // Feature flag for credit card until we fully enable this feature let autofillCreditCardStatus = featureFlags.isFeatureEnabled( .creditCardAutofillStatus, checking: .buildOnly) @@ -761,6 +809,7 @@ class BrowserViewController: UIViewController, profile.syncManager.updateCreditCardAutofillStatus(value: autofillCreditCardStatus) // Credit card initial setup telemetry creditCardInitialSetupTelemetry() + */ // Send settings telemetry for Fakespot FakespotUtils().addSettingTelemetry() @@ -920,7 +969,7 @@ class BrowserViewController: UIViewController, } func addSubviews() { - view.addSubviews(webViewContainerBackdrop, contentStackView) + view.addSubviews(contentStackView) contentStackView.addArrangedSubview(contentContainer) @@ -949,19 +998,8 @@ class BrowserViewController: UIViewController, override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - // Skip the 'restore tabs' alert on iPad; this avoids some potential UX issues with multi-window. [FXIOS-9079] - let iPadDevice = UIDevice.current.userInterfaceIdiom == .pad - if !iPadDevice && !displayedRestoreTabsAlert && crashedLastLaunch() { - logger.log("The application crashed on last session", - level: .info, - category: .lifecycle) - displayedRestoreTabsAlert = true - showRestoreTabsAlert() - } else { - // Tab restoration is triggered here for new installs, cold launches, or any BVC appearance. - // Note: `restoreTabs()` returns early if `tabs` is not-empty; repeated calls should have no effect. - tabManager.restoreTabs() - } + // Note: `restoreTabs()` returns early if `tabs` is not-empty; repeated calls should have no effect. + tabManager.restoreTabs() switchToolbarIfNeeded() updateTabCountUsingTabManager(tabManager, animated: false) @@ -976,6 +1014,9 @@ class BrowserViewController: UIViewController, override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + // Ecosia: Present Intro logic + presentIntroViewController() + if let toast = self.pendingToast { self.pendingToast = nil show(toast: toast, afterWaiting: ButtonToast.UX.delay) @@ -1163,13 +1204,6 @@ class BrowserViewController: UIViewController, } } - NSLayoutConstraint.activate([ - webViewContainerBackdrop.topAnchor.constraint(equalTo: view.topAnchor), - webViewContainerBackdrop.leadingAnchor.constraint(equalTo: view.leadingAnchor), - webViewContainerBackdrop.trailingAnchor.constraint(equalTo: view.trailingAnchor), - webViewContainerBackdrop.bottomAnchor.constraint(equalTo: view.bottomAnchor) - ]) - NSLayoutConstraint.activate([ contentStackView.topAnchor.constraint(equalTo: header.bottomAnchor), contentStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor), @@ -1289,41 +1323,6 @@ class BrowserViewController: UIViewController, overKeyboardContainer.addKeyboardSpacer(spacerHeight: spacerHeight) } - // Because crashedLastLaunch is sticky, it does not get reset, we need to remember its - // value so that we do not keep asking the user to restore their tabs. - var displayedRestoreTabsAlert = false - - fileprivate func crashedLastLaunch() -> Bool { - return logger.crashedLastLaunch - } - - fileprivate func showRestoreTabsAlert() { - let alert = UIAlertController.restoreTabsAlert( - okayCallback: { _ in - let extra = [TelemetryWrapper.EventExtraKey.isRestoreTabsStarted.rawValue: true] - TelemetryWrapper.recordEvent(category: .action, - method: .tap, - object: .restoreTabsAlert, - extras: extra) - self.isCrashAlertShowing = false - self.tabManager.restoreTabs(true) - }, - noCallback: { _ in - let extra = [TelemetryWrapper.EventExtraKey.isRestoreTabsStarted.rawValue: false] - TelemetryWrapper.recordEvent(category: .action, - method: .tap, - object: .restoreTabsAlert, - extras: extra) - self.isCrashAlertShowing = false - self.tabManager.selectTab(self.tabManager.addTab()) - self.openUrlAfterRestore() - AppEventQueue.signal(event: .tabRestoration(self.tabManager.windowUUID)) - } - ) - self.present(alert, animated: true, completion: nil) - isCrashAlertShowing = true - } - fileprivate func showQueuedAlertIfAvailable() { if let queuedAlertInfo = tabManager.selectedTab?.dequeueJavascriptAlertPrompt() { let alertController = queuedAlertInfo.alertController() @@ -1398,6 +1397,9 @@ class BrowserViewController: UIViewController, overlayManager: overlayManager ) } + + // Ecosia: Let showOverlayCard() execute + browserDelegate?.showPendingOverlayCard(inline: inline) } func showEmbeddedWebview() { @@ -1647,7 +1649,10 @@ class BrowserViewController: UIViewController, } overlayManager.finishEditing(shouldCancelLoading: false) + /* Ecosia: Update url with currentURL (ecosified) if let nav = tab.loadRequest(URLRequest(url: url)) { + */ + if let currentURL = urlBar.currentURL, let nav = tab.loadRequest(URLRequest(url: currentURL)) { self.recordNavigationInTab(tab, navigation: nav, visitType: visitType) } } @@ -1951,16 +1956,6 @@ class BrowserViewController: UIViewController, } } - func lockIconImageName(for tab: Tab?) -> String? { - guard let tab, let hasSecureContent = tab.webView?.hasOnlySecureContent else { return nil } - - let lockIconImageName = hasSecureContent ? - StandardImageIdentifiers.Large.lockFill : - StandardImageIdentifiers.Large.lockSlashFill - - return tab.url?.isReaderModeURL == false ? lockIconImageName : nil - } - func updateReaderModeState(for tab: Tab?, readerModeState: ReaderModeState) { if isToolbarRefactorEnabled { let action = ToolbarAction( @@ -1983,13 +1978,27 @@ class BrowserViewController: UIViewController, StandardImageIdentifiers.Small.notificationDotFill : nil } + var lockIconImageName: String? + var lockIconNeedsTheming = true + + if let hasSecureContent = tab.webView?.hasOnlySecureContent { + lockIconImageName = hasSecureContent ? + StandardImageIdentifiers.Large.lockFill : + StandardImageIdentifiers.Large.lockSlashFill + + let isWebsiteMode = tab.url?.isReaderModeURL == false + lockIconImageName = isWebsiteMode ? lockIconImageName : nil + lockIconNeedsTheming = isWebsiteMode ? hasSecureContent : true + } + let action = ToolbarAction( url: tab.url?.displayURL, isPrivate: tab.isPrivate, isShowingNavigationToolbar: ToolbarHelper().shouldShowNavigationToolbar(for: traitCollection), canGoBack: tab.canGoBack, canGoForward: tab.canGoForward, - lockIconImageName: lockIconImageName(for: tab), + lockIconImageName: lockIconImageName, + lockIconNeedsTheming: lockIconNeedsTheming, safeListedURLImageName: safeListedURLImageName, windowUUID: windowUUID, actionType: ToolbarActionType.urlDidChange) @@ -2236,6 +2245,7 @@ class BrowserViewController: UIViewController, for: nil ) + logger.log("Show MainMenu button tapped", level: .info, category: .mainMenu) if featureFlags.isFeatureEnabled(.menuRefactor, checking: .buildOnly) { navigationHandler?.showMainMenu() } else { @@ -2319,19 +2329,31 @@ class BrowserViewController: UIViewController, menuHelper.navigationHandler = navigationHandler updateZoomPageBarVisibility(visible: false) - menuHelper.getToolbarActions(navigationController: navigationController) { actions in + menuHelper.getToolbarActions(navigationController: navigationController) { [weak self] actions in + guard let self else { return } let shouldInverse = PhotonActionSheetViewModel.hasInvertedMainMenu( trait: self.traitCollection, isBottomSearchBar: self.isBottomSearchBar ) + // Ecosia: Add `isPhone` + let isPhone = self.traitCollection.userInterfaceIdiom == .phone let viewModel = PhotonActionSheetViewModel( actions: actions, + /* Ecosia: Update modal style for phone modalStyle: .popover, + */ + modalStyle: isPhone ? .pageSheet : .popover, isMainMenu: true, isMainMenuInverted: shouldInverse ) + if self.profile.prefs.boolForKey(PrefsKeys.PhotonMainMenuShown) == nil { + self.profile.prefs.setBool(true, forKey: PrefsKeys.PhotonMainMenuShown) + } self.presentSheetWith(viewModel: viewModel, on: self, from: button) } + + // Ecosia: Make `menuHelper` available at class level + self.menuHelper = menuHelper } func didTapOnShare(from view: UIView) { @@ -2602,7 +2624,10 @@ class BrowserViewController: UIViewController, } } + /* Ecosia: Make available to BrowserViewController+Ecosia fileprivate func popToBVC() { + */ + func popToBVC() { guard let currentViewController = navigationController?.topViewController else { return } // Avoid dismissing JSPromptAlert that causes the crash because completionHandler was not called if !isShowingJSPromptAlert() { @@ -3045,8 +3070,13 @@ class BrowserViewController: UIViewController, let currentTheme = currentTheme() statusBarOverlay.hasTopTabs = ToolbarHelper().shouldShowTopTabs(for: traitCollection) statusBarOverlay.applyTheme(theme: currentTheme) + /* Ecosia: Update backgrounds keyboardBackdrop?.backgroundColor = currentTheme.colors.layer1 - webViewContainerBackdrop.backgroundColor = currentTheme.colors.layer3 + */ + keyboardBackdrop?.backgroundColor = currentTheme.colors.ecosia.backgroundSecondary + navigationController?.view.backgroundColor = currentTheme.colors.ecosia.backgroundSecondary + header.backgroundColor = statusBarOverlay.backgroundColor + setNeedsStatusBarAppearanceUpdate() tabManager.selectedTab?.applyTheme(theme: currentTheme) @@ -3060,6 +3090,12 @@ class BrowserViewController: UIViewController, guard let contentScript = tabManager.selectedTab?.getContentScript(name: ReaderMode.name()) else { return } applyThemeForPreferences(profile.prefs, contentScript: contentScript) + + // Ecosia: Update URLBar following PrivateModeUI + updateURLBarFollowingPrivateModeUI() + + // Ecosia + updateSpotlightThemeIfNeeded() } var isPreferSwitchToOpenTabOverDuplicateFeatureEnabled: Bool { @@ -3117,6 +3153,8 @@ class BrowserViewController: UIViewController, guard !topTabsVisible, !toolbar.inOverlayMode else { return } // We're not showing the top tabs; show a toast to quick switch to the fresh new tab. let viewModel = ButtonToastViewModel(labelText: .ContextMenuButtonToastNewTabOpenedLabelText, + // Ecosia: Re-add image so toast looks like v104 + imageName: "tabs", buttonText: .ContextMenuButtonToastNewTabOpenedButtonText) let toast = ButtonToast(viewModel: viewModel, theme: currentTheme(), @@ -3414,8 +3452,10 @@ extension BrowserViewController: LegacyTabDelegate { } } + /* Ecosia: Remove Credit Cards Save/Autofill // Credit card autofill setup and callback autofillSetup(tab, didCreateWebView: webView) + */ let contextMenuHelper = ContextMenuHelper(tab: tab) tab.addContentScript(contextMenuHelper, name: ContextMenuHelper.name()) @@ -3588,6 +3628,8 @@ extension BrowserViewController: HomePanelDelegate { // We're not showing the top tabs; show a toast to quick switch to the fresh new tab. let viewModel = ButtonToastViewModel(labelText: .ContextMenuButtonToastNewTabOpenedLabelText, + // Ecosia: Re-add image so toast looks like v104 + imageName: "tabs", buttonText: .ContextMenuButtonToastNewTabOpenedButtonText) let toast = ButtonToast(viewModel: viewModel, theme: currentTheme(), @@ -3770,6 +3812,9 @@ extension BrowserViewController: TabManagerDelegate { if previousTab == nil || selectedTab.isPrivate != previousTab?.isPrivate { applyTheme() + // Ecosia: Update search engine icon + urlBar.updateSearchEngineImage() + // TODO: [FXIOS-8907] Ideally we shouldn't create tabs as a side-effect of UI theme updates. var ui = [PrivateModeUI?]() if isToolbarRefactorEnabled { @@ -3801,18 +3846,20 @@ extension BrowserViewController: TabManagerDelegate { scrollController.tab = selectedTab - if !selectedTab.isFxHomeTab, - let webView = selectedTab.webView { + var needsReload = false + if let webView = selectedTab.webView { webView.accessibilityLabel = .WebViewAccessibilityLabel webView.accessibilityIdentifier = "contentView" webView.accessibilityElementsHidden = false browserDelegate?.show(webView: webView) - + if selectedTab.isFxHomeTab { + needsReload = true + } if webView.url == nil { // The webView can go gray if it was zombified due to memory pressure. // When this happens, the URL is nil, so try restoring the page upon selection. - selectedTab.reload() + needsReload = true } } @@ -3864,8 +3911,11 @@ extension BrowserViewController: TabManagerDelegate { /// If the selectedTab is showing an error page trigger a reload if let url = selectedTab.url, let internalUrl = InternalURL(url), internalUrl.isErrorPage { + needsReload = true + } + + if needsReload { selectedTab.reloadPage() - return } } diff --git a/firefox-ios/Client/Frontend/Browser/ButtonToast.swift b/firefox-ios/Client/Frontend/Browser/ButtonToast.swift index 1568ef3ea0368..402459c51d6da 100644 --- a/firefox-ios/Client/Frontend/Browser/ButtonToast.swift +++ b/firefox-ios/Client/Frontend/Browser/ButtonToast.swift @@ -16,12 +16,23 @@ struct ButtonToastViewModel { class ButtonToast: Toast { struct UX { + // Ecosia: Add custom height + static let ecosiaButtonToastHeight: CGFloat = Toast.UX.toastHeight + Toast.UX.toastOffset static let delay = DispatchTimeInterval.milliseconds(900) - static let padding: CGFloat = 15 - static let buttonPadding: CGFloat = 10 + // Ecosia: Adjust Padding + // static let padding: CGFloat = 15 + static let padding: CGFloat = 8 + // Ecosia: Adjust Padding + // static let buttonPadding: CGFloat = 10 + static let buttonPadding: CGFloat = 16 static let buttonBorderRadius: CGFloat = 5 static let buttonBorderWidth: CGFloat = 1 + // Ecosia: Add title and description font sizes + static let titleFontSize: CGFloat = 17 + static let descriptionFontSize: CGFloat = 15 static let widthOffset: CGFloat = 20 + // Ecosia: Add properties + static let standardCornerRadius: CGFloat = 10 } // MARK: - UI @@ -29,29 +40,55 @@ class ButtonToast: Toast { stackView.axis = .horizontal stackView.alignment = .center stackView.spacing = UX.padding + // Ecosia: Adjust properties + stackView.distribution = .fill + stackView.spacing = 8 + stackView.layer.cornerRadius = UX.standardCornerRadius } - private var imageView: UIImageView = .build { imageView in } + private var imageView: UIImageView = .build { imageView in + // Ecosia: Add imageview properties + imageView.contentMode = .scaleAspectFit + imageView.setContentHuggingPriority(.required, for: .horizontal) + } private var labelStackView: UIStackView = .build { stackView in stackView.axis = .vertical stackView.alignment = .leading + // Ecosia: Review ToastView to look like v104 + stackView.setContentHuggingPriority(.defaultLow, for: .horizontal) } private var titleLabel: UILabel = .build { label in + /* Ecosia: Use title font size label.font = FXFontStyles.Bold.subheadline.scaledFont() + */ + label.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .body, + size: UX.titleFontSize) label.numberOfLines = 0 + // Ecosia: Review ToastView to look like v104 + label.adjustsFontForContentSizeCategory = true + label.lineBreakMode = .byTruncatingTail + label.setContentHuggingPriority(.defaultLow, for: .horizontal) } private var descriptionLabel: UILabel = .build { label in + /* Ecosia: Use description font size label.font = FXFontStyles.Bold.footnote.scaledFont() + */ + label.font = DefaultDynamicFontHelper.preferredFont(withTextStyle: .body, + size: UX.descriptionFontSize) label.numberOfLines = 0 } private var roundedButton: UIButton = .build { button in + /* Ecosia: Review ToastView to look like v104 button.layer.cornerRadius = UX.buttonBorderRadius button.layer.borderWidth = UX.buttonBorderWidth button.titleLabel?.font = FXFontStyles.Regular.subheadline.scaledFont() + */ + button.titleLabel?.font = DefaultDynamicFontHelper.preferredBoldFont(withTextStyle: .body, + size: UX.titleFontSize) button.titleLabel?.numberOfLines = 1 button.titleLabel?.lineBreakMode = .byClipping button.titleLabel?.adjustsFontSizeToFitWidth = true @@ -75,11 +112,18 @@ class ButtonToast: Toast { toastView.trailingAnchor.constraint(equalTo: trailingAnchor), toastView.heightAnchor.constraint(equalTo: heightAnchor), + /* Ecosia: Add custom height heightAnchor.constraint(greaterThanOrEqualToConstant: Toast.UX.toastHeight) + */ + heightAnchor.constraint(greaterThanOrEqualToConstant: UX.ecosiaButtonToastHeight), ]) + /* Ecosia: Add custom height animationConstraint = toastView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: Toast.UX.toastHeight) + */ + animationConstraint = toastView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, + constant: UX.ecosiaButtonToastHeight) animationConstraint?.isActive = true if let theme = theme { applyTheme(theme: theme) @@ -91,6 +135,12 @@ class ButtonToast: Toast { } private func createView(viewModel: ButtonToastViewModel) -> UIView { + + // Ecosia: Review ToastView to look like v104 + let space = UIView() + space.widthAnchor.constraint(equalToConstant: UX.padding).isActive = true + horizontalStackView.addArrangedSubview(space) + if let imageName = viewModel.imageName { imageView = UIImageView(image: UIImage.templateImageNamed(imageName)) horizontalStackView.addArrangedSubview(imageView) @@ -118,26 +168,41 @@ class ButtonToast: Toast { NSLayoutConstraint.activate([ labelStackView.centerYAnchor.constraint(equalTo: horizontalStackView.centerYAnchor), + /* Ecosia: Review constraints horizontalStackView.leadingAnchor.constraint(equalTo: toastView.leadingAnchor, constant: UX.padding), horizontalStackView.trailingAnchor.constraint(equalTo: toastView.trailingAnchor), horizontalStackView.bottomAnchor.constraint(equalTo: toastView.safeAreaLayoutGuide.bottomAnchor), horizontalStackView.topAnchor.constraint(equalTo: toastView.topAnchor), horizontalStackView.heightAnchor.constraint(equalToConstant: Toast.UX.toastHeight), + */ + horizontalStackView.leadingAnchor.constraint(equalTo: toastView.leadingAnchor, constant: Toast.UX.toastOffset), + horizontalStackView.trailingAnchor.constraint(equalTo: toastView.trailingAnchor, constant: -Toast.UX.toastOffset), + horizontalStackView.bottomAnchor.constraint(equalTo: toastView.safeAreaLayoutGuide.bottomAnchor, constant: -Toast.UX.toastOffset), + horizontalStackView.topAnchor.constraint(equalTo: toastView.topAnchor), + horizontalStackView.heightAnchor.constraint(equalToConstant: Toast.UX.toastHeight - Toast.UX.toastOffset), ]) + + // Ecosia: Review ToastView to look like v104 + toastView.layer.cornerRadius = UX.standardCornerRadius + toastView.layer.masksToBounds = true + return toastView } func setupPaddedButton(stackView: UIStackView, buttonText: String?) { guard let buttonText = buttonText else { return } - + /* Ecosia: Review ToastView to look like v104 let paddedView = UIView() paddedView.translatesAutoresizingMaskIntoConstraints = false stackView.addArrangedSubview(paddedView) - + */ roundedButton.setTitle(buttonText, for: []) - paddedView.addSubview(roundedButton) + // Ecosia: Review ToastView to look like v104 + // paddedView.addSubview(roundedButton) + stackView.addArrangedSubview(roundedButton) NSLayoutConstraint.activate([ + /* Ecosia: Review ToastView to look like v104 roundedButton.heightAnchor.constraint( equalToConstant: roundedButton.titleLabel!.intrinsicContentSize.height + 2 * UX.buttonPadding), roundedButton.widthAnchor.constraint( @@ -148,11 +213,15 @@ class ButtonToast: Toast { paddedView.topAnchor.constraint(equalTo: stackView.topAnchor), paddedView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), paddedView.widthAnchor.constraint(equalTo: roundedButton.widthAnchor, constant: UX.widthOffset) + */ + roundedButton.widthAnchor.constraint( + equalToConstant: roundedButton.titleLabel!.intrinsicContentSize.width + 2 * UX.buttonPadding), ]) - roundedButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(buttonPressed))) - - paddedView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(buttonPressed))) + // Ecosia: Review ToastView to look like v104 + // roundedButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(buttonPressed))) + // paddedView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(buttonPressed))) + roundedButton.addTarget(self, action: #selector(buttonPressed), for: .primaryActionTriggered) } override func applyTheme(theme: Theme) { @@ -160,9 +229,17 @@ class ButtonToast: Toast { titleLabel.textColor = theme.colors.textInverted descriptionLabel.textColor = theme.colors.textInverted + /* Ecosia: Add image tintColor to look like v104 imageView.tintColor = theme.colors.textInverted + */ + imageView.tintColor = theme.colors.ecosia.iconInverseStrong roundedButton.setTitleColor(theme.colors.textInverted, for: []) - roundedButton.layer.borderColor = theme.colors.borderInverted.cgColor + /* Ecosia: Add `horizontalStackView` background as the Toast view is made clear + so to have the padding effect from bottom, left and right + */ + horizontalStackView.backgroundColor = theme.colors.ecosia.backgroundNeutralInverse + // Ecosia: Review ToastView to look like v104 + // roundedButton.layer.borderColor = theme.colors.borderInverted.cgColor } // MARK: - Button action diff --git a/firefox-ios/Client/Frontend/Browser/DefaultSearchPrefs.swift b/firefox-ios/Client/Frontend/Browser/DefaultSearchPrefs.swift index a1c3c723c41a3..064766633ab02 100644 --- a/firefox-ios/Client/Frontend/Browser/DefaultSearchPrefs.swift +++ b/firefox-ios/Client/Frontend/Browser/DefaultSearchPrefs.swift @@ -72,6 +72,13 @@ final class DefaultSearchPrefs { if let overrides = regionOverrides?[region] as? [String: Any] { usersEngineList = usersEngineList.map({ overrides[$0] as? String ?? $0 }) } + + // Ecosia: Guarantee the global default engine is included + // This enables ecosia to be set as default even in locales where it normally is not listed + if !usersEngineList.contains(globalDefaultEngine) { + usersEngineList.append(globalDefaultEngine) + } + return usersEngineList } diff --git a/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/EnhancedTrackingProtectionVC.swift b/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/EnhancedTrackingProtectionVC.swift index 7d2298eee5456..7a92274aa862b 100644 --- a/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/EnhancedTrackingProtectionVC.swift +++ b/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/EnhancedTrackingProtectionVC.swift @@ -6,6 +6,7 @@ import Shared import UIKit import Common import SiteImageView +import Ecosia struct ETPMenuUX { struct Fonts { @@ -123,7 +124,10 @@ class EnhancedTrackingProtectionMenuVC: UIViewController, Themeable { label.adjustsFontForContentSizeCategory = true } + /* Ecosia: Use themed Switch private let toggleSwitch: UISwitch = .build { toggleSwitch in + */ + private let toggleSwitch: EcosiaThemedSwitch = .build { toggleSwitch in toggleSwitch.isEnabled = true } @@ -523,8 +527,11 @@ extension EnhancedTrackingProtectionMenuVC { connectionImage.tintColor = theme.colors.iconPrimary } toggleView.backgroundColor = theme.colors.layer2 + /* Ecosia: Use applyTheme from EcosiaThemedSwitch toggleSwitch.tintColor = theme.colors.actionPrimary toggleSwitch.onTintColor = theme.colors.actionPrimary + */ + toggleSwitch.applyTheme(theme: theme) toggleStatusLabel.textColor = theme.colors.textSecondary protectionView.backgroundColor = theme.colors.layer2 protectionButton.setTitleColor(theme.colors.textAccent, for: .normal) diff --git a/firefox-ios/Client/Frontend/Browser/Event Queue/AppEvent.swift b/firefox-ios/Client/Frontend/Browser/Event Queue/AppEvent.swift index 188bf7ea200b1..b8d52e1c6775e 100644 --- a/firefox-ios/Client/Frontend/Browser/Event Queue/AppEvent.swift +++ b/firefox-ios/Client/Frontend/Browser/Event Queue/AppEvent.swift @@ -21,6 +21,8 @@ public enum AppEvent: AppEventType { case postLaunchDependenciesComplete case accountManagerInitialized case browserIsReady + // Ecosia: Add Feature Management event + case featureManagementInitialized // Activities: Profile Syncing case profileSyncing diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuConfigurationUtility.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuConfigurationUtility.swift index 0c3f747b56de2..dadcbd2070fde 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuConfigurationUtility.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuConfigurationUtility.swift @@ -7,7 +7,7 @@ import Foundation import MenuKit import Shared -struct MainMenuConfigurationUtility: Equatable { +struct MainMenuConfigurationUtility: Equatable, FeatureFlaggable { private struct Icons { static let newTab = StandardImageIdentifiers.Large.plus static let newPrivateTab = StandardImageIdentifiers.Large.privateModeCircleFill @@ -16,7 +16,7 @@ struct MainMenuConfigurationUtility: Equatable { static let findInPage = StandardImageIdentifiers.Large.search static let tools = StandardImageIdentifiers.Large.tool static let save = StandardImageIdentifiers.Large.save - static let bookmarks = StandardImageIdentifiers.Large.bookmark + static let bookmarks = StandardImageIdentifiers.Large.bookmarkTrayFill static let history = StandardImageIdentifiers.Large.history static let downloads = StandardImageIdentifiers.Large.download static let passwords = StandardImageIdentifiers.Large.login @@ -32,7 +32,7 @@ struct MainMenuConfigurationUtility: Equatable { static let print = StandardImageIdentifiers.Large.print static let share = StandardImageIdentifiers.Large.share static let addToShortcuts = StandardImageIdentifiers.Large.pin - static let removeFromShortcuts = StandardImageIdentifiers.Large.pinSlash + static let removeFromShortcuts = StandardImageIdentifiers.Large.pinSlashFill static let saveToReadingList = StandardImageIdentifiers.Large.readingListAdd static let removeFromReadingList = StandardImageIdentifiers.Large.readingListSlashFill static let bookmarkThisPage = StandardImageIdentifiers.Large.bookmark @@ -46,6 +46,10 @@ struct MainMenuConfigurationUtility: Equatable { // static let addToHomescreen = StandardImageIdentifiers.Large.addToHomescreen } + private var shouldShowReportSiteIssue: Bool { + featureFlags.isFeatureEnabled(.reportSiteIssue, checking: .buildOnly) + } + public func generateMenuElements( with tabInfo: MainMenuTabInfo, for viewType: MainMenuDetailsViewType?, @@ -54,7 +58,7 @@ struct MainMenuConfigurationUtility: Equatable { ) -> [MenuSection] { switch viewType { case .tools: - return getToolsSubmenu(with: uuid, tabInfo: tabInfo, and: readerState) + return getToolsSubmenu(with: uuid, tabInfo: tabInfo) case .save: return getSaveSubmenu(with: uuid, and: tabInfo) @@ -65,17 +69,19 @@ struct MainMenuConfigurationUtility: Equatable { } // MARK: - Main Menu + private func getMainMenuElements( with uuid: WindowUUID, and tabInfo: MainMenuTabInfo ) -> [MenuSection] { // Always include these sections var menuSections: [MenuSection] = [ - getNewTabSection(with: uuid), - getLibrariesSection(with: uuid), + getNewTabSection(with: uuid, tabInfo: tabInfo), + getLibrariesSection(with: uuid, tabInfo: tabInfo), getOtherToolsSection( with: uuid, - isHomepage: tabInfo.isHomepage + isHomepage: tabInfo.isHomepage, + tabInfo: tabInfo ) ] @@ -91,7 +97,7 @@ struct MainMenuConfigurationUtility: Equatable { } // MARK: - New Tabs Section - private func getNewTabSection(with uuid: WindowUUID) -> MenuSection { + private func getNewTabSection(with uuid: WindowUUID, tabInfo: MainMenuTabInfo) -> MenuSection { return MenuSection(options: [ MenuElement( title: .MainMenu.TabsSection.NewTab, @@ -106,7 +112,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.newTab) + navigationDestination: MenuNavigationDestination(.newTab), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -124,15 +131,17 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.newPrivateTab) + navigationDestination: MenuNavigationDestination(.newPrivateTab), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } - ) + ), ]) } // MARK: - Tools Section + private func getToolsSection( with uuid: WindowUUID, and configuration: MainMenuTabInfo @@ -153,7 +162,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.findInPage) + navigationDestination: MenuNavigationDestination(.findInPage), + telemetryInfo: TelemetryInfo(isHomepage: configuration.isHomepage) ) ) } @@ -173,7 +183,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapShowDetailsView, - changeMenuViewTo: .tools + changeMenuViewTo: .tools, + telemetryInfo: TelemetryInfo(isHomepage: configuration.isHomepage) ) ) } @@ -193,27 +204,30 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapShowDetailsView, - changeMenuViewTo: .save + changeMenuViewTo: .save, + telemetryInfo: TelemetryInfo(isHomepage: configuration.isHomepage) ) ) } - ) + ), ] ) } - private func getToolsSubmenuDescription(with tabInfo: MainMenuTabInfo) -> String { + private func getToolsSubmenuDescription(with _: MainMenuTabInfo) -> String { typealias Preview = String.MainMenu.Submenus.Tools var description = "" description += "\(Preview.ZoomSubtitle)" - if tabInfo.readerModeIsAvailable { - description += ", \(Preview.ReaderViewSubtitle)" - } + // if tabInfo.readerModeIsAvailable { + // description += ", \(Preview.ReaderViewSubtitle)" + // } description += ", \(Preview.NightModeSubtitle)" - description += ", \(Preview.ReportBrokenSiteSubtitle)" + if shouldShowReportSiteIssue { + description += ", \(Preview.ReportBrokenSiteSubtitle)" + } description += ", \(Preview.ShareSubtitle)" return description @@ -240,29 +254,23 @@ struct MainMenuConfigurationUtility: Equatable { typealias Menu = String.MainMenu.ToolsSection typealias A11y = String.MainMenu.ToolsSection.AccessibilityLabels - let title: String = if tabInfo.isDefaultUserAgentDesktop { - tabInfo.hasChangedUserAgent ? Menu.SwitchToDesktopSite : Menu.SwitchToMobileSite - } else { - tabInfo.hasChangedUserAgent ? Menu.SwitchToMobileSite : Menu.SwitchToDesktopSite + // isDefaultUserAgentDesktop is only true if we're building on an "intel mac" + // hasChangedUserAgent describes if we've changed form the initial starting state + let userAgentStringSelector = { (desktopString: String, mobileString: String) in + tabInfo.isDefaultUserAgentDesktop == tabInfo.hasChangedUserAgent ? desktopString : mobileString } - let icon: String = if tabInfo.isDefaultUserAgentDesktop { - tabInfo.hasChangedUserAgent ? Icons.deviceDesktop : Icons.deviceMobile - } else { - tabInfo.hasChangedUserAgent ? Icons.deviceMobile : Icons.deviceDesktop - } + let title = userAgentStringSelector(Menu.SwitchToDesktopSite, Menu.SwitchToMobileSite) + let icon = userAgentStringSelector(Icons.deviceDesktop, Icons.deviceMobile) + let a11yLabel = userAgentStringSelector(A11y.SwitchToDesktopSite, A11y.SwitchToMobileSite) - let a11yLabel: String = if tabInfo.isDefaultUserAgentDesktop { - tabInfo.hasChangedUserAgent ? A11y.SwitchToDesktopSite : A11y.SwitchToMobileSite - } else { - tabInfo.hasChangedUserAgent ? A11y.SwitchToMobileSite : A11y.SwitchToDesktopSite - } + let isActive = tabInfo.isDefaultUserAgentDesktop ? !tabInfo.hasChangedUserAgent : tabInfo.hasChangedUserAgent return MenuElement( title: title, iconName: icon, isEnabled: true, - isActive: false, + isActive: isActive, a11yLabel: a11yLabel, a11yHint: "", a11yId: AccessibilityIdentifiers.MainMenu.switchToDesktopSite, @@ -270,7 +278,10 @@ struct MainMenuConfigurationUtility: Equatable { store.dispatch( MainMenuAction( windowUUID: uuid, - actionType: MainMenuActionType.tapToggleUserAgent + actionType: MainMenuActionType.tapToggleUserAgent, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage, + isDefaultUserAgentDesktop: tabInfo.isDefaultUserAgentDesktop, + hasChangedUserAgent: tabInfo.hasChangedUserAgent) ) ) } @@ -278,65 +289,84 @@ struct MainMenuConfigurationUtility: Equatable { } // MARK: - Tools Submenu + private func getToolsSubmenu( with uuid: WindowUUID, - tabInfo: MainMenuTabInfo, - and readerModeState: ReaderModeState? + tabInfo: MainMenuTabInfo ) -> [MenuSection] { + let firstSection = if shouldShowReportSiteIssue { + MenuSection(options: [ + configureZoomItem(with: uuid, and: tabInfo), + configureNightModeItem(with: uuid, and: tabInfo), + configureReportSiteIssueItem(with: uuid, tabInfo: tabInfo), + ]) + } else { + MenuSection(options: [ + configureZoomItem(with: uuid, and: tabInfo), + configureNightModeItem(with: uuid, and: tabInfo), + ]) + } + return [ - MenuSection( - options: [ - configureZoomItem(with: uuid, and: tabInfo), - configureNightModeItem(with: uuid), - MenuElement( - title: .MainMenu.Submenus.Tools.ReportBrokenSite, - iconName: Icons.reportBrokenSite, - isEnabled: true, - isActive: false, - a11yLabel: .MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite, - a11yHint: "", - a11yId: AccessibilityIdentifiers.MainMenu.reportBrokenSite, - action: { - store.dispatch( - MainMenuAction( - windowUUID: uuid, - actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination( - .goToURL, - url: SupportUtils.URLForReportSiteIssue(tabInfo.url?.absoluteString) - ) - ) - ) - } + firstSection, + MenuSection(options: [configureShareItem(with: uuid, tabInfo: tabInfo)]), + ] + } + + private func configureReportSiteIssueItem( + with uuid: WindowUUID, + tabInfo: MainMenuTabInfo + ) -> MenuElement { + return MenuElement( + title: .MainMenu.Submenus.Tools.ReportBrokenSite, + iconName: Icons.reportBrokenSite, + isEnabled: true, + isActive: false, + a11yLabel: .MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite, + a11yHint: "", + a11yId: AccessibilityIdentifiers.MainMenu.reportBrokenSite, + action: { + store.dispatch( + MainMenuAction( + windowUUID: uuid, + actionType: MainMenuActionType.tapNavigateToDestination, + navigationDestination: MenuNavigationDestination( + .goToURL, + url: SupportUtils.URLForReportSiteIssue(tabInfo.url?.absoluteString) + ), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) - ] - ), - MenuSection( - options: [ - MenuElement( - title: .MainMenu.Submenus.Tools.Share, - iconName: Icons.share, - isEnabled: true, - isActive: false, - a11yLabel: .MainMenu.Submenus.Tools.AccessibilityLabels.Share, - a11yHint: "", - a11yId: AccessibilityIdentifiers.MainMenu.share, - action: { - store.dispatch( - MainMenuAction( - windowUUID: uuid, - actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination( - .shareSheet, - url: tabInfo.canonicalURL - ) - ) - ) - } + ) + } + ) + } + + private func configureShareItem( + with uuid: WindowUUID, + tabInfo: MainMenuTabInfo + ) -> MenuElement { + return MenuElement( + title: .MainMenu.Submenus.Tools.Share, + iconName: Icons.share, + isEnabled: true, + isActive: false, + a11yLabel: .MainMenu.Submenus.Tools.AccessibilityLabels.Share, + a11yHint: "", + a11yId: AccessibilityIdentifiers.MainMenu.share, + action: { + store.dispatch( + MainMenuAction( + windowUUID: uuid, + actionType: MainMenuActionType.tapNavigateToDestination, + navigationDestination: MenuNavigationDestination( + .shareSheet, + url: tabInfo.canonicalURL + ), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) - ] - ) - ] + ) + } + ) } private func configureZoomItem( @@ -355,14 +385,15 @@ struct MainMenuConfigurationUtility: Equatable { iconName: icon, isEnabled: true, isActive: tabInfo.zoomLevel != 1.0, - a11yLabel: .MainMenu.Submenus.Tools.AccessibilityLabels.Zoom, + a11yLabel: String(format: .MainMenu.Submenus.Tools.AccessibilityLabels.Zoom, zoomLevel), a11yHint: "", a11yId: AccessibilityIdentifiers.MainMenu.zoom, action: { store.dispatch( MainMenuAction( windowUUID: uuid, - actionType: MainMenuDetailsActionType.tapZoom + actionType: MainMenuDetailsActionType.tapZoom, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -389,9 +420,17 @@ struct MainMenuConfigurationUtility: Equatable { isEnabled: readerModeState != .unavailable, isActive: readerModeState == .active, a11yLabel: a11yLabel, - a11yHint: "", + a11yHint: readerModeState != .unavailable ? "" : .MainMenu.AccessibilityLabels.OptionDisabledHint, a11yId: AccessibilityIdentifiers.MainMenu.readerView, action: { + store.dispatch( + MainMenuAction( + windowUUID: uuid, + actionType: GeneralBrowserActionType.showReaderMode, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage, + isActionOn: readerModeState == .active) + ) + ) store.dispatch( GeneralBrowserAction( windowUUID: uuid, @@ -402,7 +441,7 @@ struct MainMenuConfigurationUtility: Equatable { ) } - private func configureNightModeItem(with uuid: WindowUUID) -> MenuElement { + private func configureNightModeItem(with uuid: WindowUUID, and tabInfo: MainMenuTabInfo) -> MenuElement { typealias Strings = String.MainMenu.Submenus.Tools typealias A11y = String.MainMenu.Submenus.Tools.AccessibilityLabels @@ -423,7 +462,9 @@ struct MainMenuConfigurationUtility: Equatable { store.dispatch( MainMenuAction( windowUUID: uuid, - actionType: MainMenuDetailsActionType.tapToggleNightMode + actionType: MainMenuDetailsActionType.tapToggleNightMode, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage, + isActionOn: nightModeIsOn) ) ) } @@ -431,6 +472,7 @@ struct MainMenuConfigurationUtility: Equatable { } // MARK: - Save Submenu + private func getSaveSubmenu( with uuid: WindowUUID, and tabInfo: MainMenuTabInfo @@ -439,7 +481,7 @@ struct MainMenuConfigurationUtility: Equatable { options: [ configureBookmarkItem(with: uuid, and: tabInfo), configureShortcutsItem(with: uuid, and: tabInfo), - configureReadingListItem(with: uuid, and: tabInfo) + configureReadingListItem(with: uuid, and: tabInfo), ] )] } @@ -469,7 +511,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: actionType, - tabID: tabInfo.tabID + tabID: tabInfo.tabID, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -501,7 +544,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: actionType, - tabID: tabInfo.tabID + tabID: tabInfo.tabID, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -527,14 +571,15 @@ struct MainMenuConfigurationUtility: Equatable { isEnabled: tabInfo.readerModeIsAvailable, isActive: tabInfo.isInReadingList, a11yLabel: a11yLabel, - a11yHint: "", + a11yHint: tabInfo.readerModeIsAvailable ? "" : .MainMenu.AccessibilityLabels.OptionDisabledHint, a11yId: AccessibilityIdentifiers.MainMenu.saveToReadingList, action: { store.dispatch( MainMenuAction( windowUUID: uuid, actionType: actionType, - tabID: tabInfo.tabID + tabID: tabInfo.tabID, + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -542,7 +587,7 @@ struct MainMenuConfigurationUtility: Equatable { } // MARK: - Libraries Section - private func getLibrariesSection(with uuid: WindowUUID) -> MenuSection { + private func getLibrariesSection(with uuid: WindowUUID, tabInfo: MainMenuTabInfo) -> MenuSection { return MenuSection(options: [ MenuElement( title: .MainMenu.PanelLinkSection.Bookmarks, @@ -557,7 +602,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.bookmarks) + navigationDestination: MenuNavigationDestination(.bookmarks), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -575,7 +621,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.history) + navigationDestination: MenuNavigationDestination(.history), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -593,7 +640,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.downloads) + navigationDestination: MenuNavigationDestination(.downloads), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -611,18 +659,21 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.passwords) + navigationDestination: MenuNavigationDestination(.passwords), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } - ) + ), ]) } // MARK: - Other Tools Section + private func getOtherToolsSection( with uuid: WindowUUID, - isHomepage: Bool + isHomepage: Bool, + tabInfo: MainMenuTabInfo ) -> MenuSection { let homepageOptions = [ MenuElement( @@ -638,7 +689,8 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.customizeHomepage) + navigationDestination: MenuNavigationDestination(.customizeHomepage), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -651,8 +703,10 @@ struct MainMenuConfigurationUtility: Equatable { iconName: Icons.whatsNew, isEnabled: true, isActive: false, - a11yLabel: String(format: .MainMenu.OtherToolsSection.AccessibilityLabels.WhatsNew, - AppName.shortName.rawValue), + a11yLabel: String( + format: .MainMenu.OtherToolsSection.AccessibilityLabels.WhatsNew, + AppName.shortName.rawValue + ), a11yHint: "", a11yId: AccessibilityIdentifiers.MainMenu.whatsNew, action: { @@ -663,11 +717,12 @@ struct MainMenuConfigurationUtility: Equatable { navigationDestination: MenuNavigationDestination( .goToURL, url: SupportUtils.URLForWhatsNew - ) + ), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } - ) + ), ] let standardOptions = [ @@ -687,7 +742,8 @@ struct MainMenuConfigurationUtility: Equatable { navigationDestination: MenuNavigationDestination( .goToURL, url: SupportUtils.URLForGetHelp - ) + ), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } @@ -705,11 +761,12 @@ struct MainMenuConfigurationUtility: Equatable { MainMenuAction( windowUUID: uuid, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.settings) + navigationDestination: MenuNavigationDestination(.settings), + telemetryInfo: TelemetryInfo(isHomepage: tabInfo.isHomepage) ) ) } - ) + ), ] return MenuSection(options: isHomepage ? homepageOptions + standardOptions : standardOptions) diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuCoordinator.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuCoordinator.swift index da373b47ae7f2..f37f76a563527 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuCoordinator.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuCoordinator.swift @@ -23,16 +23,33 @@ class MainMenuCoordinator: BaseCoordinator, FeatureFlaggable { weak var parentCoordinator: ParentCoordinatorDelegate? weak var navigationHandler: MainMenuCoordinatorDelegate? - private let windowUUID: WindowUUID + let windowUUID: WindowUUID private let profile: Profile - init(router: Router, windowUUID: WindowUUID, profile: Profile) { + init( + router: Router, + windowUUID: WindowUUID, + profile: Profile + ) { self.windowUUID = windowUUID self.profile = profile super.init(router: router) } + deinit { + logger.log( + "MainMenuCoordinator - deinitialized", + level: .info, + category: .mainMenu + ) + } + func start() { + logger.log( + "MainMenuCoordinator - started", + level: .info, + category: .mainMenu + ) router.setRootViewController( createMainMenuViewController(), hideBar: true @@ -40,6 +57,11 @@ class MainMenuCoordinator: BaseCoordinator, FeatureFlaggable { } func showDetailViewController() { + logger.log( + "MainMenuCoordinator - pushing detail view controller", + level: .info, + category: .mainMenu + ) router.push( createMainMenuDetailViewController(), animated: true @@ -47,12 +69,31 @@ class MainMenuCoordinator: BaseCoordinator, FeatureFlaggable { } func dismissDetailViewController() { + logger.log( + "MainMenuCoordinator - popping detail view controller", + level: .info, + category: .mainMenu + ) router.popViewController(animated: true) } + func removeCoordinatorFromParent() { + logger.log( + "MainMenuCoordinator - removing coordinator from parent", + level: .info, + category: .mainMenu + ) + parentCoordinator?.didFinish(from: self) + } + func dismissMenuModal(animated: Bool) { + logger.log( + "MainMenuCoordinator - dismissing main menu", + level: .info, + category: .mainMenu + ) router.dismiss(animated: animated, completion: nil) - parentCoordinator?.didFinish(from: self) + removeCoordinatorFromParent() } func navigateTo(_ destination: MenuNavigationDestination, animated: Bool) { @@ -95,7 +136,7 @@ class MainMenuCoordinator: BaseCoordinator, FeatureFlaggable { self.navigationHandler?.updateZoomPageBarVisibility() } - self.parentCoordinator?.didFinish(from: self) + removeCoordinatorFromParent() }) } diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuTelemetry.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuTelemetry.swift index 1e86d5b36422a..bc0565d061cc9 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuTelemetry.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/MainMenuTelemetry.swift @@ -3,9 +3,43 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ struct MainMenuTelemetry { - func mainMenuViewed() { } - func mainMenuDismissed() { } + func mainMenuOptionTapped(with isHomepage: Bool, and option: String) { + /* Ecosia: Remove Glean + let extra = GleanMetrics.AppMenu.MainMenuOptionSelectedExtra(isHomepage: isHomepage, option: option) + GleanMetrics.AppMenu.mainMenuOptionSelected.record(extra) + */ + } + + func saveSubmenuOptionTapped(with isHomepage: Bool, and option: String) { + /* Ecosia: Remove Glean + let extra = GleanMetrics.AppMenu.SaveMenuOptionSelectedExtra(isHomepage: isHomepage, option: option) + GleanMetrics.AppMenu.saveMenuOptionSelected.record(extra) + */ + } + + func toolsSubmenuOptionTapped(with isHomepage: Bool, and option: String) { + /* Ecosia: Remove Glean + let extra = GleanMetrics.AppMenu.ToolsMenuOptionSelectedExtra(isHomepage: isHomepage, option: option) + GleanMetrics.AppMenu.toolsMenuOptionSelected.record(extra) + */ + } + + func closeButtonTapped(isHomepage: Bool) { + /* Ecosia: Remove Glean + let extra = GleanMetrics.AppMenu.CloseButtonExtra(isHomepage: isHomepage) + GleanMetrics.AppMenu.closeButton.record(extra) + */ + } + + func menuDismissed(isHomepage: Bool) { + /* Ecosia: Remove Glean + let extra = GleanMetrics.AppMenu.MenuDismissedExtra(isHomepage: isHomepage) + GleanMetrics.AppMenu.menuDismissed.record(extra) + */ + } } diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuAction.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuAction.swift index a87cc5c51a061..8c679dc62d657 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuAction.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuAction.swift @@ -14,6 +14,7 @@ final class MainMenuAction: Action { var detailsViewToShow: MainMenuDetailsViewType? var accountData: AccountData? var accountIcon: UIImage? + var telemetryInfo: TelemetryInfo? init( windowUUID: WindowUUID, @@ -23,7 +24,8 @@ final class MainMenuAction: Action { currentTabInfo: MainMenuTabInfo? = nil, tabID: TabUUID? = nil, accountData: AccountData? = nil, - accountIcon: UIImage? = nil + accountIcon: UIImage? = nil, + telemetryInfo: TelemetryInfo? = nil ) { self.navigationDestination = navigationDestination self.detailsViewToShow = changeMenuViewTo @@ -31,6 +33,7 @@ final class MainMenuAction: Action { self.tabID = tabID self.accountData = accountData self.accountIcon = accountIcon + self.telemetryInfo = telemetryInfo super.init(windowUUID: windowUUID, actionType: actionType) } } @@ -41,7 +44,9 @@ enum MainMenuActionType: ActionType { case tapShowDetailsView case tapToggleUserAgent case updateCurrentTabInfo + case didInstantiateView case viewDidLoad + case menuDismissed } enum MainMenuMiddlewareActionType: ActionType { diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift index a1783cefaad6d..fd98ca0ca139f 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuDetailState.swift @@ -14,6 +14,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { var shouldGoBackToMainMenu: Bool var navigationDestination: MenuNavigationDestination? var submenuType: MainMenuDetailsViewType? + var isHomepage: Bool? var title: String { typealias Titles = String.MainMenu.ToolsSection @@ -36,6 +37,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { windowUUID: currentState.windowUUID, menuElements: currentState.menuElements, submenuType: currentState.submenuType, + isHomepage: currentState.isHomepage, navigationDestination: currentState.navigationDestination, shouldDismiss: currentState.shouldDismiss, shouldGoBackToMenu: currentState.shouldGoBackToMainMenu @@ -47,6 +49,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { windowUUID: windowUUID, menuElements: [], submenuType: nil, + isHomepage: nil, navigationDestination: nil, shouldDismiss: false, shouldGoBackToMenu: false @@ -57,6 +60,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { windowUUID: WindowUUID, menuElements: [MenuSection], submenuType: MainMenuDetailsViewType?, + isHomepage: Bool?, navigationDestination: MenuNavigationDestination? = nil, shouldDismiss: Bool = false, shouldGoBackToMenu: Bool = false @@ -64,6 +68,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { self.windowUUID = windowUUID self.menuElements = menuElements self.submenuType = submenuType + self.isHomepage = isHomepage self.navigationDestination = navigationDestination self.shouldDismiss = shouldDismiss self.shouldGoBackToMainMenu = shouldGoBackToMenu @@ -74,7 +79,8 @@ struct MainMenuDetailsState: ScreenState, Equatable { return MainMenuDetailsState( windowUUID: state.windowUUID, menuElements: state.menuElements, - submenuType: state.submenuType + submenuType: state.submenuType, + isHomepage: state.isHomepage ) } @@ -87,12 +93,12 @@ struct MainMenuDetailsState: ScreenState, Equatable { for: .mainMenu, window: action.windowUUID), let currentTabInfo = menuState.currentTabInfo, - let currentSubmenu = menuState.currentSubmenuView, - let toolbarState = store.state.screenState( - ToolbarState.self, - for: .toolbar, - window: action.windowUUID), - let readerModeState = toolbarState.addressToolbar.readerModeState + let currentSubmenu = menuState.currentSubmenuView + // let toolbarState = store.state.screenState( + // ToolbarState.self, + // for: .toolbar, + // window: action.windowUUID), + // let readerModeState = toolbarState.addressToolbar.readerModeState else { return state } return MainMenuDetailsState( @@ -101,15 +107,17 @@ struct MainMenuDetailsState: ScreenState, Equatable { with: currentTabInfo, for: currentSubmenu, and: action.windowUUID, - readerState: readerModeState + readerState: nil ), - submenuType: currentSubmenu + submenuType: currentSubmenu, + isHomepage: state.isHomepage ) case MainMenuDetailsActionType.tapBackToMainMenu: return MainMenuDetailsState( windowUUID: state.windowUUID, menuElements: state.menuElements, submenuType: state.submenuType, + isHomepage: state.isHomepage, shouldGoBackToMenu: true ) case MainMenuDetailsActionType.tapDismissView, @@ -124,6 +132,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { windowUUID: state.windowUUID, menuElements: state.menuElements, submenuType: state.submenuType, + isHomepage: state.isHomepage, shouldDismiss: true ) case MainMenuDetailsActionType.tapEditBookmark: @@ -131,6 +140,7 @@ struct MainMenuDetailsState: ScreenState, Equatable { windowUUID: state.windowUUID, menuElements: state.menuElements, submenuType: state.submenuType, + isHomepage: state.isHomepage, navigationDestination: MenuNavigationDestination(.editBookmark) ) case MainMenuDetailsActionType.tapZoom: @@ -138,13 +148,15 @@ struct MainMenuDetailsState: ScreenState, Equatable { windowUUID: state.windowUUID, menuElements: state.menuElements, submenuType: state.submenuType, + isHomepage: state.isHomepage, navigationDestination: MenuNavigationDestination(.zoom) ) default: return MainMenuDetailsState( windowUUID: state.windowUUID, menuElements: state.menuElements, - submenuType: state.submenuType + submenuType: state.submenuType, + isHomepage: state.isHomepage ) } } diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuMiddleware.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuMiddleware.swift index e40ee8ea43cec..ccf62ee67b7de 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuMiddleware.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuMiddleware.swift @@ -6,8 +6,42 @@ import Common import Redux import ToolbarKit import Account +import Shared final class MainMenuMiddleware { + private enum TelemetryAction { + static let newTab = "new_tab" + static let newPrivateTab = "new_private_tab" + static let findInPage = "find_in_page" + static let bookmarks = "bookmarks" + static let history = "history" + static let downloads = "downloads" + static let passwords = "passwords" + static let settings = "settings" + static let customizeHomepage = "customize_homepage" + static let getHelp = "get_help" + static let newInFirefox = "new_in_firefox" + static let tools = "tools" + static let save = "save" + static let share = "share" + static let switchToDesktopSite = "switch_to_desktop_site" + static let switchToMobileSite = "switch_to_mobile_site" + static let readerViewTurnOn = "reader_view_turn_on" + static let readerViewTurnOff = "reader_view_turn_off" + static let signInAccount = "sign_in_account" + static let zoom = "zoom" + static let reportBrokenSite = "report_broken_site" + static let bookmarkThisPage = "bookmark_this_page" + static let editBookmark = "edit_bookmark" + static let addToShortcuts = "add_to_shortcuts" + static let removeFromShortcuts = "remove_from_shortcuts" + static let saveToReadingList = "save_to_reading_list" + static let removeFromReadingList = "remove_from_reading_list" + static let nightModeTurnOn = "night_mode_turn_on" + static let nightModeTurnOff = "night_mode_turn_off" + static let back = "back" + } + private let logger: Logger private let telemetry = MainMenuTelemetry() @@ -17,11 +51,43 @@ final class MainMenuMiddleware { lazy var mainMenuProvider: Middleware = { state, action in guard let action = action as? MainMenuAction else { return } + let isHomepage = action.telemetryInfo?.isHomepage ?? false switch action.actionType { + case MainMenuActionType.tapNavigateToDestination: + guard let destination = action.navigationDestination?.destination else { return } + self.handleTelemetryFor(for: destination, + isHomepage: isHomepage, + and: action.navigationDestination?.url) + + case MainMenuActionType.tapShowDetailsView: + if action.detailsViewToShow == .tools { + self.telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.tools) + } else if action.detailsViewToShow == .save { + self.telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.save) + } + + case MainMenuActionType.tapToggleUserAgent: + guard let defaultIsDesktop = action.telemetryInfo?.isDefaultUserAgentDesktop, + let hasChangedUserAgent = action.telemetryInfo?.hasChangedUserAgent + else { return } + if defaultIsDesktop { + let option = hasChangedUserAgent ? TelemetryAction.switchToDesktopSite : TelemetryAction.switchToMobileSite + self.telemetry.mainMenuOptionTapped(with: isHomepage, and: option) + } else { + let option = hasChangedUserAgent ? TelemetryAction.switchToMobileSite : TelemetryAction.switchToDesktopSite + self.telemetry.mainMenuOptionTapped(with: isHomepage, and: option) + } + case MainMenuActionType.tapCloseMenu: - self.telemetry.mainMenuDismissed() - case MainMenuActionType.viewDidLoad: + self.telemetry.closeButtonTapped(isHomepage: isHomepage) + + case GeneralBrowserActionType.showReaderMode: + guard let isActionOn = action.telemetryInfo?.isActionOn else { return } + let option = isActionOn ? TelemetryAction.readerViewTurnOn : TelemetryAction.readerViewTurnOff + self.telemetry.toolsSubmenuOptionTapped(with: false, and: option) + + case MainMenuActionType.didInstantiateView: if let accountData = self.getAccountData() { if let iconURL = accountData.iconURL { GeneralizedImageFetcher().getImageFor(url: iconURL) { [weak self] image in @@ -32,26 +98,72 @@ final class MainMenuMiddleware { icon: image) } } else { - self.dispatchUpdateAccountHeader( - accountData: accountData, - action: action, - icon: nil) + self.dispatchUpdateAccountHeader(accountData: accountData, action: action) } + } else { + self.dispatchUpdateAccountHeader(action: action) } + + case MainMenuActionType.viewDidLoad: store.dispatch( MainMenuAction( windowUUID: action.windowUUID, actionType: MainMenuMiddlewareActionType.requestTabInfo ) ) - default: - break + + case MainMenuActionType.menuDismissed: + self.telemetry.menuDismissed(isHomepage: isHomepage) + + case MainMenuDetailsActionType.tapZoom: + self.telemetry.toolsSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.zoom) + + case MainMenuDetailsActionType.tapReportBrokenSite: + self.telemetry.toolsSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.reportBrokenSite) + + case MainMenuDetailsActionType.tapAddToBookmarks: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.bookmarkThisPage) + + case MainMenuDetailsActionType.tapEditBookmark: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.editBookmark) + + case MainMenuDetailsActionType.tapAddToShortcuts: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.addToShortcuts) + + case MainMenuDetailsActionType.tapRemoveFromShortcuts: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.removeFromShortcuts) + + case MainMenuDetailsActionType.tapAddToReadingList: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.saveToReadingList) + + case MainMenuDetailsActionType.tapRemoveFromReadingList: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.removeFromReadingList) + + case MainMenuDetailsActionType.tapToggleNightMode: + guard let isActionOn = action.telemetryInfo?.isActionOn else { return } + let option = isActionOn ? TelemetryAction.nightModeTurnOn : TelemetryAction.nightModeTurnOff + self.telemetry.toolsSubmenuOptionTapped(with: isHomepage, and: option) + + case MainMenuDetailsActionType.tapBackToMainMenu: + guard let submenuType = action.telemetryInfo?.submenuType else { return } + if submenuType == .save { + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.back) + } else { + self.telemetry.toolsSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.back) + } + + case MainMenuDetailsActionType.tapDismissView: + self.telemetry.closeButtonTapped(isHomepage: isHomepage) + + default: break } } - private func dispatchUpdateAccountHeader(accountData: AccountData?, - action: MainMenuAction, - icon: UIImage?) { + private func dispatchUpdateAccountHeader( + accountData: AccountData? = nil, + action: MainMenuAction, + icon: UIImage? = nil + ) { store.dispatch( MainMenuAction( windowUUID: action.windowUUID, @@ -87,4 +199,56 @@ final class MainMenuMiddleware { warningIcon: warningIcon, iconURL: iconURL) } + + private func handleTelemetryFor(for navigationDestination: MainMenuNavigationDestination, + isHomepage: Bool, + and urlToVisit: URL?) { + switch navigationDestination { + case .newTab: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.newTab) + + case .newPrivateTab: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.newPrivateTab) + + case .findInPage: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.findInPage) + + case .bookmarks: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.bookmarks) + + case .history: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.history) + + case .downloads: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.downloads) + + case .passwords: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.passwords) + + case .settings: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.settings) + + case .customizeHomepage: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.customizeHomepage) + + case .goToURL: + if urlToVisit == SupportUtils.URLForGetHelp { + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.getHelp) + } else if urlToVisit == SupportUtils.URLForWhatsNew { + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.newInFirefox) + } + + case .shareSheet: + telemetry.toolsSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.share) + + case .syncSignIn: + telemetry.mainMenuOptionTapped(with: isHomepage, and: TelemetryAction.signInAccount) + + case .editBookmark: + self.telemetry.saveSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.editBookmark) + + case .zoom: + self.telemetry.toolsSubmenuOptionTapped(with: isHomepage, and: TelemetryAction.zoom) + } + } } diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift index 5c31b8d858c9e..44602802f19a8 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Redux/MainMenuState.swift @@ -14,6 +14,26 @@ struct AccountData: Equatable { let iconURL: URL? } +struct TelemetryInfo: Equatable { + let isHomepage: Bool + let isActionOn: Bool? + let submenuType: MainMenuDetailsViewType? + let isDefaultUserAgentDesktop: Bool? + let hasChangedUserAgent: Bool? + + init(isHomepage: Bool, + isActionOn: Bool? = nil, + submenuType: MainMenuDetailsViewType? = nil, + isDefaultUserAgentDesktop: Bool? = nil, + hasChangedUserAgent: Bool? = nil) { + self.isHomepage = isHomepage + self.isActionOn = isActionOn + self.submenuType = submenuType + self.isDefaultUserAgentDesktop = isDefaultUserAgentDesktop + self.hasChangedUserAgent = hasChangedUserAgent + } +} + struct MainMenuTabInfo: Equatable { let tabID: TabUUID let url: URL? @@ -119,15 +139,14 @@ struct MainMenuState: ScreenState, Equatable { accountIcon: state.accountIcon ) case MainMenuMiddlewareActionType.updateAccountHeader: - guard let action = action as? MainMenuAction, - let accountData = action.accountData + guard let action = action as? MainMenuAction else { return state } return MainMenuState( windowUUID: state.windowUUID, menuElements: state.menuElements, currentTabInfo: state.currentTabInfo, - accountData: accountData, + accountData: action.accountData, accountIcon: action.accountIcon ) case MainMenuActionType.updateCurrentTabInfo: diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuDetailsViewController.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuDetailsViewController.swift index 1d3638e3ba0fe..9d7a8f73ad822 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuDetailsViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuDetailsViewController.swift @@ -102,6 +102,7 @@ class MainMenuDetailsViewController: UIViewController, submenuContent.setupAccessibilityIdentifiers( closeButtonA11yLabel: .MainMenu.Account.AccessibilityLabels.CloseButton, closeButtonA11yId: AccessibilityIdentifiers.MainMenu.NavigationHeaderView.closeButton, + titleA11yId: AccessibilityIdentifiers.MainMenu.NavigationHeaderView.title, backButtonA11yLabel: .MainMenu.Account.AccessibilityLabels.BackButton, backButtonA11yId: AccessibilityIdentifiers.MainMenu.NavigationHeaderView.backButton) } @@ -116,7 +117,9 @@ class MainMenuDetailsViewController: UIViewController, store.dispatch( MainMenuAction( windowUUID: self.windowUUID, - actionType: MainMenuDetailsActionType.tapBackToMainMenu + actionType: MainMenuDetailsActionType.tapBackToMainMenu, + telemetryInfo: TelemetryInfo(isHomepage: submenuState.isHomepage ?? false, + submenuType: submenuState.submenuType) ) ) } @@ -125,7 +128,8 @@ class MainMenuDetailsViewController: UIViewController, store.dispatch( MainMenuAction( windowUUID: self.windowUUID, - actionType: MainMenuDetailsActionType.tapDismissView + actionType: MainMenuDetailsActionType.tapDismissView, + telemetryInfo: TelemetryInfo(isHomepage: submenuState.isHomepage ?? false) ) ) } diff --git a/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuViewController.swift b/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuViewController.swift index 6b8d141cc4d63..03458f8538145 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenu/Views/MainMenuViewController.swift @@ -21,13 +21,17 @@ class MainMenuViewController: UIViewController, FeatureFlaggable { private struct UX { static let hintViewCornerRadius: CGFloat = 20 - static let hintViewHeight: CGFloat = 120 + static let hintViewHeight: CGFloat = 140 static let hintViewMargin: CGFloat = 20 } typealias SubscriberStateType = MainMenuState // MARK: - UI/UX elements private lazy var menuContent: MenuMainView = .build() + private var hintView: ContextualHintView = .build { view in + view.isAccessibilityElement = true + } + private var hintViewHeightConstraint: NSLayoutConstraint? // MARK: - Properties var notificationCenter: NotificationProtocol @@ -40,12 +44,18 @@ class MainMenuViewController: UIViewController, private var menuState: MainMenuState private let logger: Logger - private var hintView: ContextualHintView = .build() - let viewProvider: ContextualHintViewProvider var currentWindowUUID: UUID? { return windowUUID } + private var isPad: Bool { + traitCollection.verticalSizeClass == .regular && + !(UIDevice.current.userInterfaceIdiom == .phone) + } + + // Used to save the last screen orientation + private var lastOrientation: UIDeviceOrientation + // MARK: - Initializers init( windowUUID: WindowUUID, @@ -62,11 +72,18 @@ class MainMenuViewController: UIViewController, menuState = MainMenuState(windowUUID: windowUUID) viewProvider = ContextualHintViewProvider(forHintType: .mainMenu, with: profile) + self.lastOrientation = UIDevice.current.orientation super.init(nibName: nil, bundle: nil) setupNotifications(forObserver: self, observing: [.DynamicFontChanged]) subscribeToRedux() + store.dispatch( + MainMenuAction( + windowUUID: windowUUID, + actionType: MainMenuActionType.didInstantiateView + ) + ) } required init?(coder: NSCoder) { @@ -94,7 +111,8 @@ class MainMenuViewController: UIViewController, store.dispatch( MainMenuAction( windowUUID: self.windowUUID, - actionType: MainMenuActionType.tapCloseMenu + actionType: MainMenuActionType.tapCloseMenu, + currentTabInfo: menuState.currentTabInfo ) ) } @@ -105,7 +123,8 @@ class MainMenuViewController: UIViewController, MainMenuAction( windowUUID: self.windowUUID, actionType: MainMenuActionType.tapNavigateToDestination, - navigationDestination: MenuNavigationDestination(.syncSignIn) + navigationDestination: MenuNavigationDestination(.syncSignIn), + currentTabInfo: menuState.currentTabInfo ) ) } @@ -153,8 +172,15 @@ class MainMenuViewController: UIViewController, override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) - coordinator.animate(alongsideTransition: { _ in - self.adjustLayout() + coordinator.animate(alongsideTransition: { [weak self] _ in + guard let self else { return } + // We should dismiss CFR when device is rotating + if UIDevice.current.orientation != lastOrientation { + lastOrientation = UIDevice.current.orientation + self.adjustLayout(isDeviceRotating: true) + } else { + self.adjustLayout() + } }, completion: nil) } @@ -199,16 +225,27 @@ class MainMenuViewController: UIViewController, if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let window = windowScene.windows.first { window.addSubview(hintView) - NSLayoutConstraint.activate([ - hintView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: UX.hintViewMargin), - hintView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -UX.hintViewMargin), - hintView.bottomAnchor.constraint(equalTo: menuContent.accountHeaderView.topAnchor, - constant: -UX.hintViewMargin), - hintView.heightAnchor.constraint(equalToConstant: UX.hintViewHeight) - ]) + if isPad { + NSLayoutConstraint.activate([ + hintView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: UX.hintViewMargin * 4), + hintView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -UX.hintViewMargin * 4), + hintView.topAnchor.constraint(equalTo: menuContent.accountHeaderView.topAnchor, + constant: UX.hintViewMargin) + ]) + } else { + NSLayoutConstraint.activate([ + hintView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: UX.hintViewMargin), + hintView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -UX.hintViewMargin), + hintView.bottomAnchor.constraint(equalTo: menuContent.accountHeaderView.topAnchor, + constant: -UX.hintViewMargin) + ]) + } + hintViewHeightConstraint = hintView.heightAnchor.constraint(equalToConstant: UX.hintViewHeight) + hintViewHeightConstraint?.isActive = true } hintView.layer.cornerRadius = UX.hintViewCornerRadius hintView.layer.masksToBounds = true + adjustLayout() } // MARK: - Redux @@ -243,6 +280,7 @@ class MainMenuViewController: UIViewController, if let accountData = menuState.accountData { updateHeaderWith(accountData: accountData, icon: menuState.accountIcon) + setupAccessibilityIdentifiers(mainButtonA11yLabel: accountData.title) } if menuState.currentSubmenuView != nil { @@ -306,38 +344,66 @@ class MainMenuViewController: UIViewController, return sheetController.selectedDetentIdentifier } - private func setupAccessibilityIdentifiers() { + private func setupAccessibilityIdentifiers( + mainButtonA11yLabel: String = .MainMenu.Account.AccessibilityLabels.MainButton) { menuContent.setupAccessibilityIdentifiers( closeButtonA11yLabel: .MainMenu.Account.AccessibilityLabels.CloseButton, closeButtonA11yId: AccessibilityIdentifiers.MainMenu.HeaderView.closeButton, - mainButtonA11yLabel: .MainMenu.Account.AccessibilityLabels.MainButton, + mainButtonA11yLabel: mainButtonA11yLabel, mainButtonA11yId: AccessibilityIdentifiers.MainMenu.HeaderView.mainButton, menuA11yId: AccessibilityIdentifiers.MainMenu.mainMenu, menuA11yLabel: .MainMenu.TabsSection.AccessibilityLabels.MainMenu) } - private func adjustLayout() { + private func adjustLayout(isDeviceRotating: Bool = false) { menuContent.accountHeaderView.adjustLayout() + if isDeviceRotating { + hintView.removeFromSuperview() + } else { + if let screenHeight = view.window?.windowScene?.screen.bounds.height { + let maxHeight: CGFloat = if isPad { + view.frame.height / 2 + } else { + screenHeight - view.frame.height - UX.hintViewMargin * 4 + } + let height = min(UIFontMetrics.default.scaledValue(for: UX.hintViewHeight), maxHeight) + let contentSizeCategory = UIApplication.shared.preferredContentSizeCategory + hintViewHeightConstraint?.constant = + contentSizeCategory.isAccessibilityCategory ? height : UX.hintViewHeight + } + } + view.setNeedsLayout() + view.layoutIfNeeded() } private func shouldDisplayHintView() -> Bool { - // 1. Present hint if it was not presented before, - // 2. feature is enabled and - // 3. is not fresh install - if viewProvider.shouldPresentContextualHint() && - featureFlags.isFeatureEnabled(.menuRefactorHint, checking: .buildOnly) { - if InstallType.get() == .fresh { - viewProvider.markContextualHintPresented() - return false + // Don't display CFR in landscape mode for iPhones + if UIDevice.current.isIphoneLandscape { + return false + } + + // Don't display CFR for fresh installs for users that never saw before the photon main menu + if InstallType.get() == .fresh { + if let photonMainMenuShown = profile.prefs.boolForKey(PrefsKeys.PhotonMainMenuShown), + photonMainMenuShown { + return viewProvider.shouldPresentContextualHint() } - return true + viewProvider.markContextualHintPresented() + return false } - return false + return viewProvider.shouldPresentContextualHint() } // MARK: - UIAdaptivePresentationControllerDelegate func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { - coordinator?.dismissMenuModal(animated: true) + store.dispatch( + MainMenuAction( + windowUUID: self.windowUUID, + actionType: MainMenuActionType.menuDismissed, + currentTabInfo: menuState.currentTabInfo + ) + ) + coordinator?.removeCoordinatorFromParent() } // MARK: - UISheetPresentationControllerDelegate diff --git a/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift b/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift index 6e61d004ccae1..e31576836c767 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift @@ -2,13 +2,17 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ -import Account +// Ecosia: Remove `import Account` +// import Account import Foundation import Shared import Storage import UIKit import SwiftUI import Common +import Ecosia +// Ecosia: Need SafariServices to enable "open in safari" action +import SafariServices protocol ToolBarActionMenuDelegate: AnyObject { func updateToolbarState() @@ -17,6 +21,8 @@ protocol ToolBarActionMenuDelegate: AnyObject { @discardableResult func openURLInNewTab(_ url: URL?, isPrivate: Bool) -> Tab func openNewTabFromMenu(focusLocationField: Bool, isPrivate: Bool) + // Ecosia: Add option to open in same tab + func openURLInCurrentTab(_ url: URL?) func showLibrary(panel: LibraryPanelType) func showViewController(viewController: UIViewController) @@ -105,12 +111,15 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, func getToolbarActions(navigationController: UINavigationController?, completion: @escaping ([[PhotonRowActions]]) -> Void) { var actions: [[PhotonRowActions]] = [] - let firstMiscSection = getFirstMiscSection(navigationController) + // Ecosia: Remove unused constant + // let firstMiscSection = getFirstMiscSection(navigationController) if isHomePage { actions.append(contentsOf: [ + /* Ecosia: Remove Misc Section + firstMiscSection + */ getLibrarySection(), - firstMiscSection, getLastSection() ]) @@ -119,11 +128,16 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, // Actions on site page need specific data to be loaded updateData(dataLoadingCompletion: { actions.append(contentsOf: [ + /* Ecosia: Review Sections self.getNewTabSection(), - self.getLibrarySection(), + getLibrarySection(), firstMiscSection, self.getSecondMiscSection(), self.getLastSection() + */ + self.getPageActionsSection(navigationController), + self.getLibrarySection(), + self.getLastSection() ]) DispatchQueue.main.async { @@ -133,6 +147,69 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } } + // Ecosia: Readd sections from v104 + + private func getPageActionsSection(_ navigationController: UINavigationController?) -> [PhotonRowActions] { + var section = [PhotonRowActions]() + + let bookmarkAction = getBookmarkAction() + section.append(.init(bookmarkAction)) + + let readingListAction = getReadingListAction() + section.append(.init(readingListAction)) + + let shortAction = getShortcutAction() + append(to: §ion, action: shortAction) + + let copyAction = getCopyAction() + append(to: §ion, action: copyAction) + + if !isHomePage && !isFileURL { + + // Ecosia: Add Zoom Action + let zoomAction = getZoomAction() + append(to: §ion, action: zoomAction) + + let findInPageAction = getFindInPageAction() + append(to: §ion, action: findInPageAction) + + let desktopSiteAction = getRequestDesktopSiteAction() + append(to: §ion, action: desktopSiteAction) + + /* Ecosia: Remove report issue + let reportSiteIssueAction = getReportSiteIssueAction() + append(to: §ion, action: reportSiteIssueAction) + */ + } + + if let safari = getOpenInSafariAction(navigationController) { + section.append(.init(safari)) + } + + return section + } + + // MARK: - Ecosia Additions + + private func getOpenInSafariAction(_ navigationController: UINavigationController?) -> SingleActionViewModel? { + + guard let url = selectedTab?.canonicalURL?.displayURL, + ["http", "https"].contains(url.scheme), let navigationController = navigationController else { return nil } + + let model = SingleActionViewModel(title: .localized(.openInSafari), iconString: "safari") { model in + + let config = SFSafariViewController.Configuration() + config.entersReaderIfAvailable = false + config.barCollapsingEnabled = false + let safari = SFSafariViewController(url: url, configuration: config) + safari.dismissButtonStyle = .close + navigationController.present(safari, animated: true, completion: nil) + Analytics.shared.menuClick(.openInSafari) + } + + return model + } + // MARK: - Update data private let dataQueue = DispatchQueue(label: "com.moz.mainMenuAction.queue") @@ -204,6 +281,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, var section = [PhotonRowActions]() if !isFileURL { + /* Ecosia: Rearrange item positions let bookmarkSection = getBookmarkSection() append(to: §ion, action: bookmarkSection) @@ -215,17 +293,33 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let readingListSection = getReadingListSection() append(to: §ion, action: readingListSection) + */ + + let bookmarkSection = getBookmarkSection() + append(to: §ion, action: bookmarkSection) + + let historySection = getHistoryLibraryAction() + append(to: §ion, action: historySection) + + let readingListSection = getReadingListSection() + append(to: §ion, action: readingListSection) + + let downloadSection = getDownloadsLibraryAction() + append(to: §ion, action: downloadSection) } + /* Ecosia: Remove sync menu let syncAction = syncMenuButton() append(to: §ion, action: syncAction) - + */ return section } private func getFirstMiscSection(_ navigationController: UINavigationController?) -> [PhotonRowActions] { var section = [PhotonRowActions]() + // Ecosia: Rearrange items + if !isHomePage && !isFileURL { if featureFlags.isFeatureEnabled(.zoomFeature, checking: .buildOnly) { let zoomAction = getZoomAction() @@ -239,14 +333,23 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, append(to: §ion, action: desktopSiteAction) } + // Ecosia: Adding help button + let helpAction = getHelpAction() + section.append(helpAction) + + /* Ecosia: Disable night mode feature flag if featureFlags.isFeatureEnabled(.nightMode, checking: .buildOnly) { let nightModeAction = getNightModeAction() append(to: §ion, action: nightModeAction) } + */ + let nightModeAction = getNightModeAction() + append(to: §ion, action: nightModeAction) + /* Ecosia: Disable Passwords option let passwordsAction = getPasswordAction(navigationController: navigationController) append(to: §ion, action: passwordsAction) - + */ if !isHomePage && !isFileURL { let reportSiteIssueAction = getReportSiteIssueAction() append(to: §ion, action: reportSiteIssueAction) @@ -288,6 +391,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, private func getLastSection() -> [PhotonRowActions] { var section = [PhotonRowActions]() + /* Ecosia: Return different items for this sections if isHomePage { let whatsNewAction = getWhatsNewAction() append(to: §ion, action: whatsNewAction) @@ -301,6 +405,16 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let settingsAction = getSettingsAction() section.append(settingsAction) + */ + + let nightModeAction = getNightModeAction() + section.append(contentsOf: nightModeAction) + + let customizeHomePageAction = getCustomizeHomePageAction() + append(to: §ion, action: customizeHomePageAction) + + let helpAction = getHelpAction() + section.append(helpAction) return section } @@ -314,6 +428,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let shouldFocusLocationField = NewTabAccessors.getNewTabPage(self.profile.prefs) != .homePage self.delegate?.openNewTabFromMenu(focusLocationField: shouldFocusLocationField, isPrivate: tab.isPrivate) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .createNewTab) + + // Ecosia: Track new tab action + Analytics.shared.menuClick(.newTab) }.items } @@ -322,6 +439,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, iconString: StandardImageIdentifiers.Large.history) { _ in self.delegate?.showLibrary(panel: .history) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .viewHistoryPanel) + + // Ecosia: Track history action + Analytics.shared.menuClick(.history) }.items } @@ -330,6 +450,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, iconString: StandardImageIdentifiers.Large.download) { _ in self.delegate?.showLibrary(panel: .downloads) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .viewDownloadsPanel) + + // Ecosia: Track downloads action + Analytics.shared.menuClick(.downloads) }.items } @@ -342,15 +465,25 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let zoomAction = SingleActionViewModel(title: title, iconString: StandardImageIdentifiers.Large.pageZoom) { _ in self.delegate?.showZoomPage(tab: tab) + + // Ecosia: Track zoom action + Analytics.shared.menuClick(.zoom) }.items return zoomAction } private func getFindInPageAction() -> PhotonRowActions { + /* Ecosia: Update Image return SingleActionViewModel(title: .LegacyAppMenu.AppMenuFindInPageTitleString, iconString: StandardImageIdentifiers.Large.search) { _ in + */ + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuFindInPageTitleString, + iconString: "menu-FindInPageUpdate") { _ in TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .findInPage) self.delegate?.showFindInPage() + + // Ecosia: Track find in page action + Analytics.shared.menuClick(.findInPage) }.items } @@ -383,18 +516,28 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, isPrivate: tab.isPrivate ) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: siteTypeTelemetryObject) + + // Ecosia: Track request desktop site action + Analytics.shared.menuClick(.requestDesktopSite) } }.items } private func getCopyAction() -> PhotonRowActions? { + /* Ecosia: Update Image return SingleActionViewModel(title: .LegacyAppMenu.AppMenuCopyLinkTitleString, iconString: StandardImageIdentifiers.Large.link) { _ in + */ + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuCopyLinkTitleString, + iconString: "menu-Copy-Link") { _ in TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .copyAddress) if let url = self.selectedTab?.canonicalURL?.displayURL { UIPasteboard.general.url = url self.delegate?.showToast(message: .LegacyAppMenu.AppMenuCopyURLConfirmMessage, toastAction: .copyUrl) } + + // Ecosia: Track copy link action + Analytics.shared.menuClick(.copyLink) }.items } @@ -438,10 +581,14 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, private func getHelpAction() -> PhotonRowActions { return SingleActionViewModel(title: .LegacyAppMenu.Help, iconString: StandardImageIdentifiers.Large.helpCircle) { _ in + /* Ecosia: Replacing Firefox Support URL with Ecosia FAQ URL if let url = URL(string: "https://support.mozilla.org/products/ios") { self.delegate?.openURLInNewTab(url, isPrivate: self.tabManager.selectedTab?.isPrivate ?? false) } TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .help) + */ + self.delegate?.openURLInCurrentTab(EcosiaEnvironment.current.urlProvider.faq) + Analytics.shared.menuClick(.help) }.items } @@ -450,6 +597,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, iconString: StandardImageIdentifiers.Large.edit) { _ in self.delegate?.showCustomizeHomePage() TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .customizeHomePage) + + // Ecosia: Track customize homepage action + Analytics.shared.menuClick(.customizeHomepage) }.items } @@ -470,19 +620,28 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, var items: [PhotonRowActions] = [] let nightModeEnabled = NightModeHelper.isActivated() + /* Ecosia: Update Night mode copy/imagery let nightModeTitle: String = if nightModeEnabled { .LegacyAppMenu.AppMenuTurnOffNightMode } else { .LegacyAppMenu.AppMenuTurnOnNightMode } - let nightMode = SingleActionViewModel( title: nightModeTitle, iconString: StandardImageIdentifiers.Large.nightMode, isEnabled: nightModeEnabled ) { _ in + */ + let nightModeTitle: String = nightModeEnabled ? .localized(.turnOffDarkMode) : .localized(.forceDarkMode) + let nightMode = SingleActionViewModel(title: nightModeTitle, + text: .localized(.invertColors), + iconString: nightModeEnabled ? "darkModeSolid" : "darkMode", + isEnabled: nightModeEnabled) { _ in NightModeHelper.toggle() + // Ecosia: Track dark mode changes + Analytics.shared.menuStatus(changed: .darkMode, to: !nightModeEnabled) + if NightModeHelper.isActivated() { TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .nightModeEnabled) } else { @@ -496,6 +655,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, return items } + /* Ecosia: Remove sync menu button dependant from the Account module private func syncMenuButton() -> PhotonRowActions? { let action: (SingleActionViewModel) -> Void = { [weak self] action in let fxaParams = FxALaunchParams(entrypoint: .browserMenu, query: [:]) @@ -540,6 +700,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, tapHandler: action).items return syncOption } + */ // MARK: Whats New @@ -579,16 +740,30 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, else { return } self.share(fileURL: url, buttonView: self.buttonView) + + // Ecosia: Analytics + Analytics.shared.menuShare(.file) }.items } private func getShareAction() -> PhotonRowActions { return SingleActionViewModel(title: .LegacyAppMenu.Share, iconString: StandardImageIdentifiers.Large.share) { _ in - guard let tab = self.selectedTab, let url = tab.canonicalURL?.displayURL else { return } + + // Ecosia: if we have nothing to share we share Ecosia Root URL + // guard let tab = self.selectedTab, let url = tab.canonicalURL?.displayURL else { return } + guard let tab = self.selectedTab else { return } + let url = tab.canonicalURL?.displayURL ?? EcosiaEnvironment.current.urlProvider.root TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .sharePageWith) + // Ecosia: Analytics + if tab.canonicalURL?.displayURL == nil { + Analytics.shared.menuShare(.ntp) + } else { + Analytics.shared.menuShare(.web) + } + guard let temporaryDocument = tab.temporaryDocument else { self.navigationHandler?.showShareExtension( url: url, @@ -657,9 +832,16 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getReadingListLibraryAction() -> SingleActionViewModel { + /* Ecosia: Update image return SingleActionViewModel(title: .LegacyAppMenu.ReadingList, iconString: StandardImageIdentifiers.Large.readingList) { _ in + */ + return SingleActionViewModel(title: .LegacyAppMenu.ReadingList, + iconString: "libraryReading") { _ in self.delegate?.showLibrary(panel: .readingList) + + // Ecosia: Track reading list action + Analytics.shared.menuClick(.readingList) } } @@ -668,8 +850,12 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getAddReadingListAction() -> SingleActionViewModel { + /* Ecosia: Update Title and Image return SingleActionViewModel(title: .LegacyAppMenu.AddReadingList, iconString: StandardImageIdentifiers.Large.readingListAdd) { _ in + */ + return SingleActionViewModel(title: .ShareAddToReadingList, + iconString: "addToReadingListUpdate") { _ in guard let tab = self.selectedTab, let url = self.tabUrl?.displayURL else { return } @@ -686,6 +872,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, value: .pageActionMenu ) self.delegate?.showToast(message: .LegacyAppMenu.AddToReadingListConfirmMessage, toastAction: .addToReadingList) + + // Ecosia: Track add reading list + Analytics.shared.menuStatus(changed: .readingList, to: true) } } @@ -703,6 +892,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, method: .delete, object: .readingListItem, value: .pageActionMenu) + + // Ecosia: Track remove reading list + Analytics.shared.menuStatus(changed: .readingList, to: false) } } @@ -724,6 +916,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, return SingleActionViewModel(title: .LegacyAppMenu.Bookmarks, iconString: StandardImageIdentifiers.Large.bookmarkTrayFill) { _ in self.delegate?.showLibrary(panel: .bookmarks) + + // Ecosia: Track reading list action + Analytics.shared.menuClick(.bookmarks) } } @@ -732,8 +927,12 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getAddBookmarkAction() -> SingleActionViewModel { + /* Ecosia: Update Title and Image return SingleActionViewModel(title: .LegacyAppMenu.AddBookmark, iconString: StandardImageIdentifiers.Large.bookmark) { _ in + */ + return SingleActionViewModel(title: .KeyboardShortcuts.AddBookmark, + iconString: "menu-Bookmark") { _ in guard let tab = self.selectedTab, let url = tab.canonicalURL?.displayURL else { return } @@ -746,11 +945,16 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, object: .bookmark, value: .pageActionMenu ) + + // Ecosia: Track add bookmark + Analytics.shared.menuStatus(changed: .bookmark, to: true) } } private func getRemoveBookmarkAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .LegacyAppMenu.RemoveBookmark, + // Ecosia: Update Title + // return SingleActionViewModel(title: .LegacyAppMenu.RemoveBookmark, + return SingleActionViewModel(title: .RemoveBookmarkContextMenuTitle, iconString: StandardImageIdentifiers.Large.bookmarkSlash) { _ in guard let url = self.tabUrl?.displayURL else { return } @@ -769,6 +973,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, object: .bookmark, value: .pageActionMenu ) + + // Ecosia: Track remove bookmark + Analytics.shared.menuStatus(changed: .bookmark, to: false) } } @@ -779,8 +986,12 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getAddShortcutAction() -> SingleActionViewModel { + /* Ecosia: Update Image return SingleActionViewModel(title: .AddToShortcutsActionTitle, iconString: StandardImageIdentifiers.Large.pin) { _ in + */ + return SingleActionViewModel(title: .AddToShortcutsActionTitle, + iconString: "action_pin") { _ in guard let url = self.selectedTab?.url?.displayURL, let title = self.selectedTab?.displayTitle else { return } let site = Site(url: url.absoluteString, title: title) @@ -790,12 +1001,19 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .pinToTopSites) + + // Ecosia: Track add shortcut + Analytics.shared.menuStatus(changed: .shortcut, to: true) } } private func getRemoveShortcutAction() -> SingleActionViewModel { + /* Ecosia: Update Image return SingleActionViewModel(title: .LegacyAppMenu.RemoveFromShortcuts, iconString: StandardImageIdentifiers.Large.pinSlash) { _ in + */ + return SingleActionViewModel(title: .LegacyAppMenu.RemoveFromShortcuts, + iconString: "action_unpin") { _ in guard let url = self.selectedTab?.url?.displayURL, let title = self.selectedTab?.displayTitle else { return } let site = Site(url: url.absoluteString, title: title) @@ -808,6 +1026,9 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } } TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .removePinnedSite) + + // Ecosia: Track remove shortcut + Analytics.shared.menuStatus(changed: .shortcut, to: false) } } @@ -838,3 +1059,10 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } } } + +// Ecosia: Add helper function from old codebase +extension MainMenuActionHelper { + func getSharingAction() -> PhotonRowActions { + isFileURL ? getShareFileAction() : getShareAction() + } +} diff --git a/firefox-ios/Client/Frontend/Browser/MetadataParserHelper.swift b/firefox-ios/Client/Frontend/Browser/MetadataParserHelper.swift index 348dc41b65c08..6d31b68ef74ef 100644 --- a/firefox-ios/Client/Frontend/Browser/MetadataParserHelper.swift +++ b/firefox-ios/Client/Frontend/Browser/MetadataParserHelper.swift @@ -6,6 +6,7 @@ import Foundation import Shared import Storage import WebKit +import Ecosia class MetadataParserHelper: TabEventHandler { let tabEventWindowResponseType: TabEventHandlerWindowResponseType = .allWindows diff --git a/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift b/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift index df13e3d8ac603..af2b28ec3d157 100644 --- a/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift +++ b/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift @@ -25,6 +25,8 @@ struct MIMEType { static let Calendar = "text/calendar" static let USDZ = "model/vnd.usdz+zip" static let Reality = "model/vnd.reality" + static let OpenDocument = "application/msword" + static let MicrosoftWord = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" private static let webViewViewableTypes: [String] = [ MIMEType.Bitmap, @@ -34,12 +36,26 @@ struct MIMEType { MIMEType.PDF, MIMEType.PlainText, MIMEType.PNG, - MIMEType.WebP] + MIMEType.WebP + ] + + private static let downloadableTypes: [String] = [ + MIMEType.PDF, + MIMEType.OpenDocument, + MIMEType.MicrosoftWord, + MIMEType.PNG, + MIMEType.JPEG + ] static func canShowInWebView(_ mimeType: String) -> Bool { return webViewViewableTypes.contains(mimeType.lowercased()) } + static func canBeDownloaded(_ mimeType: String?) -> Bool { + guard let mimeType else { return false } + return downloadableTypes.contains(mimeType.lowercased()) + } + static func mimeTypeFromFileExtension(_ fileExtension: String) -> String { if let uti = UTType(filenameExtension: fileExtension), let mimeType = uti.preferredMIMEType { @@ -74,28 +90,43 @@ class DownloadHelper: NSObject { required init?( request: URLRequest?, response: URLResponse, - cookieStore: WKHTTPCookieStore, - canShowInWebView: Bool, - forceDownload: Bool + cookieStore: WKHTTPCookieStore ) { guard let request = request else { return nil } - let mimeType = response.mimeType ?? MIMEType.OctetStream - let isAttachment = mimeType == MIMEType.OctetStream - - // Bug 1474339 - Don't auto-download files served with 'Content-Disposition: attachment' - // Leaving this here for now, but commented out. Checking this HTTP header is - // what Desktop does should we ever decide to change our minds on this. - // let contentDisposition = (response as? HTTPURLResponse)?.allHeaderFields["Content-Disposition"] as? String - // let isAttachment = contentDisposition?.starts(with: "attachment") ?? (mimeType == MIMEType.OctetStream) - - guard isAttachment || !canShowInWebView || forceDownload else { return nil } - self.cookieStore = cookieStore self.request = request self.preflightResponse = response } + func shouldDownloadFile(canShowInWebView: Bool, + forceDownload: Bool, + isForMainFrame: Bool) -> Bool { + let mimeType = preflightResponse.mimeType ?? MIMEType.OctetStream + + // Handles automatic Blob URL download + if mimeType == MIMEType.OctetStream { + return true + } + + // Handles attachments downloads. + // Only supports PDF and Words docs but can be expanded to support more extensions + if shouldDownloadAttachment(isForMainFrame: isForMainFrame) { + return true + } + + // Handles forced downloads && MIMETypes not supported on webview + return !canShowInWebView || forceDownload + } + + func shouldDownloadAttachment(isForMainFrame: Bool) -> Bool { + let contentDisposition = (preflightResponse as? HTTPURLResponse)?.allHeaderFields["Content-Disposition"] as? String + let isAttachment = contentDisposition?.starts(with: "attachment") ?? false + let canBeDownloaded = MIMEType.canBeDownloaded(preflightResponse.mimeType) && !isForMainFrame + + return isAttachment && canBeDownloaded + } + func downloadViewModel(windowUUID: WindowUUID, okAction: @escaping (HTTPDownload) -> Void) -> PhotonActionSheetViewModel? { var requestUrl = request.url diff --git a/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift b/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift index e7c7df5259fd8..c11845e7e8bd3 100644 --- a/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Search/SearchViewController.swift @@ -7,6 +7,7 @@ import Shared import Storage import Common import SiteImageView +import Ecosia private struct SearchViewControllerUX { static let EngineButtonHeight: Float = 44 // Equivalent to toolbar height, fixed at the moment @@ -75,7 +76,10 @@ class SearchViewController: SiteTableViewController, }() private lazy var searchButton: UIButton = .build { button in + /* Ecosia: Update image reference for search button let image = UIImage(named: StandardImageIdentifiers.Large.search)?.withRenderingMode(.alwaysTemplate) + */ + let image = UIImage(named: "searches")?.withRenderingMode(.alwaysTemplate) button.setImage(image, for: []) button.imageView?.contentMode = .scaleAspectFit button.addTarget(self, action: #selector(self.didClickSearchButton), for: .touchUpInside) @@ -89,7 +93,12 @@ class SearchViewController: SiteTableViewController, self.viewModel = viewModel self.tabManager = tabManager self.searchTelemetry = SearchTelemetry(tabManager: tabManager) + + /* Ecosia: Update init adding `style` super.init(profile: profile, windowUUID: tabManager.windowUUID) + */ + super.init(profile: profile, windowUUID: tabManager.windowUUID, style: .insetGrouped) + viewModel.delegate = self tableView.sectionHeaderTopPadding = 0 @@ -250,7 +259,9 @@ class SearchViewController: SiteTableViewController, tableView.topAnchor.constraint(equalTo: view.topAnchor), tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - tableView.bottomAnchor.constraint(equalTo: searchEngineScrollView.topAnchor) + // Ecosia: Hide `searchEngineScrollView` by changing constraints + // tableView.bottomAnchor.constraint(equalTo: searchEngineScrollView.topAnchor) + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) } @@ -436,6 +447,12 @@ class SearchViewController: SiteTableViewController, searchTelemetry?.engagementType = .tap switch SearchListSection(rawValue: indexPath.section)! { case .searchSuggestions: + // Ecosia: Check if this is the AI Search item + if isAISearchRow(indexPath) { + handleAISearchSelection(indexPath) + return + } + guard let defaultEngine = viewModel.searchEnginesManager?.defaultEngine else { return } searchTelemetry?.selectedResult = .searchSuggest @@ -556,11 +573,20 @@ class SearchViewController: SiteTableViewController, if let section = SearchListSection(rawValue: indexPath.section) { switch section { case .searchSuggestions: + /* Ecosia: Modified to skip telemetry for AI Search item and use safe array access if let site = viewModel.suggestions?[indexPath.row] { if searchTelemetry?.visibleSuggestions.contains(site) == false { searchTelemetry?.visibleSuggestions.append(site) } } + */ + // Ecosia: Skip telemetry for AI Search item + if !isAISearchRow(indexPath), + let site = safeSuggestion(at: indexPath.row) { + if searchTelemetry?.visibleSuggestions.contains(site) == false { + searchTelemetry?.visibleSuggestions.append(site) + } + } case .openedTabs: if viewModel.filteredOpenedTabs.count > indexPath.row { let openedTab = viewModel.filteredOpenedTabs[indexPath.row] @@ -606,13 +632,20 @@ class SearchViewController: SiteTableViewController, } } } + + // Ecosia: Update cell background + cell.backgroundColor = currentTheme().colors.ecosia.backgroundElevation1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch SearchListSection(rawValue: section)! { case .searchSuggestions: + // Ecosia: Use our custom method that includes AI Search item + return numberOfRowsForSearchSuggestions() + /* Ecosia: Modified to include AI Search item when feature flag is enabled guard let count = viewModel.suggestions?.count else { return 0 } return count < 4 ? count : 4 + */ case .openedTabs: return viewModel.filteredOpenedTabs.count case .remoteTabs: @@ -629,7 +662,8 @@ class SearchViewController: SiteTableViewController, } func numberOfSections(in tableView: UITableView) -> Int { - return SearchListSection.allCases.count + // return SearchListSection.allCases.count + return 4 } func tableView(_ tableView: UITableView, didHighlightRowAt indexPath: IndexPath) { @@ -643,7 +677,16 @@ class SearchViewController: SiteTableViewController, let suggestion = viewModel.historySites[indexPath.item] searchDelegate?.searchViewController(self, didHighlightText: suggestion.url, search: false) case .searchSuggestions: - guard let suggestion = viewModel.suggestions?[indexPath.item] else { return } + // Ecosia: Check if this is the AI Search item + if isAISearchRow(indexPath) { + handleAISearchHighlight(indexPath) + return + } + + /* Ecosia: Modified to handle AI Search item and use safe array access + let suggestion = viewModel.suggestions?[indexPath.item] ?? "" + */ + guard let suggestion = safeSuggestion(at: indexPath.item) else { return } searchDelegate?.searchViewController(self, didHighlightText: suggestion, search: false) case .remoteTabs: let suggestion = viewModel.remoteClientTabs[indexPath.item] @@ -663,7 +706,11 @@ class SearchViewController: SiteTableViewController, searchButton.layer.backgroundColor = SearchViewControllerUX.EngineButtonBackgroundColor searchButton.tintColor = currentTheme().colors.iconPrimary + /* Ecosia: Update background searchEngineContainerView.layer.backgroundColor = currentTheme().colors.layer1.cgColor + */ + searchEngineContainerView.layer.backgroundColor = currentTheme().colors.ecosia.backgroundPrimary.cgColor + searchEngineContainerView.layer.shadowColor = currentTheme().colors.shadowDefault.cgColor reloadData() } @@ -685,10 +732,19 @@ class SearchViewController: SiteTableViewController, oneLineCell: OneLineTableViewCell, for section: SearchListSection, _ indexPath: IndexPath) -> UITableViewCell { + // Ecosia: It makes sense to call applyTheme here to let it do the first setup and then override the values appropriately + oneLineCell.applyTheme(theme: currentTheme()) + twoLineCell.applyTheme(theme: currentTheme()) + var cell = UITableViewCell() switch section { case .searchSuggestions: + /* Ecosia: Modified to handle AI Search item and use safe array access if let site = viewModel.suggestions?[indexPath.row] { + */ + if isAISearchRow(indexPath) { + cell = configureAISearchCell(oneLineCell) + } else if let site = safeSuggestion(at: indexPath.row) { oneLineCell.titleLabel.text = site if Locale.current.languageCode == "en", let attributedString = getAttributedBoldSearchSuggestions( @@ -699,17 +755,38 @@ class SearchViewController: SiteTableViewController, } oneLineCell.leftImageView.contentMode = .center oneLineCell.leftImageView.layer.borderWidth = 0 + + /* Ecosia: Update set image oneLineCell.leftImageView.manuallySetImage( UIImage(named: SearchViewControllerUX.SearchImage)?.withRenderingMode(.alwaysTemplate) ?? UIImage() ) + */ + oneLineCell.leftImageView.manuallySetImage(UIImage.templateImageNamed(SearchViewControllerUX.SearchImage) ?? UIImage()) + /* Ecosia: Update tintColor oneLineCell.leftImageView.tintColor = currentTheme().colors.iconPrimary + */ + oneLineCell.leftImageView.tintColor = currentTheme().colors.ecosia.buttonBackgroundPrimary + oneLineCell.leftImageView.backgroundColor = nil let appendButton = UIButton(type: .roundedRect) + + /* Ecosia: Update image appendButton.setImage(searchAppendImage?.withRenderingMode(.alwaysTemplate), for: .normal) + */ + appendButton.setImage(UIImage.templateImageNamed("searchAppend"), for: .normal) + appendButton.addTarget(self, action: #selector(append(_ :)), for: .touchUpInside) + + /* Ecosia: Update tintColor appendButton.tintColor = currentTheme().colors.iconPrimary + */ + appendButton.tintColor = currentTheme().colors.textPrimary + appendButton.sizeToFit() oneLineCell.accessoryView = indexPath.row > 0 ? appendButton : nil + + // Ecosia: Enrich background + oneLineCell.backgroundColor = currentTheme().colors.ecosia.backgroundElevation1 cell = oneLineCell } case .openedTabs: @@ -724,6 +801,8 @@ class SearchViewController: SiteTableViewController, if let urlString = openedTab.url?.absoluteString { twoLineCell.leftImageView.setFavicon(FaviconImageViewModel(siteURLString: urlString)) } + // Ecosia: Enrich background + twoLineCell.backgroundColor = currentTheme().colors.ecosia.backgroundElevation1 twoLineCell.accessoryView = nil cell = twoLineCell } @@ -740,6 +819,8 @@ class SearchViewController: SiteTableViewController, twoLineCell.leftImageView.layer.borderWidth = SearchViewControllerUX.IconBorderWidth let urlString = remoteTab.URL.absoluteString twoLineCell.leftImageView.setFavicon(FaviconImageViewModel(siteURLString: urlString)) + // Ecosia: Enrich background + twoLineCell.backgroundColor = currentTheme().colors.ecosia.backgroundElevation1 twoLineCell.accessoryView = nil cell = twoLineCell } @@ -793,10 +874,11 @@ class SearchViewController: SiteTableViewController, twoLineCell.accessoryView = nil cell = twoLineCell } - + /* Ecosia: No sense to call applyTheme at this point which overrides all the values set above. Will move up // We need to set the correct theme on the cells when the initial display happens oneLineCell.applyTheme(theme: currentTheme()) twoLineCell.applyTheme(theme: currentTheme()) + */ return cell } @@ -818,9 +900,13 @@ class SearchViewController: SiteTableViewController, func append(_ sender: UIButton) { let buttonPosition = sender.convert(CGPoint(), to: tableView) + /* Ecosia: Modified to handle AI Search item and use safe array access + if let indexPath = tableView.indexPathForRow(at: buttonPosition), let newQuery = viewModel.suggestions?[indexPath.row] { + */ if let indexPath = tableView.indexPathForRow( at: buttonPosition - ), let newQuery = viewModel.suggestions?[indexPath.row] { + ), !isAISearchRow(indexPath), // Ecosia: Add AI Search check + let newQuery = safeSuggestion(at: indexPath.row) { searchDelegate?.searchViewController(self, didAppend: newQuery + " ") viewModel.searchQuery = newQuery + " " searchTelemetry?.searchQuery = viewModel.searchQuery diff --git a/firefox-ios/Client/Frontend/Browser/Search/SearchViewModel.swift b/firefox-ios/Client/Frontend/Browser/Search/SearchViewModel.swift index 17b0bea9b9089..4575fc14a2fba 100644 --- a/firefox-ios/Client/Frontend/Browser/Search/SearchViewModel.swift +++ b/firefox-ios/Client/Frontend/Browser/Search/SearchViewModel.swift @@ -164,7 +164,10 @@ class SearchViewModel: FeatureFlaggable, LoaderListener { func shouldShowHeader(for section: Int) -> Bool { switch section { case SearchListSection.firefoxSuggestions.rawValue: + /* Ecosia: Do not show header return hasFirefoxSuggestions + */ + return false case SearchListSection.searchSuggestions.rawValue: return shouldShowSearchEngineSuggestions default: @@ -284,10 +287,13 @@ class SearchViewModel: FeatureFlaggable, LoaderListener { } } let config = searchFeature.value().awesomeBar + /* Ecosia: hardcode config // Searching within the content will get annoying, so only start searching // in content when there are at least one word with more than 3 letters in. let searchInContent = config.usePageContent && searchTerms.contains(where: { $0.count >= config.minSearchTerm }) + */ + let searchInContent = searchTerms.contains(where: { $0.count >= 3 }) filteredOpenedTabs = currentTabs.filter { tab in guard let url = tab.url, diff --git a/firefox-ios/Client/Frontend/Browser/SearchEngines/DefaultSearchEngineProvider.swift b/firefox-ios/Client/Frontend/Browser/SearchEngines/DefaultSearchEngineProvider.swift index 273058fd5a518..c3fe4318055f4 100644 --- a/firefox-ios/Client/Frontend/Browser/SearchEngines/DefaultSearchEngineProvider.swift +++ b/firefox-ios/Client/Frontend/Browser/SearchEngines/DefaultSearchEngineProvider.swift @@ -102,7 +102,8 @@ class DefaultSearchEngineProvider: SearchEngineProvider { }).compactMap({ parser.parse($0.path, engineID: $0.name) }).sorted { e, _ in - e.shortName == defaultEngineName + // Ecosia: Add lowercased to ensure equality + e.shortName.lowercased() == defaultEngineName.lowercased() } completion(result) diff --git a/firefox-ios/Client/Frontend/Browser/SearchEngines/OpenSearchEngine.swift b/firefox-ios/Client/Frontend/Browser/SearchEngines/OpenSearchEngine.swift index 43b44d44a946f..4364d963273d0 100644 --- a/firefox-ios/Client/Frontend/Browser/SearchEngines/OpenSearchEngine.swift +++ b/firefox-ios/Client/Frontend/Browser/SearchEngines/OpenSearchEngine.swift @@ -92,7 +92,7 @@ class OpenSearchEngine: NSObject, NSSecureCoding { /// Returns the search URL for the given query. func searchURLForQuery(_ query: String) -> URL? { - return getURLFromTemplate(searchTemplate, query: query) + return URL.ecosiaSearchWithQuery(query) } /// Returns the search suggestion URL for the given query. diff --git a/firefox-ios/Client/Frontend/Browser/SearchLoader.swift b/firefox-ios/Client/Frontend/Browser/SearchLoader.swift index 5999be1a77782..62eecf0182136 100644 --- a/firefox-ios/Client/Frontend/Browser/SearchLoader.swift +++ b/firefox-ios/Client/Frontend/Browser/SearchLoader.swift @@ -5,7 +5,9 @@ import Foundation import Shared import Storage +/* Ecosia: Remove Glean import Glean + */ import Common private let URLBeforePathRegex = try? NSRegularExpression(pattern: "^https?://([^/]+)/", options: []) @@ -82,16 +84,22 @@ final class SearchLoader: Loader, SearchViewModel>, FeatureFlaggabl var query: String = "" { didSet { + /* Ecosia: Remove Glean let timerid = GleanMetrics.Awesomebar.queryTime.start() + */ guard profile is BrowserProfile else { assertionFailure("nil profile") + /* Ecosia: Remove Glean GleanMetrics.Awesomebar.queryTime.cancel(timerid) + */ return } if query.isEmpty { load(Cursor(status: .success, msg: "Empty query")) + /* Ecosia: Remove Glean GleanMetrics.Awesomebar.queryTime.cancel(timerid) + */ return } @@ -116,7 +124,9 @@ final class SearchLoader: Loader, SearchViewModel>, FeatureFlaggabl DispatchQueue.main.async { self.updateUIWithBookmarksAsSitesResults(queries: queries, + /* Ecosia: Remove Glean timerid: timerid, + */ historyHighlightsEnabled: historyHighlightsEnabled, oldValue: oldValue) } @@ -125,12 +135,16 @@ final class SearchLoader: Loader, SearchViewModel>, FeatureFlaggabl } private func updateUIWithBookmarksAsSitesResults(queries: [[Site]], + /* Ecosia: Remove Glean timerid: TimerId, + */ historyHighlightsEnabled: Bool, oldValue: String) { let results = queries defer { + /* Ecosia: Remove Glean GleanMetrics.Awesomebar.queryTime.stopAndAccumulate(timerid) + */ } let bookmarksSites = results[safe: 0] ?? [] diff --git a/firefox-ios/Client/Frontend/Browser/SimpleToast.swift b/firefox-ios/Client/Frontend/Browser/SimpleToast.swift index 0aa6ad29b1e76..71091f07888bd 100644 --- a/firefox-ios/Client/Frontend/Browser/SimpleToast.swift +++ b/firefox-ios/Client/Frontend/Browser/SimpleToast.swift @@ -58,7 +58,9 @@ struct SimpleToast: ThemeApplicable { ) } - private func animate(_ toast: UIView) { + // Ecosia: Modify privacy for func + // private func animate(_ toast: UIView) { + func animate(_ toast: UIView) { UIView.animate( withDuration: Toast.UX.toastAnimationDuration, animations: { diff --git a/firefox-ios/Client/Frontend/Browser/StartAtHomeHelper.swift b/firefox-ios/Client/Frontend/Browser/StartAtHomeHelper.swift index 99462a87443f5..191bb66f04402 100644 --- a/firefox-ios/Client/Frontend/Browser/StartAtHomeHelper.swift +++ b/firefox-ios/Client/Frontend/Browser/StartAtHomeHelper.swift @@ -32,10 +32,16 @@ class StartAtHomeHelper: FeatureFlaggable { var startAtHomeSetting: StartAtHomeSetting { get { + /* Ecosia: Set to disabled if let prefs = prefs.stringForKey(PrefsKeys.UserFeatureFlagPrefs.StartAtHome) { return StartAtHomeSetting(rawValue: prefs) ?? .afterFourHours } return .afterFourHours + */ + if let prefs = prefs.stringForKey(PrefsKeys.UserFeatureFlagPrefs.StartAtHome) { + return StartAtHomeSetting(rawValue: prefs) ?? .disabled + } + return .disabled } set { prefs.setString(newValue.rawValue, forKey: PrefsKeys.UserFeatureFlagPrefs.StartAtHome) } } diff --git a/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift b/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift index a66cc71853ce9..ffcd16fb66ad5 100644 --- a/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift +++ b/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift @@ -963,7 +963,10 @@ extension LegacyTabDisplayManager: TabManagerDelegate { return } + /* Ecosia: Skip invisible tabs from being added to UI guard tab.isPrivate == self.isPrivate else { return } + */ + guard tab.isPrivate == self.isPrivate, !tab.isInvisible else { return } updateWith(animationType: .addTab) { [unowned self] in let indexToPlaceTab = getIndexToPlaceTab(placeNextToParentTab: placeNextToParentTab) diff --git a/firefox-ios/Client/Frontend/Browser/TabTrayButtonExtensions.swift b/firefox-ios/Client/Frontend/Browser/TabTrayButtonExtensions.swift index a353a16fe4de5..3d87b3ef0a57b 100644 --- a/firefox-ios/Client/Frontend/Browser/TabTrayButtonExtensions.swift +++ b/firefox-ios/Client/Frontend/Browser/TabTrayButtonExtensions.swift @@ -7,6 +7,7 @@ import UIKit import Shared class PrivateModeButton: ToggleButton, PrivateModeUI { + override init(frame: CGRect) { super.init(frame: frame) accessibilityLabel = .TabTrayToggleAccessibilityLabel @@ -25,7 +26,10 @@ class PrivateModeButton: ToggleButton, PrivateModeUI { func applyUIMode(isPrivate: Bool, theme: Theme) { isSelected = isPrivate + /* Ecosia: Update tint tintColor = isPrivate ? theme.colors.iconOnColor : theme.colors.iconPrimary + */ + tintColor = isPrivate ? theme.colors.ecosia.backgroundPrimary : theme.colors.ecosia.textPrimary imageView?.tintColor = tintColor accessibilityValue = isSelected ? .TabTrayToggleAccessibilityValueOn : .TabTrayToggleAccessibilityValueOff diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Action/TabTrayAction.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Action/TabTrayAction.swift index 81dc3a051245e..30650f2ed7277 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Action/TabTrayAction.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Action/TabTrayAction.swift @@ -31,4 +31,5 @@ enum TabTrayActionType: ActionType { case didLoadTabTray case dismissTabTray case firefoxAccountChanged + case closePrivateTabsSettingToggled } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift index 8240ae009052b..d4184d3c78104 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift @@ -57,7 +57,9 @@ class LegacyGridTabViewController: UIViewController, var currentWindowUUID: UUID? { windowUUID } var toolbarHeight: CGFloat { - return !shouldUseiPadSetup() ? view.safeAreaInsets.bottom : 0 + // Ecosia: hide Toolbar + // return !shouldUseiPadSetup() ? view.safeAreaInsets.bottom : 0 + return 0 } // This is an optional variable used if we wish to focus a tab that is not the @@ -702,7 +704,9 @@ extension LegacyGridTabViewController { let controller = AlertController(title: nil, message: nil, preferredStyle: .actionSheet) controller.addAction(UIAlertAction(title: .LegacyAppMenu.AppMenuCloseAllTabsTitleString, - style: .default, + // Ecosia: Change close all button title color + // style: .default, + style: .destructive, handler: { _ in self.closeTabsTrayBackground() }), accessibilityIdentifier: AccessibilityIdentifiers.TabTray.deleteCloseAllButton) controller.addAction(UIAlertAction(title: .TabTrayCloseAllTabsPromptCancel, diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabButton.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabButton.swift index deafcdb4d71f5..5d2eab9776f9f 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabButton.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabButton.swift @@ -95,7 +95,9 @@ class LegacyInactiveTabButton: UITableViewCell, ThemeApplicable, ReusableCell { func applyTheme(theme: Theme) { backgroundColor = .clear selectedView.backgroundColor = theme.colors.layer5Hover - roundedButton.setTitleColor(theme.colors.textPrimary, for: .normal) + // Ecosia: Update button's color + // roundedButton.setTitleColor(theme.colors.textPrimary, for: .normal) + roundedButton.setTitleColor(theme.colors.iconCritical, for: .normal) roundedButton.backgroundColor = theme.colors.layer3 roundedButton.tintColor = theme.colors.textPrimary let image = UIImage.templateImageNamed(StandardImageIdentifiers.Large.delete) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabCell.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabCell.swift index c4d11deb7772a..53922fb7e88a2 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabCell.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabCell.swift @@ -226,6 +226,10 @@ extension LegacyInactiveTabCell: UITableViewDataSource, UITableViewDelegate { guard let headerView = tableView.dequeueReusableHeaderFooterView( withIdentifier: LegacyInactiveTabHeader.cellIdentifier ) as? LegacyInactiveTabHeader else { return nil } + // Ecosia: add missing ThemeApplicable implementation + if let theme = inactiveTabsViewModel?.theme { + headerView.applyTheme(theme: theme) + } headerView.state = hasExpanded ? .down : .trailing headerView.title = String.TabsTrayInactiveTabsSectionTitle headerView.accessibilityLabel = hasExpanded ? diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabHeader.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabHeader.swift index 425e49844a64e..88266372bcb1d 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabHeader.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabHeader.swift @@ -6,7 +6,8 @@ import Common import Foundation import Shared -class LegacyInactiveTabHeader: UITableViewHeaderFooterView, ReusableCell { +// Ecosia: Add missing ThemeApplicable protocol declaration +class LegacyInactiveTabHeader: UITableViewHeaderFooterView, ReusableCell, ThemeApplicable { var state: ExpandButtonState? { willSet(state) { moreButton.setImage(state?.image, for: .normal) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabViewModel.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabViewModel.swift index 17b654182fa65..acc3e7e66b9bd 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabViewModel.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyInactiveTabs/LegacyInactiveTabViewModel.swift @@ -94,6 +94,9 @@ class LegacyInactiveTabViewModel { inactiveTabModel.tabWithStatus = LegacyInactiveTabModel.get()?.tabWithStatus ?? [String: InactiveTabStates]() clearAll() for tab in self.allTabs { + // Ecosia: Skip invisible tabs from inactive tab management + guard !tab.isInvisible else { continue } + let status = inactiveTabModel.tabWithStatus[tab.tabUUID] if status == nil { activeTabs.append(tab) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabCell.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabCell.swift index 895f04e8d3f83..d3a3da7d2f1b5 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabCell.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabCell.swift @@ -273,7 +273,9 @@ class LegacyTabCell: UICollectionViewCell, left: LegacyTabCell.borderWidth, bottom: LegacyTabCell.borderWidth, right: LegacyTabCell.borderWidth) - layer.borderColor = (isPrivate ? theme.colors.borderAccentPrivate : theme.colors.borderAccent).cgColor + // Ecosia: Update border color + // layer.borderColor = (isPrivate ? theme.colors.borderAccentPrivate : theme.colors.borderAccent).cgColor + layer.borderColor = theme.colors.ecosia.buttonBackgroundPrimary.cgColor layer.borderWidth = LegacyTabCell.borderWidth layer.cornerRadius = LegacyGridTabViewController.UX.cornerRadius + LegacyTabCell.borderWidth } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift index a158c8b39b2de..27a458cf9efe9 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift @@ -152,17 +152,25 @@ class LegacyTabTrayViewController: UIViewController, Themeable, TabTrayControlle }() private lazy var segmentedControlIpad: UISegmentedControl = { - let items = TabTrayPanelType.allCases.map { $0.label } + // Ecosia: Update button items + // let items = TabTrayPanelType.allCases.map { $0.label } + let items: [String] = [TabTrayPanelType.tabs, + TabTrayPanelType.privateTabs].map { $0.label } return createSegmentedControl(items: items, action: #selector(segmentIpadChanged), a11yId: AccessibilityIdentifiers.TabTray.navBarSegmentedControl) }() private lazy var segmentedControlIphone: UISegmentedControl = { + /* Ecosia: Update button items + let items = [ + TabTrayPanelType.tabs.image!.overlayWith(image: countLabel), + TabTrayPanelType.privateTabs.image!, + TabTrayPanelType.syncedTabs.image!] + */ let items = [ TabTrayPanelType.tabs.image!.overlayWith(image: countLabel), - TabTrayPanelType.privateTabs.image!, - TabTrayPanelType.syncedTabs.image!] + TabTrayPanelType.privateTabs.image!] return createSegmentedControl(items: items, action: #selector(segmentIphoneChanged), a11yId: AccessibilityIdentifiers.TabTray.navBarSegmentedControl) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift index babcfc7453b59..851a2d5fd7f26 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift @@ -28,7 +28,7 @@ class TabManagerMiddleware { } else if let action = action as? TabTrayAction { self.resolveTabTrayActions(action: action, state: state) } else if let action = action as? TabPanelViewAction { - self.resovleTabPanelViewActions(action: action, state: state) + self.resolveTabPanelViewActions(action: action, state: state) } else if let action = action as? MainMenuAction { self.resolveMainMenuActions(with: action, appState: state) } else if let action = action as? HeaderAction { @@ -82,12 +82,14 @@ class TabManagerMiddleware { guard let panelType = action.panelType else { return } changePanel(panelType, uuid: action.windowUUID) + case TabTrayActionType.closePrivateTabsSettingToggled: + preserveTabs(uuid: action.windowUUID) default: break } } - private func resovleTabPanelViewActions(action: TabPanelViewAction, state: AppState) { + private func resolveTabPanelViewActions(action: TabPanelViewAction, state: AppState) { switch action.actionType { case TabPanelViewActionType.tabPanelDidLoad: let isPrivate = action.panelType == .privateTabs @@ -948,4 +950,9 @@ class TabManagerMiddleware { ) } } + + private func preserveTabs(uuid: WindowUUID) { + let tabManager = tabManager(for: uuid) + tabManager.preserveTabs() + } } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/TabTrayAnimationQueue.swift b/firefox-ios/Client/Frontend/Browser/Tabs/TabTrayAnimationQueue.swift deleted file mode 100644 index 879c0adbe3368..0000000000000 --- a/firefox-ios/Client/Frontend/Browser/Tabs/TabTrayAnimationQueue.swift +++ /dev/null @@ -1,42 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import UIKit - -protocol TabTrayAnimationQueue { - func addAnimation(for collectionView: UICollectionView, - animation: @escaping (() -> Void)) -} - -class TabTrayAnimationQueueImplementation: TabTrayAnimationQueue { - private var animations = [() -> Void]() - private var performingChainedOperations = false - - func addAnimation(for collectionView: UICollectionView, - animation: @escaping (() -> Void)) { - animations.append(animation) - performChainedOperations(for: collectionView) - } - - private func performChainedOperations(for collectionView: UICollectionView) { - guard !performingChainedOperations, - let animation = animations.first - else { return } - - performingChainedOperations = true - animations.removeFirst() - /// Fix crash related to bug from `collectionView.performBatchUpdates` when the - /// collectionView is not visible the dataSource section/items differs from the actions to be perform - /// which causes the crash - if collectionView.numberOfSections != 0 { - collectionView.numberOfItems(inSection: 0) - } - collectionView.performBatchUpdates({ - animation() - }, completion: { [weak self] (done) in - self?.performingChainedOperations = false - self?.performChainedOperations(for: collectionView) - }) - } -} diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/EmptyPrivateTabsView.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/EmptyPrivateTabsView.swift index 69fdf70f654f5..9bd675859e02d 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/EmptyPrivateTabsView.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/EmptyPrivateTabsView.swift @@ -18,7 +18,9 @@ class EmptyPrivateTabsView: UIView { static let paddingInBetweenItems: CGFloat = 15 static let verticalPadding: CGFloat = 20 static let horizontalPadding: CGFloat = 24 - static let imageSize = CGSize(width: 90, height: 90) + // Ecosia + // static let imageSize = CGSize(width: 90, height: 90) + static let imageSize = CGSize(width: 120, height: 120) } // MARK: - Properties @@ -32,6 +34,14 @@ class EmptyPrivateTabsView: UIView { private lazy var containerView: UIView = .build { _ in } + // Ecosia: Add StackView to center items without corrupting the containerView as affect the ScrollView and its parent view + private lazy var containerStackView: UIStackView = .build { stackView in + stackView.spacing = UX.paddingInBetweenItems + stackView.axis = .vertical + stackView.alignment = .center + stackView.distribution = .fill + } + private let titleLabel: UILabel = .build { label in label.adjustsFontForContentSizeCategory = true label.font = FXFontStyles.Regular.title2.scaledFont() @@ -44,7 +54,9 @@ class EmptyPrivateTabsView: UIView { label.font = FXFontStyles.Regular.body.scaledFont() label.textAlignment = .center label.numberOfLines = 0 - label.text = .TabTrayPrivateBrowsingDescription + // Ecosia + // label.text = .TabTrayPrivateBrowsingDescription + label.text = .localized(.privateEmpty) } private lazy var learnMoreButton: LinkButton = .build { button in @@ -52,9 +64,12 @@ class EmptyPrivateTabsView: UIView { button.addTarget(self, action: #selector(self.didTapLearnMore), for: .touchUpInside) } + /* Ecosia private let iconImageView: UIImageView = .build { imageView in imageView.image = UIImage.templateImageNamed(StandardImageIdentifiers.Large.privateMode) } + */ + private let iconImageView: UIImageView = .build() // MARK: - Inits @@ -77,8 +92,19 @@ class EmptyPrivateTabsView: UIView { } private func setupLayout() { + /* Ecosia: Remove Learn More button configureLearnMoreButton() + learnMoreButton.addTarget(self, + action: #selector(didTapLearnMore), + for: .touchUpInside) containerView.addSubviews(iconImageView, titleLabel, descriptionLabel, learnMoreButton) + */ + // Ecosia: Move subviews to stackview + // containerView.addSubviews(iconImageView, titleLabel, descriptionLabel) + containerStackView.addArrangedSubview(iconImageView) + containerStackView.addArrangedSubview(titleLabel) + containerStackView.addArrangedSubview(descriptionLabel) + containerView.addSubview(containerStackView) scrollView.addSubview(containerView) addSubview(scrollView) @@ -99,6 +125,16 @@ class EmptyPrivateTabsView: UIView { scrollView.contentLayoutGuide.bottomAnchor.constraint(equalTo: containerView.bottomAnchor), scrollView.contentLayoutGuide.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + // Ecosia: Update costraints + containerStackView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor), + containerStackView.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor, + constant: -UX.horizontalPadding*2), + containerStackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), + containerStackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), + iconImageView.heightAnchor.constraint(equalToConstant: UX.imageSize.height), + iconImageView.widthAnchor.constraint(equalToConstant: UX.imageSize.width), + + /* Ecosia: Remove unneded costraints iconImageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: UX.paddingInBetweenItems), iconImageView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor), @@ -114,21 +150,24 @@ class EmptyPrivateTabsView: UIView { constant: UX.paddingInBetweenItems), descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), descriptionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), - + learnMoreButton.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: UX.paddingInBetweenItems), learnMoreButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), learnMoreButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), learnMoreButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -UX.paddingInBetweenItems), + */ ]) } func applyTheme(_ theme: Theme) { titleLabel.textColor = theme.colors.textPrimary descriptionLabel.textColor = theme.colors.textPrimary - learnMoreButton.applyTheme(theme: theme) + // Ecosia: Remove Learn More button + // learnMoreButton.applyTheme(theme: theme) iconImageView.tintColor = theme.colors.iconDisabled + iconImageView.image = .init(named: "tigerIncognito") } @objc diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabCell.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabCell.swift index f2b7ba4b6034b..83736612a3733 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabCell.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabCell.swift @@ -152,12 +152,19 @@ class TabCell: UICollectionViewCell, ThemeApplicable, ReusableCell { func applyTheme(theme: Theme) { headerView.effect = UIBlurEffect(style: theme.type.tabTitleBlurStyle()) + /* Ecosia: Add custom theme references backgroundHolder.backgroundColor = theme.colors.layer1 closeButton.tintColor = theme.colors.indicatorActive titleText.textColor = theme.colors.textPrimary screenshotView.backgroundColor = theme.colors.layer1 favicon.tintColor = theme.colors.textPrimary smallFaviconView.tintColor = theme.colors.textPrimary + */ + backgroundHolder.backgroundColor = theme.colors.ecosia.backgroundPrimary + closeButton.tintColor = theme.colors.ecosia.textPrimary + titleText.textColor = theme.colors.ecosia.textPrimary + screenshotView.backgroundColor = theme.colors.ecosia.backgroundElevation1 + favicon.tintColor = theme.colors.ecosia.textPrimary } // MARK: - Configuration diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayDiffableDataSource.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayDiffableDataSource.swift index 3b7b805cdecad..a8b72615765c8 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayDiffableDataSource.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayDiffableDataSource.swift @@ -6,8 +6,11 @@ typealias TabDisplayViewSection = TabDisplayDiffableDataSource.TabSection typealias TabDisplayViewItem = TabDisplayDiffableDataSource.TabItem final class TabDisplayDiffableDataSource: UICollectionViewDiffableDataSource { - enum TabSection: Int, CaseIterable { - case inactiveTabs + enum TabSection: Hashable { + // Adding a UUID to the section allows us to trigger a reload on the header by updating the UUID + // (which creates a diff) while updating the snapshot. This avoids calling reloadSections on inactiveTabs + // when there are no inactiveTabs in the section, which triggers a crash on iOS 15. + case inactiveTabs(UUID) case tabs } @@ -19,14 +22,12 @@ final class TabDisplayDiffableDataSource: UICollectionViewDiffableDataSource() - snapshot.appendSections([.inactiveTabs, .tabs]) - - // reloading .inactiveTabs is necessary to animate the caret moving when we show or hide inactive tabs - snapshot.reloadSections([.inactiveTabs]) + let inactiveTabsUUID = UUID() + snapshot.appendSections([.inactiveTabs(inactiveTabsUUID), .tabs]) if state.isInactiveTabsExpanded { let inactiveTabs = state.inactiveTabs.map { TabDisplayViewItem.inactiveTab($0) } - snapshot.appendItems(inactiveTabs, toSection: .inactiveTabs) + snapshot.appendItems(inactiveTabs, toSection: .inactiveTabs(inactiveTabsUUID)) } let tabs = state.tabs.map { TabDisplayViewItem.tab($0) } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift index 078acb822ad94..5d8c2fcd000c7 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayView.swift @@ -23,7 +23,6 @@ class TabDisplayView: UIView, private var inactiveTabsSectionManager: InactiveTabsSectionManager private var tabsSectionManager: TabsSectionManager private let windowUUID: WindowUUID - private let animationQueue: TabTrayAnimationQueue var theme: Theme? private var dataSource: TabDisplayDiffableDataSource? @@ -78,14 +77,12 @@ class TabDisplayView: UIView, public init(panelType: TabTrayPanelType, state: TabsPanelState, - windowUUID: WindowUUID, - animationQueue: TabTrayAnimationQueue = TabTrayAnimationQueueImplementation()) { + windowUUID: WindowUUID) { self.panelType = panelType self.tabsState = state self.inactiveTabsSectionManager = InactiveTabsSectionManager() self.tabsSectionManager = TabsSectionManager() self.windowUUID = windowUUID - self.animationQueue = animationQueue super.init(frame: .zero) self.inactiveTabsSectionManager.delegate = self setupLayout() @@ -181,11 +178,9 @@ class TabDisplayView: UIView, dataSource?.supplementaryViewProvider = { [weak self] (collectionView, kind, indexPath) -> UICollectionReusableView? in // swiftlint:enable line_length let reusableView = UICollectionReusableView() - let section = self?.getTabDisplay(for: indexPath.section) + let section = self?.getSection(for: indexPath.section) - guard let self, section == .inactiveTabs else { - return nil - } + guard let self, section != .tabs else { return nil } if kind == UICollectionView.elementKindSectionHeader, let headerView = collectionView.dequeueReusableSupplementaryView( @@ -251,7 +246,7 @@ class TabDisplayView: UIView, return self.tabsSectionManager.layoutSection(layoutEnvironment) } - let section = TabDisplayViewSection(rawValue: sectionIndex) + let section = getSection(for: sectionIndex) switch section { case .inactiveTabs: return self.inactiveTabsSectionManager.layoutSection( @@ -259,8 +254,6 @@ class TabDisplayView: UIView, isExpanded: tabsState.isInactiveTabsExpanded) case .tabs: return self.tabsSectionManager.layoutSection(layoutEnvironment) - case .none: - return self.tabsSectionManager.layoutSection(layoutEnvironment) } } return layout @@ -271,10 +264,14 @@ class TabDisplayView: UIView, collectionView.backgroundColor = theme.colors.layer3 } - private func getTabDisplay(for section: Int) -> TabDisplayViewSection { - guard !shouldHideInactiveTabs else { return .tabs } + private func getSection(for sectionIndex: Int) -> TabDisplayViewSection { + guard + let snapshot = dataSource?.snapshot(), + sectionIndex >= 0, + sectionIndex < snapshot.sectionIdentifiers.count + else { return TabDisplayViewSection.tabs } - return TabDisplayViewSection(rawValue: section) ?? .tabs + return snapshot.sectionIdentifiers[sectionIndex] } func deleteInactiveTab(for index: Int) { @@ -310,7 +307,7 @@ class TabDisplayView: UIView, func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { - guard getTabDisplay(for: indexPath.section) == .tabs + guard getSection(for: indexPath.section) == .tabs else { return nil } let tabVC = TabPeekViewController(tab: tabsState.tabs[indexPath.row], windowUUID: windowUUID) @@ -329,14 +326,11 @@ class TabDisplayView: UIView, // MARK: - TabCellDelegate func tabCellDidClose(for tabUUID: TabUUID) { - animationQueue.addAnimation(for: collectionView) { [weak self] in - guard let self else { return } - let action = TabPanelViewAction(panelType: panelType, - tabUUID: tabUUID, - windowUUID: windowUUID, - actionType: TabPanelViewActionType.closeTab) - store.dispatch(action) - } + let action = TabPanelViewAction(panelType: panelType, + tabUUID: tabUUID, + windowUUID: windowUUID, + actionType: TabPanelViewActionType.closeTab) + store.dispatch(action) } // MARK: - SwipeAnimatorDelegate @@ -364,7 +358,7 @@ extension TabDisplayView: UICollectionViewDragDelegate, UICollectionViewDropDele func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { - guard getTabDisplay(for: indexPath.section) == .tabs else { return [] } + guard getSection(for: indexPath.section) == .tabs else { return [] } let itemProvider = NSItemProvider() let dragItem = UIDragItem(itemProvider: itemProvider) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift index 41d3f74f2ccff..4d0cdf2498cad 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift @@ -107,11 +107,21 @@ class TabTrayViewController: UIViewController, }() var segmentControlItems: [Any] { + /* Ecosia: Update button items let iPhoneItems = [ TabTrayPanelType.tabs.image!.overlayWith(image: countLabel), TabTrayPanelType.privateTabs.image!, TabTrayPanelType.syncedTabs.image!] + let iPhoneItems = [ + TabTrayPanelType.tabs.image!.overlayWith(image: countLabel), + TabTrayPanelType.privateTabs.image!] return isRegularLayout ? TabTrayPanelType.allCases.map { $0.label } : iPhoneItems + */ + let iPhoneItems = [ + TabTrayPanelType.tabs.image!.overlayWith(image: countLabel), + TabTrayPanelType.privateTabs.image!] + let regularLayoutItems: [TabTrayPanelType] = [.tabs, .privateTabs] + return isRegularLayout ? regularLayoutItems.map { $0.label } : iPhoneItems } private lazy var deleteButton: UIBarButtonItem = { @@ -335,6 +345,11 @@ class TabTrayViewController: UIViewController, newTabButton.tintColor = theme.colors.iconPrimary doneButton.tintColor = theme.colors.iconPrimary syncTabButton.tintColor = theme.colors.iconPrimary + + // Ecosia: Add segmented control colors + segmentedControl.backgroundColor = theme.colors.ecosia.segmentedControlBackgroundRest + segmentedControl.selectedSegmentTintColor = theme.colors.ecosia.segmentedControlBackgroundActive + segmentedControl.tintColor = theme.colors.ecosia.buttonContentSecondary } // MARK: Private @@ -470,8 +485,11 @@ class TabTrayViewController: UIViewController, let toast = SimpleToast() toast.showAlertWithText(toastType.title, bottomContainer: view, + /* Ecosia: Remove bottom padding theme: currentTheme(), bottomConstraintPadding: -toolbarHeight) + */ + theme: currentTheme()) } } diff --git a/firefox-ios/Client/Frontend/Browser/Toast.swift b/firefox-ios/Client/Frontend/Browser/Toast.swift index 91fddff8c42c1..b27a13262d9bd 100644 --- a/firefox-ios/Client/Frontend/Browser/Toast.swift +++ b/firefox-ios/Client/Frontend/Browser/Toast.swift @@ -10,6 +10,7 @@ import Shared class Toast: UIView, ThemeApplicable { struct UX { static let toastHeight: CGFloat = 56 + static let toastOffset: CGFloat = 16 static let toastDismissAfter = DispatchTimeInterval.milliseconds(4500) // 4.5 seconds. static let toastDelayBefore = DispatchTimeInterval.milliseconds(0) // 0 seconds static let toastPrivateModeDelayBefore = DispatchTimeInterval.milliseconds(750) @@ -93,6 +94,8 @@ class Toast: UIView, ThemeApplicable { } func applyTheme(theme: Theme) { - toastView.backgroundColor = theme.colors.actionPrimary + // Ecosia: Update background + // toastView.backgroundColor = theme.colors.actionPrimary + toastView.backgroundColor = .clear } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift index 66c35ed83668f..c33166268dccd 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift @@ -166,6 +166,12 @@ final class AddressToolbarContainer: UIView, windowUUID: windowUUID) shouldDisplayCompact = newModel.shouldDisplayCompact if self.model != newModel { + // in case we are in edit mode but overlay is not active yet we have to activate it + // so that `inOverlayMode` is set to true so we avoid getting stuck in overlay mode + if newModel.isEditing, !inOverlayMode { + enterOverlayMode(nil, pasted: false, search: true) + } + updateProgressBarPosition(toolbarState.toolbarPosition) compactToolbar.configure(state: newModel.addressToolbarState, toolbarDelegate: self, @@ -355,6 +361,8 @@ final class AddressToolbarContainer: UIView, actionType: ToolbarActionType.didPasteSearchTerm ) store.dispatch(action) + + delegate?.openSuggestions(searchTerm: locationText ?? "") } else { let action = ToolbarAction(searchTerm: locationText, windowUUID: windowUUID, diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift index b6327ace6c812..4b9f3041e71fc 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift @@ -16,6 +16,7 @@ class AddressToolbarContainerModel: Equatable { let searchEngineImage: UIImage? let searchEnginesManager: SearchEnginesManager let lockIconImageName: String? + let lockIconNeedsTheming: Bool let safeListedURLImageName: String? let url: URL? let searchTerm: String? @@ -49,6 +50,7 @@ class AddressToolbarContainerModel: Equatable { urlTextFieldA11yId: AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField, searchEngineImage: searchEngineImage, lockIconImageName: lockIconImageName, + lockIconNeedsTheming: lockIconNeedsTheming, safeListedURLImageName: safeListedURLImageName, url: url, droppableUrl: droppableUrl, @@ -96,6 +98,7 @@ class AddressToolbarContainerModel: Equatable { self.searchEngineImage = profile.searchEnginesManager.defaultEngine?.image self.searchEnginesManager = profile.searchEnginesManager self.lockIconImageName = state.addressToolbar.lockIconImageName + self.lockIconNeedsTheming = state.addressToolbar.lockIconNeedsTheming self.safeListedURLImageName = state.addressToolbar.safeListedURLImageName self.url = state.addressToolbar.url self.searchTerm = state.addressToolbar.searchTerm diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift index bf8ce629fb075..3c43626c70724 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift @@ -15,6 +15,7 @@ struct AddressBarState: StateType, Equatable { var url: URL? var searchTerm: String? var lockIconImageName: String? + var lockIconNeedsTheming: Bool var safeListedURLImageName: String? var isEditing: Bool var isScrollingDuringEdit: Bool @@ -82,7 +83,8 @@ struct AddressBarState: StateType, Equatable { pageActions: [], browserActions: [], borderPosition: nil, - url: nil) + url: nil, + lockIconNeedsTheming: true) } init(windowUUID: WindowUUID, @@ -93,6 +95,7 @@ struct AddressBarState: StateType, Equatable { url: URL?, searchTerm: String? = nil, lockIconImageName: String? = nil, + lockIconNeedsTheming: Bool, safeListedURLImageName: String? = nil, isEditing: Bool = false, isScrollingDuringEdit: Bool = false, @@ -109,6 +112,7 @@ struct AddressBarState: StateType, Equatable { self.url = url self.searchTerm = searchTerm self.lockIconImageName = lockIconImageName + self.lockIconNeedsTheming = lockIconNeedsTheming self.safeListedURLImageName = safeListedURLImageName self.isEditing = isEditing self.isScrollingDuringEdit = isScrollingDuringEdit @@ -124,449 +128,549 @@ struct AddressBarState: StateType, Equatable { switch action.actionType { case ToolbarActionType.didLoadToolbars: - guard let borderPosition = (action as? ToolbarAction)?.addressBorderPosition else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: [ToolbarActionState](), - pageActions: [qrCodeScanAction], - browserActions: [tabsAction(), menuAction()], - borderPosition: borderPosition, - url: nil - ) + return handleDidLoadToolbarsAction(state: state, action: action) case ToolbarActionType.numberOfTabsChanged: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: state.pageActions, - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: toolbarAction.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleNumberOfTabsChangedAction(state: state, action: action) case ToolbarActionType.readerModeStateChanged: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing, - showQRPageAction: state.showQRPageAction), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: toolbarAction.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleReaderModeStateChangedAction(state: state, action: action) case ToolbarActionType.websiteLoadingStateDidChange: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: toolbarAction.isLoading ?? state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleWebsiteLoadingStateDidChangeAction(state: state, action: action) case ToolbarActionType.urlDidChange: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: navigationActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing), - pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: toolbarAction.url, - searchTerm: nil, - lockIconImageName: toolbarAction.lockIconImageName ?? state.lockIconImageName, - safeListedURLImageName: toolbarAction.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: toolbarAction.url == nil - ) + return handleUrlDidChangeAction(state: state, action: action) case ToolbarActionType.backForwardButtonStateChanged: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: navigationActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing), - pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: nil, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleBackForwardButtonStateChangedAction(state: state, action: action) case ToolbarActionType.traitCollectionDidChange: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: navigationActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing), - pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: state.url, - searchTerm: nil, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleTraitCollectionDidChangeAction(state: state, action: action) case ToolbarActionType.showMenuWarningBadge: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: state.pageActions, - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleShowMenuWarningBadgeAction(state: state, action: action) case ToolbarActionType.borderPositionChanged, ToolbarActionType.toolbarPositionChanged: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: state.pageActions, - browserActions: state.browserActions, - borderPosition: toolbarAction.addressBorderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handlePositionChangedAction(state: state, action: action) case ToolbarActionType.didPasteSearchTerm: - guard let toolbarAction = action as? ToolbarAction else { return state } - - let isEmptySearch = toolbarAction.searchTerm == nil || toolbarAction.searchTerm?.isEmpty == true - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: navigationActions(action: toolbarAction, addressBarState: state, isEditing: true), - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: true, - showQRPageAction: isEmptySearch), - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: state.url, - searchTerm: toolbarAction.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: true, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: false, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: false, - showQRPageAction: isEmptySearch - ) + return handleDidPasteSearchTermAction(state: state, action: action) case ToolbarActionType.didStartEditingUrl: - guard let toolbarAction = action as? ToolbarAction else { return state } - - let searchTerm = toolbarAction.searchTerm ?? state.searchTerm - let locationText = searchTerm ?? state.url?.absoluteString - let showQRPageAction = locationText == nil || locationText?.isEmpty == true - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: navigationActions(action: toolbarAction, addressBarState: state, isEditing: true), - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: true, - showQRPageAction: showQRPageAction), - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: state.url, - searchTerm: searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: true, - isScrollingDuringEdit: false, - shouldSelectSearchTerm: true, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: false, - showQRPageAction: showQRPageAction - ) + return handleDidStartEditingUrlAction(state: state, action: action) case ToolbarActionType.cancelEdit: - guard let toolbarAction = action as? ToolbarAction else { return state } - - let url = toolbarAction.url ?? state.url - let showQRPageAction = url == nil - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: navigationActions(action: toolbarAction, addressBarState: state), - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: false, - showQRPageAction: showQRPageAction), - browserActions: browserActions(action: toolbarAction, addressBarState: state), - borderPosition: state.borderPosition, - url: url, - searchTerm: nil, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: false, - isScrollingDuringEdit: false, - shouldSelectSearchTerm: true, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: false, - showQRPageAction: showQRPageAction - ) + return handleCancelEditAction(state: state, action: action) case ToolbarActionType.didSetTextInLocationView: - guard let toolbarAction = action as? ToolbarAction else { return state } - - let isEmptySearch = toolbarAction.searchTerm == nil || toolbarAction.searchTerm?.isEmpty == true - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: true, - showQRPageAction: isEmptySearch), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: toolbarAction.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: true, - shouldSelectSearchTerm: false, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: false, - showQRPageAction: isEmptySearch - ) + return handleDidSetTextInLocationViewAction(state: state, action: action) case ToolbarActionType.didScrollDuringEdit: - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: state.pageActions, - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: true, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleDidScrollDuringEditAction(state: state) case ToolbarActionType.clearSearch: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing, - showQRPageAction: true), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: nil, - searchTerm: nil, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: true - ) + return handleClearSearchAction(state: state, action: action) case ToolbarActionType.didDeleteSearchTerm: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing, - showQRPageAction: true), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: true, - showQRPageAction: true - ) + return handleDidDeleteSearchTermAction(state: state, action: action) case ToolbarActionType.didEnterSearchTerm: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: pageActions(action: toolbarAction, - addressBarState: state, - isEditing: state.isEditing, - showQRPageAction: false), - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: true, - showQRPageAction: false - ) + return handleDidEnterSearchTermAction(state: state, action: action) case ToolbarActionType.didStartTyping: - guard let toolbarAction = action as? ToolbarAction else { return state } - - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: state.pageActions, - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: true, - showQRPageAction: state.showQRPageAction - ) + return handleDidStartTypingAction(state: state, action: action) default: - return AddressBarState( - windowUUID: state.windowUUID, - navigationActions: state.navigationActions, - pageActions: state.pageActions, - browserActions: state.browserActions, - borderPosition: state.borderPosition, - url: state.url, - searchTerm: state.searchTerm, - lockIconImageName: state.lockIconImageName, - safeListedURLImageName: state.safeListedURLImageName, - isEditing: state.isEditing, - isScrollingDuringEdit: state.isScrollingDuringEdit, - shouldSelectSearchTerm: state.shouldSelectSearchTerm, - isLoading: state.isLoading, - readerModeState: state.readerModeState, - didStartTyping: state.didStartTyping, - showQRPageAction: state.showQRPageAction - ) + return handleDefaultAction(state: state) } } + private static func handleDidLoadToolbarsAction(state: Self, action: Action) -> Self { + guard let borderPosition = (action as? ToolbarAction)?.addressBorderPosition else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: [ToolbarActionState](), + pageActions: [qrCodeScanAction], + browserActions: [tabsAction(), menuAction()], + borderPosition: borderPosition, + url: nil, + lockIconNeedsTheming: true + ) + } + + private static func handleNumberOfTabsChangedAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: toolbarAction.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleReaderModeStateChangedAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + let lockIconImageName = toolbarAction.readerModeState == .active ? nil : state.lockIconImageName + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing, + showQRPageAction: state.showQRPageAction), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: toolbarAction.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleWebsiteLoadingStateDidChangeAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: toolbarAction.isLoading ?? state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleUrlDidChangeAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: navigationActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing), + pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: toolbarAction.url, + searchTerm: nil, + lockIconImageName: toolbarAction.lockIconImageName ?? state.lockIconImageName, + lockIconNeedsTheming: toolbarAction.lockIconNeedsTheming ?? state.lockIconNeedsTheming, + safeListedURLImageName: toolbarAction.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: toolbarAction.url == nil + ) + } + + private static func handleBackForwardButtonStateChangedAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: navigationActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing), + pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: nil, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleTraitCollectionDidChangeAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: navigationActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing), + pageActions: pageActions(action: toolbarAction, addressBarState: state, isEditing: state.isEditing), + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: state.url, + searchTerm: nil, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleShowMenuWarningBadgeAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handlePositionChangedAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: state.browserActions, + borderPosition: toolbarAction.addressBorderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleDidPasteSearchTermAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + let isEmptySearch = toolbarAction.searchTerm == nil || toolbarAction.searchTerm?.isEmpty == true + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: navigationActions(action: toolbarAction, addressBarState: state, isEditing: true), + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: true, + showQRPageAction: isEmptySearch), + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: state.url, + searchTerm: toolbarAction.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: true, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: false, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: false, + showQRPageAction: isEmptySearch + ) + } + + private static func handleDidStartEditingUrlAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + let searchTerm = toolbarAction.searchTerm ?? state.searchTerm + let locationText = searchTerm ?? state.url?.absoluteString + let showQRPageAction = locationText == nil || locationText?.isEmpty == true + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: navigationActions(action: toolbarAction, addressBarState: state, isEditing: true), + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: true, + showQRPageAction: showQRPageAction), + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: state.url, + searchTerm: searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: true, + isScrollingDuringEdit: false, + shouldSelectSearchTerm: true, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: false, + showQRPageAction: showQRPageAction + ) + } + + private static func handleCancelEditAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + let url = toolbarAction.url ?? state.url + let showQRPageAction = url == nil + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: navigationActions(action: toolbarAction, addressBarState: state), + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: false, + showQRPageAction: showQRPageAction), + browserActions: browserActions(action: toolbarAction, addressBarState: state), + borderPosition: state.borderPosition, + url: url, + searchTerm: nil, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: false, + isScrollingDuringEdit: false, + shouldSelectSearchTerm: true, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: false, + showQRPageAction: showQRPageAction + ) + } + + private static func handleDidSetTextInLocationViewAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + let isEmptySearch = toolbarAction.searchTerm == nil || toolbarAction.searchTerm?.isEmpty == true + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: true, + showQRPageAction: isEmptySearch), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: toolbarAction.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: true, + shouldSelectSearchTerm: false, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: false, + showQRPageAction: isEmptySearch + ) + } + + private static func handleDidScrollDuringEditAction(state: Self) -> Self { + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: true, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleClearSearchAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing, + showQRPageAction: true), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: nil, + searchTerm: nil, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: true + ) + } + + private static func handleDidDeleteSearchTermAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing, + showQRPageAction: true), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: true, + showQRPageAction: true + ) + } + + private static func handleDidEnterSearchTermAction(state: Self, action: Action) -> Self { + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: pageActions(action: toolbarAction, + addressBarState: state, + isEditing: state.isEditing, + showQRPageAction: false), + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: true, + showQRPageAction: false + ) + } + + private static func handleDidStartTypingAction(state: Self, action: Action) -> Self { + guard action is ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: true, + showQRPageAction: state.showQRPageAction + ) + } + + private static func handleDefaultAction(state: Self) -> Self { + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + lockIconNeedsTheming: state.lockIconNeedsTheming, + safeListedURLImageName: state.safeListedURLImageName, + isEditing: state.isEditing, + isScrollingDuringEdit: state.isScrollingDuringEdit, + shouldSelectSearchTerm: state.shouldSelectSearchTerm, + isLoading: state.isLoading, + readerModeState: state.readerModeState, + didStartTyping: state.didStartTyping, + showQRPageAction: state.showQRPageAction + ) + } + // MARK: - Address Toolbar Actions private static func navigationActions( action: ToolbarAction, @@ -663,7 +767,11 @@ struct AddressBarState: StateType, Equatable { window: action.windowUUID) else { return actions } - if !(action.isShowingTopTabs ?? toolbarState.isShowingTopTabs) { + let isURLDidChangeAction = action.actionType as? ToolbarActionType == .urlDidChange + let isShowingTopTabs = action.isShowingTopTabs ?? toolbarState.isShowingTopTabs + let isHomepage = (isURLDidChangeAction ? action.url : toolbarState.addressToolbar.url) == nil + + if !isShowingTopTabs, !isHomepage { actions.append(newTabAction) } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift index b066add358bc5..9be2d756ff226 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift @@ -21,6 +21,7 @@ final class ToolbarAction: Action { let addressBorderPosition: AddressToolbarBorderPosition? let displayNavBorder: Bool? let lockIconImageName: String? + let lockIconNeedsTheming: Bool? let safeListedURLImageName: String? let isLoading: Bool? let isNewTabFeatureEnabled: Bool? @@ -40,6 +41,7 @@ final class ToolbarAction: Action { addressBorderPosition: AddressToolbarBorderPosition = .none, displayNavBorder: Bool? = nil, lockIconImageName: String? = nil, + lockIconNeedsTheming: Bool? = nil, safeListedURLImageName: String? = nil, isLoading: Bool? = nil, isNewTabFeatureEnabled: Bool? = nil, @@ -60,6 +62,7 @@ final class ToolbarAction: Action { self.addressBorderPosition = addressBorderPosition self.displayNavBorder = displayNavBorder self.lockIconImageName = lockIconImageName + self.lockIconNeedsTheming = lockIconNeedsTheming self.safeListedURLImageName = safeListedURLImageName self.isLoading = isLoading self.isNewTabFeatureEnabled = isNewTabFeatureEnabled diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift index c8f4ab215afcf..c1db35a69cbf5 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift @@ -371,8 +371,8 @@ final class ToolbarMiddleware: FeatureFlaggable { // MARK: - Helper private func cancelEditMode(windowUUID: WindowUUID) { var url = tabManager(for: windowUUID).selectedTab?.url - if let urlIsWebpage = url?.isWebPage() { - url = urlIsWebpage ? url : nil + if let currentURL = url { + url = (currentURL.isWebPage() && !currentURL.isReaderModeURL) ? url : nil } let action = ToolbarAction(url: url, windowUUID: windowUUID, actionType: ToolbarActionType.cancelEdit) store.dispatch(action) diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/ToolbarTelemetry.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/ToolbarTelemetry.swift index 847a3b3151983..af9d671a5cee3 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/ToolbarTelemetry.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/ToolbarTelemetry.swift @@ -3,104 +3,144 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ struct ToolbarTelemetry { // Tap func qrCodeButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.QrScanButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.qrScanButtonTapped.record(isPrivateExtra) + */ } func clearSearchButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.ClearSearchButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.clearSearchButtonTapped.record(isPrivateExtra) + */ } func shareButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.ShareButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.shareButtonTapped.record(isPrivateExtra) + */ } func refreshButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.RefreshButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.refreshButtonTapped.record(isPrivateExtra) + */ } func readerModeButtonTapped(isPrivate: Bool, isEnabled: Bool) { + /* Ecosia: Remove Glean let readerModeExtra = GleanMetrics.Toolbar.ReaderModeButtonTappedExtra(enabled: isPrivate, isPrivate: isEnabled) GleanMetrics.Toolbar.readerModeButtonTapped.record(readerModeExtra) + */ } func siteInfoButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.SiteInfoButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.siteInfoButtonTapped.record(isPrivateExtra) + */ } func backButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.BackButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.backButtonTapped.record(isPrivateExtra) + */ } func forwardButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.ForwardButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.forwardButtonTapped.record(isPrivateExtra) + */ } func homeButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.HomeButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.homeButtonTapped.record(isPrivateExtra) + */ } func oneTapNewTabButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.OneTapNewTabButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.oneTapNewTabButtonTapped.record(isPrivateExtra) + */ } func searchButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.SearchButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.searchButtonTapped.record(isPrivateExtra) + */ } func tabTrayButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.TabTrayButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.tabTrayButtonTapped.record(isPrivateExtra) + */ } func menuButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.AppMenuButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.appMenuButtonTapped.record(isPrivateExtra) + */ } func dataClearanceButtonTapped(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.DataClearanceButtonTappedExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.dataClearanceButtonTapped.record(isPrivateExtra) + */ } // Long Press func backButtonLongPressed(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.BackLongPressExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.backLongPress.record(isPrivateExtra) + */ } func forwardButtonLongPressed(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.ForwardLongPressExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.forwardLongPress.record(isPrivateExtra) + */ } func oneTapNewTabButtonLongPressed(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.OneTapNewTabLongPressExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.oneTapNewTabLongPress.record(isPrivateExtra) + */ } func tabTrayButtonLongPressed(isPrivate: Bool) { + /* Ecosia: Remove Glean let isPrivateExtra = GleanMetrics.Toolbar.TabTrayLongPressExtra(isPrivate: isPrivate) GleanMetrics.Toolbar.tabTrayLongPress.record(isPrivateExtra) + */ } // Other func dragInteractionStarted() { + /* Ecosia: Remove Glean GleanMetrics.Awesomebar.dragLocationBar.record() + */ } } diff --git a/firefox-ios/Client/Frontend/Browser/TopTabCell.swift b/firefox-ios/Client/Frontend/Browser/TopTabCell.swift index 3bb799bd185de..76c5b6301c15a 100644 --- a/firefox-ios/Client/Frontend/Browser/TopTabCell.swift +++ b/firefox-ios/Client/Frontend/Browser/TopTabCell.swift @@ -118,6 +118,7 @@ class TopTabCell: UICollectionViewCell, ThemeApplicable, LegacyTabTrayCell, Reus func applySelectedStyle(theme: Theme) { let colors = theme.colors + /* Ecosia: Update theme favicon.tintColor = colors.textPrimary titleText.textColor = colors.textPrimary closeButton.tintColor = colors.textPrimary @@ -127,6 +128,14 @@ class TopTabCell: UICollectionViewCell, ThemeApplicable, LegacyTabTrayCell, Reus cellBackground.backgroundColor = backgroundColor cellBackground.layer.shadowColor = colors.shadowDefault.cgColor cellBackground.isHidden = false + */ + cellBackground.backgroundColor = colors.ecosia.buttonBackgroundPrimary + + let tint = isSelectedTab ? colors.ecosia.textInversePrimary : colors.ecosia.textPrimary + titleText.textColor = tint + closeButton.tintColor = tint + favicon.tintColor = tint + cellBackground.isHidden = !isSelectedTab } func applyUnselectedStyle(theme: Theme) { diff --git a/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift b/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift index 1c515424d9aee..3efcbcd37f8f0 100644 --- a/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift @@ -194,7 +194,11 @@ class TopTabsViewController: UIViewController, Themeable, Notifiable, FeatureFla let currentTheme = themeManager.getCurrentTheme(for: windowUUID) let colors = currentTheme.colors + /* Ecosia: update background view.backgroundColor = colors.layer3 + */ + view.backgroundColor = currentTheme.colors.ecosia.backgroundPrimary + tabsButton.applyTheme(theme: currentTheme) privateModeButton.applyTheme(theme: currentTheme) newTab.tintColor = colors.iconPrimary @@ -295,12 +299,16 @@ class TopTabsViewController: UIViewController, Themeable, Notifiable, FeatureFla privateModeButton.centerYAnchor.constraint(equalTo: view.centerYAnchor), privateModeButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10), - privateModeButton.widthAnchor.constraint(equalTo: view.heightAnchor), - privateModeButton.heightAnchor.constraint(equalTo: view.heightAnchor), + // Ecosia: Update constraint + // privateModeButton.widthAnchor.constraint(equalTo: view.heightAnchor), + // privateModeButton.heightAnchor.constraint(equalTo: view.heightAnchor), + privateModeButton.heightAnchor.constraint(equalTo: view.heightAnchor, constant: -8), topTabFader.topAnchor.constraint(equalTo: view.topAnchor), topTabFader.bottomAnchor.constraint(equalTo: view.bottomAnchor), - topTabFader.leadingAnchor.constraint(equalTo: privateModeButton.trailingAnchor), + // Ecosia: Update constraint + // topTabFader.leadingAnchor.constraint(equalTo: privateModeButton.trailingAnchor), + topTabFader.leadingAnchor.constraint(equalTo: privateModeButton.trailingAnchor, constant: 8), topTabFader.trailingAnchor.constraint(equalTo: newTab.leadingAnchor), collectionView.topAnchor.constraint(equalTo: topTabFader.topAnchor), diff --git a/firefox-ios/Client/Frontend/Components/ContentContainer.swift b/firefox-ios/Client/Frontend/Components/ContentContainer.swift index 56db6c060138d..d43e167f5304b 100644 --- a/firefox-ios/Client/Frontend/Components/ContentContainer.swift +++ b/firefox-ios/Client/Frontend/Components/ContentContainer.swift @@ -37,6 +37,11 @@ class ContentContainer: UIView { return type == .homepage } + // Ecosia: add computed var due to cherry pick https://github.com/mozilla-mobile/firefox-ios/pull/24609 + var hasAnyHomepage: Bool { + return hasLegacyHomepage || hasHomepage || hasPrivateHomepage + } + var hasWebView: Bool { return type == .webview } diff --git a/firefox-ios/Client/Frontend/Components/StatusBarOverlay.swift b/firefox-ios/Client/Frontend/Components/StatusBarOverlay.swift index a944c3b7460b5..a9284494619a5 100644 --- a/firefox-ios/Client/Frontend/Components/StatusBarOverlay.swift +++ b/firefox-ios/Client/Frontend/Components/StatusBarOverlay.swift @@ -71,7 +71,10 @@ class StatusBarOverlay: UIView, // MARK: - ThemeApplicable func applyTheme(theme: Theme) { + /* Ecosia: update background savedBackgroundColor = hasTopTabs ? theme.colors.layer3 : theme.colors.layer1 + */ + savedBackgroundColor = theme.colors.ecosia.backgroundPrimary backgroundColor = savedBackgroundColor?.withAlphaComponent(scrollOffset) } diff --git a/firefox-ios/Client/Frontend/ContextualHint/ContextualHintEligibilityUtility.swift b/firefox-ios/Client/Frontend/ContextualHint/ContextualHintEligibilityUtility.swift index 09b1009eae668..8734edf58955c 100644 --- a/firefox-ios/Client/Frontend/ContextualHint/ContextualHintEligibilityUtility.swift +++ b/firefox-ios/Client/Frontend/ContextualHint/ContextualHintEligibilityUtility.swift @@ -45,7 +45,7 @@ struct ContextualHintEligibilityUtility: ContextualHintEligibilityUtilityProtoco case .toolbarLocation: hintTypeShouldBePresented = isSearchBarLocationFeatureEnabled case .mainMenu: - hintTypeShouldBePresented = true + hintTypeShouldBePresented = canMenuCFRBePresented case .inactiveTabs: hintTypeShouldBePresented = true case .shoppingExperience: @@ -65,6 +65,14 @@ struct ContextualHintEligibilityUtility: ContextualHintEligibilityUtilityProtoco return overlayState?.inOverlayMode ?? false } + /// Determine if the CFR for Menu is presentable. + /// + /// It's presentable on these conditions: + /// - menu-hint flag is enabled + private var canMenuCFRBePresented: Bool { + return featureFlags.isFeatureEnabled(.menuRefactorHint, checking: .buildOnly) ? true : false + } + /// If device is iPhone we present JumpBackIn and SyncTab CFRs only after Toolbar CFR has been /// presented if the feature is enabled. If the Toolbar CFR flag is disabled or the device /// is iPad (toolbar CFR is not presented on iPad) we bypass it diff --git a/firefox-ios/Client/Frontend/ContextualHint/ContextualHintViewController.swift b/firefox-ios/Client/Frontend/ContextualHint/ContextualHintViewController.swift index e57d85b910598..ec7173449c62c 100644 --- a/firefox-ios/Client/Frontend/ContextualHint/ContextualHintViewController.swift +++ b/firefox-ios/Client/Frontend/ContextualHint/ContextualHintViewController.swift @@ -223,3 +223,12 @@ class ContextualHintViewController: UIViewController, self.view.setNeedsLayout() } } + +/* Ecosia: Accessing the function of the private `viewProvider` + via helper function. The scope is to deactivate the hint for new users + */ +extension ContextualHintViewController { + func deactivateHintForNewUsers() { + viewProvider.markContextualHintPresented() + } +} diff --git a/firefox-ios/Client/Frontend/Extensions/DevicePickerViewController.swift b/firefox-ios/Client/Frontend/Extensions/DevicePickerViewController.swift index 7ef44ea002ff2..cb8d573829fc3 100644 --- a/firefox-ios/Client/Frontend/Extensions/DevicePickerViewController.swift +++ b/firefox-ios/Client/Frontend/Extensions/DevicePickerViewController.swift @@ -78,8 +78,10 @@ class DevicePickerViewController: UITableViewController { private var selectedIdentifiers = Set() // Stores Device.id private var notification: Any? private var loadingState = LoadingState.loading - + /* Ecosia: Swap Theme Manager with Ecosia's private let themeManager = DefaultThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + */ + private let themeManager = EcosiaThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) // ShareItem has been added as we are now using this class outside of the ShareTo extension to // provide Share To functionality diff --git a/firefox-ios/Client/Frontend/Home/CustomizeHome/CustomizeHomepageSectionViewModel.swift b/firefox-ios/Client/Frontend/Home/CustomizeHome/CustomizeHomepageSectionViewModel.swift index 80c0810ecee1c..d6fcd52fe1f7c 100644 --- a/firefox-ios/Client/Frontend/Home/CustomizeHome/CustomizeHomepageSectionViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/CustomizeHome/CustomizeHomepageSectionViewModel.swift @@ -19,7 +19,8 @@ class CustomizeHomepageSectionViewModel { // MARK: HomeViewModelProtocol extension CustomizeHomepageSectionViewModel: HomepageViewModelProtocol { var sectionType: HomepageSectionType { - return .customizeHome + // Ecosia: Update `sectionType` + return .ntpCustomization } var headerViewModel: LabelButtonHeaderViewModel { diff --git a/firefox-ios/Client/Frontend/Home/HistoryHighlights/HistoryHighlightsViewModel.swift b/firefox-ios/Client/Frontend/Home/HistoryHighlights/HistoryHighlightsViewModel.swift index 421f98029602d..03f5d5ed24f69 100644 --- a/firefox-ios/Client/Frontend/Home/HistoryHighlights/HistoryHighlightsViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/HistoryHighlights/HistoryHighlightsViewModel.swift @@ -53,6 +53,7 @@ struct HistoryHighlightsModel { } } +/* Ecosia class HistoryHighlightsViewModel { struct UX { static let maxNumberOfItemsPerColumn = 3 @@ -452,3 +453,4 @@ extension HistoryHighlightsViewModel: HistoryHighlightsDelegate { } } } +*/ diff --git a/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift b/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift index 69b8f17d1987b..45477db0f507e 100644 --- a/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift +++ b/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift @@ -64,6 +64,8 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { if sectionType == .topSites, let topSitesActions = getTopSitesActions(site: site, with: sourceView) { actions = topSitesActions + } + /* Ecosia: Remove History Highlights and Pocket } else if sectionType == .pocket, let pocketActions = getPocketActions(site: site, with: sourceView) { actions = pocketActions @@ -74,6 +76,7 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { let jumpBackInActions = getJumpBackInActions(site: site, with: sourceView) { actions = jumpBackInActions } + */ return actions } @@ -91,25 +94,35 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { with sourceView: UIView?, sectionType: HomepageSectionType ) -> [PhotonRowActions]? { + /* Ecosia: Remove History Highlights and Pocket guard sectionType == .historyHighlights, let highlightsActions = getHistoryHighlightsActions(for: highlightItem, with: sourceView) else { return nil } return highlightsActions + */ + return nil } // MARK: - Default actions - func getOpenInNewPrivateTabAction(siteURL: URL, sectionType: HomepageSectionType) -> PhotonRowActions { + /* Ecosia: Receive `Site` in addition to `URL`. Required for tracking. + func getOpenInNewPrivateTabAction(siteURL: URL, sectionType: HomepageSectionType) -> PhotonRowActions { + */ + func getOpenInNewPrivateTabAction(site: Site, siteURL: URL, sectionType: HomepageSectionType) -> PhotonRowActions { return SingleActionViewModel( title: .OpenInNewPrivateTabContextMenuTitle, iconString: StandardImageIdentifiers.Large.privateMode, allowIconScaling: true ) { _ in self.delegate?.homePanelDidRequestToOpenInNewTab(siteURL, isPrivate: true) - sectionType.newPrivateTabActionTelemetry() + // Ecosia: Track open in new tab top site action + self.viewModel.topSiteViewModel.trackTopSiteMenuAction(site: site, action: .openPrivateTab) + // Ecosia: Remove Telemetry section type helper + // sectionType.newPrivateTabActionTelemetry() }.items } + /* Ecosia: Remove History Highlights and Pocket // MARK: - History Highlights private func getHistoryHighlightsActions( @@ -164,15 +177,21 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { return [openInNewTabAction, openInNewPrivateTabAction, bookmarkAction, shareAction] } + */ + /* Ecosia: Receive `Site` in addition to `URL`. Required for tracking. private func getOpenInNewTabAction(siteURL: URL, sectionType: HomepageSectionType) -> PhotonRowActions { + */ + private func getOpenInNewTabAction(site: Site, siteURL: URL, sectionType: HomepageSectionType) -> PhotonRowActions { return SingleActionViewModel( title: .OpenInNewTabContextMenuTitle, iconString: StandardImageIdentifiers.Large.plus, allowIconScaling: true ) { _ in self.delegate?.homePanelDidRequestToOpenInNewTab(siteURL, isPrivate: false) - + // Ecosia: Track open in new tab top site action + self.viewModel.topSiteViewModel.trackTopSiteMenuAction(site: site, action: .openNewTab) + /* Ecosia: Remove History Highlights and Pocket if sectionType == .pocket { let originExtras = TelemetryWrapper.getOriginExtras(isZeroSearch: self.viewModel.isZeroSearch) TelemetryWrapper.recordEvent(category: .action, @@ -180,6 +199,7 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { object: .pocketStory, extras: originExtras) } + */ }.items } @@ -264,20 +284,32 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { let topSiteActions: [PhotonRowActions] if let site = site as? PinnedSite { topSiteActions = [getRemovePinTopSiteAction(site: site), + /* Ecosia: Include `site` required by tracking getOpenInNewTabAction(siteURL: siteURL, sectionType: .topSites), getOpenInNewPrivateTabAction(siteURL: siteURL, sectionType: .topSites), + */ + getOpenInNewTabAction(site: site, siteURL: siteURL, sectionType: .topSites), + getOpenInNewPrivateTabAction(site: site, siteURL: siteURL, sectionType: .topSites), getRemoveTopSiteAction(site: site), getShareAction(site: site, sourceView: sourceView)] } else if site as? SponsoredTile != nil { + /* Ecosia: Include `site` required by tracking topSiteActions = [getOpenInNewTabAction(siteURL: siteURL, sectionType: .topSites), getOpenInNewPrivateTabAction(siteURL: siteURL, sectionType: .topSites), + */ + topSiteActions = [getOpenInNewTabAction(site: site, siteURL: siteURL, sectionType: .topSites), + getOpenInNewPrivateTabAction(site: site, siteURL: siteURL, sectionType: .topSites), getSettingsAction(), getSponsoredContentAction(), getShareAction(site: site, sourceView: sourceView)] } else { topSiteActions = [getPinTopSiteAction(site: site), + /* Ecosia: Include `site` required by tracking getOpenInNewTabAction(siteURL: siteURL, sectionType: .topSites), getOpenInNewPrivateTabAction(siteURL: siteURL, sectionType: .topSites), + */ + getOpenInNewTabAction(site: site, siteURL: siteURL, sectionType: .topSites), + getOpenInNewPrivateTabAction(site: site, siteURL: siteURL, sectionType: .topSites), getRemoveTopSiteAction(site: site), getShareAction(site: site, sourceView: sourceView)] } @@ -296,6 +328,8 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { } self.sendTopSiteContextualTelemetry(type: .remove) + // Ecosia: Track remove top site action + self.viewModel.topSiteViewModel.trackTopSiteMenuAction(site: site, action: .remove) }).items } @@ -306,6 +340,8 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { tapHandler: { _ in self.viewModel.topSiteViewModel.pinTopSite(site) self.sendTopSiteContextualTelemetry(type: .pin) + // Ecosia: Track pin top site action + self.viewModel.topSiteViewModel.trackTopSiteMenuAction(site: site, action: .pin) }).items } @@ -317,6 +353,8 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { tapHandler: { _ in self.viewModel.topSiteViewModel.removePinTopSite(site) self.sendTopSiteContextualTelemetry(type: .unpin) + // Ecosia: Track unpin top site action + self.viewModel.topSiteViewModel.trackTopSiteMenuAction(site: site, action: .unpin) }).items } diff --git a/firefox-ios/Client/Frontend/Home/HomepageViewModel.swift b/firefox-ios/Client/Frontend/Home/HomepageViewModel.swift index a2526c2ca2c48..4379a2d6e78a2 100644 --- a/firefox-ios/Client/Frontend/Home/HomepageViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/HomepageViewModel.swift @@ -4,6 +4,7 @@ import Common import Shared +import Ecosia protocol HomepageViewModelDelegate: AnyObject { func reloadView() @@ -15,7 +16,9 @@ protocol HomepageDataModelDelegate: AnyObject { class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { struct UX { - static let spacingBetweenSections: CGFloat = 62 + // Ecosia: Update `spacingBetweenSections` + // static let spacingBetweenSections: CGFloat = 62 + static let spacingBetweenSections: CGFloat = 32 static let standardInset: CGFloat = 16 static let iPadInset: CGFloat = 50 static let iPadTopSiteInset: CGFloat = 25 @@ -67,9 +70,15 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { var isZeroSearch: Bool { didSet { topSiteViewModel.isZeroSearch = isZeroSearch - jumpBackInViewModel.isZeroSearch = isZeroSearch + /* Ecosia: Remove bookmarks section bookmarksViewModel.isZeroSearch = isZeroSearch + */ + /* Ecosia: Remove `jumpBackIn` section reference + jumpBackInViewModel.isZeroSearch = isZeroSearch + */ + /* Ecosia: Remove History Highlights and Pocket pocketViewModel.isZeroSearch = isZeroSearch + */ } } @@ -99,27 +108,57 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { // Child View models private var childViewModels: [HomepageViewModelProtocol] var headerViewModel: HomepageHeaderViewModel - var messageCardViewModel: HomepageMessageCardViewModel + // Ecosia: Remove message Card from HomePage + // var messageCardViewModel: HomepageMessageCardViewModel var topSiteViewModel: TopSitesViewModel + /* Ecosia Remove bookmarks section var bookmarksViewModel: BookmarksViewModel + */ + /* Ecosia: Remove `jumpBackIn` section reference var jumpBackInViewModel: JumpBackInViewModel + */ + /* Ecosia: Remove History Highlights and Pocket var historyHighlightsViewModel: HistoryHighlightsViewModel var pocketViewModel: PocketViewModel + */ + /* Ecosia: Remove `customizeHome` reference var customizeButtonViewModel: CustomizeHomepageSectionViewModel + */ var shouldDisplayHomeTabBanner: Bool { - return messageCardViewModel.shouldDisplayMessageCard + false + // Ecosia: Remove message Card from HomePage + // return messageCardViewModel.shouldDisplayMessageCard } + // Ecosia: Add Ecosia's ViewModels + var multiPurposeEcosiaHeaderViewModel: NTPHeaderViewModel + var libraryViewModel: NTPLibraryCellViewModel + var impactViewModel: NTPImpactCellViewModel + var newsViewModel: NTPNewsCellViewModel + var ntpCustomizationViewModel: NTPCustomizationCellViewModel + var productTourNTPViewModel: NTPFirstSearchCellViewModel + + /* + Ecosia: Represents the container that stores some of the `HomepageSectionType`s. + The earlier a section type appears in the array, the higher its priority. + */ + private let cardsPrioritySectionTypes: [HomepageSectionType] = [] + // MARK: - Initializers init(profile: Profile, isPrivate: Bool, tabManager: TabManager, nimbus: FxNimbus = FxNimbus.shared, + referrals: Referrals = .init(), // Ecosia: Add referrals isZeroSearch: Bool = false, theme: Theme, wallpaperManager: WallpaperManager = WallpaperManager(), - logger: Logger = DefaultLogger.shared) { + logger: Logger = DefaultLogger.shared, + // Ecosia: Add EcosiaAuth + auth: EcosiaAuth, + // Ecosia: Add delegate for multi-purpose header actions + multiPurposeEcosiaHeaderDelegate: NTPHeaderDelegate? = nil) { self.profile = profile self.isZeroSearch = isZeroSearch self.theme = theme @@ -127,14 +166,27 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { self.windowUUID = tabManager.windowUUID self.headerViewModel = HomepageHeaderViewModel(profile: profile, theme: theme, tabManager: tabManager) + /* Ecosia: Remove message Card from HomePage let messageCardAdaptor = MessageCardDataAdaptorImplementation() self.messageCardViewModel = HomepageMessageCardViewModel(dataAdaptor: messageCardAdaptor, theme: theme) messageCardAdaptor.delegate = messageCardViewModel + */ self.topSiteViewModel = TopSitesViewModel(profile: profile, theme: theme, wallpaperManager: wallpaperManager) + // Ecosia: Add Ecosia's ViewModels + self.multiPurposeEcosiaHeaderViewModel = NTPHeaderViewModel(profile: profile, + theme: theme, + windowUUID: tabManager.windowUUID, + auth: auth, + delegate: multiPurposeEcosiaHeaderDelegate) + self.libraryViewModel = NTPLibraryCellViewModel(theme: theme) + self.impactViewModel = NTPImpactCellViewModel(referrals: referrals, theme: theme) + self.newsViewModel = NTPNewsCellViewModel(theme: theme) + self.ntpCustomizationViewModel = NTPCustomizationCellViewModel(theme: theme) + self.productTourNTPViewModel = NTPFirstSearchCellViewModel(theme: theme, windowUUID: tabManager.windowUUID) self.wallpaperManager = wallpaperManager - + /* Ecosia: Remove `jumpBackIn` section reference let jumpBackInAdaptor = JumpBackInDataAdaptorImplementation(profile: profile, tabManager: tabManager) self.jumpBackInViewModel = JumpBackInViewModel( @@ -144,10 +196,13 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { tabManager: tabManager, adaptor: jumpBackInAdaptor, wallpaperManager: wallpaperManager) - + */ + /* Ecosia: Remove bookmarks section self.bookmarksViewModel = BookmarksViewModel(profile: profile, theme: theme, wallpaperManager: wallpaperManager) + */ + /* Ecosia: Remove History Highlights and Pocket let deletionUtility = HistoryDeletionUtility(with: profile) let historyDataAdaptor = HistoryHighlightsDataAdaptorImplementation( profile: profile, @@ -166,8 +221,12 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { prefs: profile.prefs, wallpaperManager: wallpaperManager) pocketDataAdaptor.delegate = pocketViewModel + */ + // Ecosia: Remove `customizeHome` reference + // self.customizeButtonViewModel = CustomizeHomepageSectionViewModel(theme: theme) - self.customizeButtonViewModel = CustomizeHomepageSectionViewModel(theme: theme) + /* + Ecosia: Replace view models. self.childViewModels = [headerViewModel, messageCardViewModel, topSiteViewModel, @@ -175,21 +234,46 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { bookmarksViewModel, historyHighlightsViewModel, pocketViewModel, - customizeButtonViewModel] + customizeButtonViewModel + ] + */ + // Ecosia: Those models needs to follow strictly the order defined in `enum HomepageSectionType` + self.childViewModels = [multiPurposeEcosiaHeaderViewModel, + headerViewModel, + libraryViewModel, + topSiteViewModel, + impactViewModel, + newsViewModel, + ntpCustomizationViewModel, + productTourNTPViewModel] self.isPrivate = isPrivate self.nimbus = nimbus + // Ecosia: Add Ecosia's ViewModels delegates + newsViewModel.dataModelDelegate = self + productTourNTPViewModel.dataModelDelegate = self topSiteViewModel.delegate = self - historyHighlightsViewModel.delegate = self + /* Ecosia Remove bookmarks section bookmarksViewModel.delegate = self + */ + /* Ecosia: Remove History Highlights and Pocket + historyHighlightsViewModel.delegate = self + */ + /* Ecosia: Remove History Highlights and Pocket pocketViewModel.delegate = self + */ + /* Ecosia: Remove `jumpBackIn` section reference jumpBackInViewModel.delegate = self + */ + /* Ecosia: Remove message Card from HomePage messageCardViewModel.delegate = self + */ + /* Ecosia: Remove `jumpBackIn` section reference Task { await jumpBackInAdaptor.setDelegate(delegate: jumpBackInViewModel) } - + */ updateEnabledSections() } @@ -218,11 +302,26 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { object: .firefoxHomepage, value: trackingValue, extras: nil) - childViewModels.forEach { $0.screenWasShown() } + // Ecosia: Only execute screenWasShown for shown sections + // childViewModels.forEach { $0.screenWasShown() } + shownSections.forEach { section in + if let viewModel = childViewModels.first(where: { $0.sectionType == section }) { + viewModel.screenWasShown() + } + } + + // Ecosia + if NTPTooltip.highlight() == .referralSpotlight { + Analytics.shared.referral(action: .view, label: .promo) + } + + impactViewModel.subscribeToProjections() } func recordViewDisappeared() { viewAppeared = false + // Ecosia: Unsubscribe to projections + impactViewModel.unsubscribeToProjections() } // MARK: - Manage sections @@ -230,9 +329,36 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { func updateEnabledSections() { shownSections.removeAll() + // Ecosia: Check if we should show product tour layout instead + if ProductTourManager.shared.shouldShowProductTourHomepage { + childViewModels.filter({ $0.sectionType.isProductTourSection }).forEach { + if $0.shouldShow { shownSections.append($0.sectionType) } + } + return + } + + /* Ecosia: Handle priority of cards view models + childViewModels.forEach { + if $0.shouldShow { shownSections.append($0.sectionType) } + } + */ + var prioritySectionAdded = false childViewModels.forEach { - if $0.shouldShow { shownSections.append($0.sectionType) } + if $0.shouldShow { + if cardsPrioritySectionTypes.contains($0.sectionType) { + if !prioritySectionAdded { + shownSections.append($0.sectionType) + prioritySectionAdded = true + } + // If a priority section has already been added, skip the rest + } else { + // Non-priority section, add if shouldShow is true + shownSections.append($0.sectionType) + } + } + // If shouldShow is false, skip this viewModel } + logger.log("Homepage amount of sections shown \(shownSections.count)", level: .debug, category: .legacyHomepage) @@ -247,13 +373,6 @@ class HomepageViewModel: FeatureFlaggable, InjectedThemeUUIDIdentifiable { device: UIDevice.current.userInterfaceIdiom) } } - - // MARK: - Section ViewModel helper - - func getSectionViewModel(shownSection: Int) -> HomepageViewModelProtocol? { - guard let actualSectionNumber = shownSections[safe: shownSection]?.rawValue else { return nil } - return childViewModels[safe: actualSectionNumber] - } } // MARK: - HomepageDataModelDelegate @@ -262,3 +381,11 @@ extension HomepageViewModel: HomepageDataModelDelegate { delegate?.reloadView() } } + +// Ecosia: NTPLayoutHighlightDataSource +extension HomepageViewModel: NTPLayoutHighlightDataSource { + func getSectionViewModel(shownSection: Int) -> HomepageViewModelProtocol? { + guard let actualSectionNumber = shownSections[safe: shownSection]?.rawValue else { return nil } + return childViewModels[safe: actualSectionNumber] + } +} diff --git a/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInSectionLayout.swift b/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInSectionLayout.swift index 22f55895e57c5..312937f5b0049 100644 --- a/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInSectionLayout.swift +++ b/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInSectionLayout.swift @@ -58,7 +58,9 @@ enum JumpBackInSectionLayout: Equatable { ) -> JumpBackInDisplayGroupCount { return JumpBackInDisplayGroupCount( tabsCount: maxJumpBackInItemsToDisplay(device: device), - syncedTabCount: hasAccount ? JumpBackInViewModel.UX.maxDisplayedSyncedTabs : 0 + // Ecosia: Remove `jumpBackIn` section reference + // syncedTabCount: hasAccount ? JumpBackInViewModel.UX.maxDisplayedSyncedTabs : 0 + syncedTabCount: 0 ) } diff --git a/firefox-ios/Client/Frontend/Home/LegacyHomepageViewController.swift b/firefox-ios/Client/Frontend/Home/LegacyHomepageViewController.swift index 3f87ff46e20a3..dcc8e1543b6ff 100644 --- a/firefox-ios/Client/Frontend/Home/LegacyHomepageViewController.swift +++ b/firefox-ios/Client/Frontend/Home/LegacyHomepageViewController.swift @@ -8,6 +8,7 @@ import Shared import Storage import Redux import UIKit +import Ecosia import enum MozillaAppServices.VisitType @@ -34,7 +35,9 @@ class LegacyHomepageViewController: weak var statusBarScrollDelegate: StatusBarScrollDelegate? - private var viewModel: HomepageViewModel + // Ecosia: Expose `viewModel` in extensions + // private var viewModel: HomepageViewModel + var viewModel: HomepageViewModel private var contextMenuHelper: HomepageContextMenuHelper private var tabManager: TabManager private var overlayManager: OverlayModeManager @@ -42,7 +45,9 @@ class LegacyHomepageViewController: private lazy var wallpaperView: WallpaperBackgroundView = .build { _ in } private var jumpBackInContextualHintViewController: ContextualHintViewController private var syncTabContextualHintViewController: ContextualHintViewController - private var collectionView: UICollectionView! = nil + // Ecosia: Expose `collectionView` in extensions + // private var collectionView: UICollectionView! = nil + var collectionView: UICollectionView! = nil private var lastContentOffsetY: CGFloat = 0 private var logger: Logger var windowUUID: WindowUUID { return tabManager.windowUUID } @@ -63,6 +68,10 @@ class LegacyHomepageViewController: var currentTab: Tab? { return tabManager.selectedTab } + // Ecosia: Add referrals + weak var referrals: Referrals! + // Ecosia: Add HomePageViewControllerDelegate reference + weak var delegate: HomepageViewControllerDelegate? // MARK: - Initializers init(profile: Profile, @@ -73,7 +82,13 @@ class LegacyHomepageViewController: userDefaults: UserDefaultsInterface = UserDefaults.standard, themeManager: ThemeManager = AppContainer.shared.resolve(), notificationCenter: NotificationProtocol = NotificationCenter.default, - logger: Logger = DefaultLogger.shared + logger: Logger = DefaultLogger.shared, + // Ecosia: Add Referrals + referrals: Referrals, + // Ecosia: Add HomePageViewControllerDelegate + delegate: HomepageViewControllerDelegate?, + // Ecosia: Add EcosiaAuth + auth: EcosiaAuth ) { self.overlayManager = overlayManager self.tabManager = tabManager @@ -82,7 +97,9 @@ class LegacyHomepageViewController: self.viewModel = HomepageViewModel(profile: profile, isPrivate: isPrivate, tabManager: tabManager, - theme: themeManager.getCurrentTheme(for: tabManager.windowUUID)) + referrals: referrals, // Ecosia: Add referrals + theme: themeManager.getCurrentTheme(for: tabManager.windowUUID), + auth: auth) // Ecosia: Add EcosiaAuth let jumpBackInContextualViewProvider = ContextualHintViewProvider(forHintType: .jumpBackIn, with: viewModel.profile) @@ -100,7 +117,15 @@ class LegacyHomepageViewController: self.themeManager = themeManager self.notificationCenter = notificationCenter self.logger = logger + // Ecosia: Add referrals + self.referrals = referrals + // Ecosia: Add HomePageViewControllerDelegate + self.delegate = delegate super.init(nibName: nil, bundle: nil) + + // Ecosia: Set multi-purpose header delegate after super.init + viewModel.multiPurposeEcosiaHeaderViewModel.delegate = self + updateHeaderToShowPrivateModeToggle() viewModel.isZeroSearch = isZeroSearch @@ -113,7 +138,8 @@ class LegacyHomepageViewController: setupNotifications(forObserver: self, observing: [.HomePanelPrefsChanged, .TabsPrivacyModeChanged, - .WallpaperDidChange]) + .WallpaperDidChange, + UIApplication.didBecomeActiveNotification]) } required init?(coder aDecoder: NSCoder) { @@ -152,11 +178,13 @@ class LegacyHomepageViewController: super.viewWillAppear(animated) viewModel.recordViewAppeared() + // Ecosia: Refresh referral claims + Task { + try? await referrals.refresh() + } + notificationCenter.post(name: .ShowHomepage, withUserInfo: windowUUID.userInfo) notificationCenter.post(name: .HistoryUpdated) - - applyTheme() - reloadView() } override func viewDidAppear(_ animated: Bool) { @@ -247,9 +275,18 @@ class LegacyHomepageViewController: collectionView.register(LegacyLabelButtonHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: LegacyLabelButtonHeaderView.cellIdentifier) + // Ecosia: NTP Toltip + collectionView.register(NTPTooltip.self, + forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, + withReuseIdentifier: NTPTooltip.key) + collectionView.register(NTPImpactDividerFooter.self, + forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, + withReuseIdentifier: NTPImpactDividerFooter.cellIdentifier) + /* Ecosia: Remove PocketFooterView collectionView.register(PocketFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: PocketFooterView.cellIdentifier) + */ collectionView.keyboardDismissMode = .onDrag collectionView.addGestureRecognizer(longPressRecognizer) collectionView.delegate = self @@ -289,10 +326,11 @@ class LegacyHomepageViewController: } func createLayout() -> UICollectionViewLayout { - // swiftlint: disable line_length + /* Ecosia: Update Layout type let layout = UICollectionViewCompositionalLayout { [weak self] (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) + */ + let layout = NTPLayout { [weak self] (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in - // swiftlint: enable line_length guard let self, let viewModel = self.viewModel.getSectionViewModel(shownSection: sectionIndex), viewModel.shouldShow @@ -314,6 +352,7 @@ class LegacyHomepageViewController: size: self.view.frame.size ) } + layout.highlightDataSource = viewModel return layout } @@ -393,7 +432,10 @@ class LegacyHomepageViewController: func applyTheme() { let theme = themeManager.getCurrentTheme(for: windowUUID) viewModel.theme = theme + /* Ecosia: Update NTP background view.backgroundColor = theme.colors.layer1 + */ + view.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative } // called when the homepage is displayed to make sure it's scrolled to top @@ -564,6 +606,7 @@ extension LegacyHomepageViewController: UICollectionViewDelegate, UICollectionVi viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { let reusableView = UICollectionReusableView() + /* Ecosia: Adjust HomePageViewController's CollectionView if kind == UICollectionView.elementKindSectionHeader { guard let headerView = collectionView.dequeueReusableSupplementaryView( ofKind: kind, @@ -578,7 +621,8 @@ extension LegacyHomepageViewController: UICollectionViewDelegate, UICollectionVi // swiftlint:enable line_length headerView.configure(viewModel: headerViewModel, theme: themeManager.getCurrentTheme(for: windowUUID)) - + + /* Ecosia: Remove `jumpBackIn` section reference // Jump back in header specific setup if sectionViewModel.sectionType == .jumpBackIn { self.viewModel.jumpBackInViewModel.sendImpressionTelemetry() @@ -588,6 +632,7 @@ extension LegacyHomepageViewController: UICollectionViewDelegate, UICollectionVi self?.prepareJumpBackInContextualHint(onView: headerView) } } + */ return headerView } @@ -609,6 +654,41 @@ extension LegacyHomepageViewController: UICollectionViewDelegate, UICollectionVi return footerView } return reusableView + */ + guard let sectionViewModel = viewModel.getSectionViewModel(shownSection: indexPath.section) + else { return reusableView } + + // Ecosia: tooltip for impact + if sectionViewModel.sectionType == .impact, + let text = NTPTooltip.highlight()?.text, + kind == UICollectionView.elementKindSectionHeader { + let tooltip = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: NTPTooltip.key, for: indexPath) as! NTPTooltip + tooltip.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + tooltip.setText(text) + tooltip.delegate = self + return tooltip + } + + // Ecosia: footer for impact + if sectionViewModel.sectionType == .impact, kind == UICollectionView.elementKindSectionFooter { + let dividerFooter = collectionView + .dequeueReusableSupplementaryView(ofKind: kind, + withReuseIdentifier: NTPImpactDividerFooter.cellIdentifier, + for: indexPath) as? NTPImpactDividerFooter + dividerFooter?.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + return dividerFooter ?? UICollectionReusableView() + } + + guard let headerView = collectionView.dequeueReusableSupplementaryView( + ofKind: UICollectionView.elementKindSectionHeader, + withReuseIdentifier: LegacyLabelButtonHeaderView.cellIdentifier, + for: indexPath) as? LegacyLabelButtonHeaderView + else { return UICollectionReusableView() } + + // Configure header only if section is shown + let headerViewModel = sectionViewModel.shouldShow ? sectionViewModel.headerViewModel : LabelButtonHeaderViewModel.emptyHeader + headerView.configure(viewModel: headerViewModel, theme: themeManager.getCurrentTheme(for: windowUUID)) + return headerView } func numberOfSections(in collectionView: UICollectionView) -> Int { @@ -655,10 +735,12 @@ private extension LegacyHomepageViewController { // No action currently set if the logo button is tapped. } + /* Ecosia: Remove message Card from HomePage // Message card viewModel.messageCardViewModel.dismissClosure = { [weak self] in self?.reloadView() } + */ // Top sites viewModel.topSiteViewModel.tilePressedHandler = { [weak self] site, isGoogle in @@ -674,6 +756,7 @@ private extension LegacyHomepageViewController { ) } + /* Ecosia: Remove `bookmarks` reference // Bookmarks viewModel.bookmarksViewModel.headerButtonAction = { [weak self] button in self?.openBookmarks(button) @@ -686,7 +769,9 @@ private extension LegacyHomepageViewController { sectionType: .bookmarks ) } + */ + /* Ecosia: Remove `jumpBackIn` reference // Jumpback in viewModel.jumpBackInViewModel.headerButtonAction = { [weak self] button in self?.openTabTray(button) @@ -735,7 +820,9 @@ private extension LegacyHomepageViewController { viewModel.jumpBackInViewModel.prepareContextualHint = { [weak self] syncedTabCell in self?.prepareSyncedTabContextualHint(onCell: syncedTabCell) } + */ + /* Ecosia: Remove History Highlights and Pocket // History highlights viewModel.historyHighlightsViewModel.onTapItem = { [weak self] highlight in guard let url = highlight.siteUrl else { @@ -767,11 +854,19 @@ private extension LegacyHomepageViewController { viewModel.pocketViewModel.onLongPressTileAction = { [weak self] (site, sourceView) in self?.contextMenuHelper.presentContextMenu(for: site, with: sourceView, sectionType: .pocket) } - + */ + /* Ecosia: Remove `customizeHome` reference // Customize home viewModel.customizeButtonViewModel.onTapAction = { [weak self] _ in self?.openCustomizeHomeSettings() } + */ + // Ecosia: Adjust HomePageViewController's CollectionView + viewModel.libraryViewModel.delegate = self + viewModel.impactViewModel.delegate = self + viewModel.newsViewModel.delegate = self + viewModel.productTourNTPViewModel.delegate = self + viewModel.ntpCustomizationViewModel.delegate = self } private func openHistoryHighlightsSearchGroup(item: HighlightItem) { @@ -969,7 +1064,6 @@ extension LegacyHomepageViewController: Notifiable { case .HomePanelPrefsChanged, .WallpaperDidChange: self.reloadView() - default: break } } diff --git a/firefox-ios/Client/Frontend/Home/LogoHeader/HomeLogoHeaderViewModel.swift b/firefox-ios/Client/Frontend/Home/LogoHeader/HomeLogoHeaderViewModel.swift index 096daa92a476f..041af9095b44a 100644 --- a/firefox-ios/Client/Frontend/Home/LogoHeader/HomeLogoHeaderViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/LogoHeader/HomeLogoHeaderViewModel.swift @@ -9,8 +9,12 @@ import Shared // Header view model for the Firefox Normal Homepage class HomepageHeaderViewModel { struct UX { - static let bottomSpacing: CGFloat = 30 + // Ecosia: Update bottom spacing + // static let bottomSpacing: CGFloat = 30 + static let bottomSpacing: CGFloat = 8 static let topSpacing: CGFloat = 16 + // Ecosia: Update top inset for Seed Experiment + static let seedCounterNTPExperimentTopInset: CGFloat = 24 } private let profile: Profile @@ -48,12 +52,24 @@ extension HomepageHeaderViewModel: HomepageViewModelProtocol, FeatureFlaggable { let section = NSCollectionLayoutSection(group: group) + /* Ecosia: Migrate the top edge inset calculation from older app version let leadingInset = HomepageViewModel.UX.leadingInset(traitCollection: traitCollection) section.contentInsets = NSDirectionalEdgeInsets( top: UX.topSpacing, leading: leadingInset, bottom: UX.bottomSpacing, trailing: leadingInset) + */ + let height = max(UIScreen.main.bounds.width, UIScreen.main.bounds.height) + let pos: SearchBarPosition = LegacyFeatureFlagsManager.shared.getCustomState(for: .searchBarPosition) ?? .top + let factor = pos == .bottom ? 0.1 : 0.05 + + let leadingInset = HomepageViewModel.UX.leadingInset(traitCollection: traitCollection) + section.contentInsets = NSDirectionalEdgeInsets( + top: height * factor, + leading: 0, + bottom: UX.bottomSpacing, + trailing: 0) return section } @@ -72,7 +88,9 @@ extension HomepageHeaderViewModel: HomepageViewModelProtocol, FeatureFlaggable { } extension HomepageHeaderViewModel: HomepageSectionHandler { + func configure(_ cell: UICollectionViewCell, at indexPath: IndexPath) -> UICollectionViewCell { + /* Ecosia: Cell as NTPLogoCell guard let headerCell = cell as? LegacyHomepageHeaderCell else { return UICollectionViewCell() } headerCell.configure( with: HomepageHeaderCellViewModel( @@ -85,5 +103,9 @@ extension HomepageHeaderViewModel: HomepageSectionHandler { ) headerCell.applyTheme(theme: theme) return headerCell + */ + guard let logoHeaderCell = cell as? NTPLogoCell else { return UICollectionViewCell() } + logoHeaderCell.applyTheme(theme: theme) + return logoHeaderCell } } diff --git a/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/TopSitesDataAdaptor.swift b/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/TopSitesDataAdaptor.swift index 04c7c52e60073..671273ffe3762 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/TopSitesDataAdaptor.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/DataManagement/TopSitesDataAdaptor.swift @@ -86,6 +86,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { /// We only add Google top site or Contiles if number of pins doesn't exceeds the available number shown of tiles. private func recalculateTopSiteData() { var sites = historySites + /* Ecosia: remove sponsored tiles let availableSpaceCount = getAvailableSpaceCount(maxTopSites: maxTopSites) let shouldAddGoogle = shouldAddGoogle(availableSpaceCount: availableSpaceCount) @@ -100,7 +101,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { if shouldAddGoogle { addGoogleTopSite(sites: &sites) } - + */ sites.removeDuplicates() topSites = sites.map { TopSite(site: $0) } @@ -121,6 +122,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { } private func loadContiles() { + /* Ecosia: deactivate sponsored tiles guard shouldLoadSponsoredTiles else { return } dispatchGroup.enter() @@ -132,6 +134,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { } self?.dispatchGroup.leave() } + */ } private func loadTopSites() { @@ -163,7 +166,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { let defaultNumberOfRows = TopSitesRowCountSettingsController.defaultNumberOfRows return Int(preferredNumberOfRows ?? defaultNumberOfRows) } - + /* Ecosia: remove sponsored tiles func addSponsoredTiles(sites: inout [Site], shouldAddGoogle: Bool, availableSpaceCount: Int) { @@ -176,6 +179,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { defaultSearchEngine: profile.searchEnginesManager.defaultEngine) } } + */ private func countPinnedSites(sites: [Site]) -> Int { var pinnedSites = 0 @@ -185,6 +189,7 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { return pinnedSites } + /* Ecosia: remove sponsored tiles // MARK: - Google Tile private func shouldAddGoogle(availableSpaceCount: Int) -> Bool { @@ -210,10 +215,12 @@ class TopSitesDataAdaptorImplementation: TopSitesDataAdaptor, FeatureFlaggable { let googleAdjustedSpaceCount = availableSpaceCount - GoogleTopSiteManager.Constants.reservedSpaceCount return shouldAddGoogle ? googleAdjustedSpaceCount : availableSpaceCount } + */ } // MARK: Site Array extension private extension Array where Element == Site { + /* Ecosia: remove sponsored tiles /// Add sponsored tiles to the top sites. /// - Parameters: /// - sponsoredTileSpaces: The number of spaces available for sponsored tiles @@ -244,7 +251,7 @@ private extension Array where Element == Site { guard siteAddedCount < maxNumberOfSponsoredTile else { break } } } - + */ // Keeping the order of the sites, we remove duplicate tiles. // Ex: If a sponsored tile is present then it has precedence over the history sites. // Ex: A default site is present but user has recent history site of the same site. @@ -259,7 +266,9 @@ private extension Array where Element == Site { } let siteDomain = site.url.asURL?.shortDomain - let shouldAddSite = !alreadyThere.contains(where: { $0.url.asURL?.shortDomain == siteDomain }) + // Ecosia: don't sort out suggested sites + // let shouldAddSite = !alreadyThere.contains(where: { $0.url.asURL?.shortDomain == siteDomain }) + let shouldAddSite = !alreadyThere.contains(where: { $0.url.asURL?.shortDomain == siteDomain }) || site is SuggestedSite // If shouldAddSite or site domain was not found, then insert the site guard shouldAddSite || siteDomain == nil else { return nil } alreadyThere.insert(site) diff --git a/firefox-ios/Client/Frontend/Home/TopSites/TopSitesDimension.swift b/firefox-ios/Client/Frontend/Home/TopSites/TopSitesDimension.swift index b365f54670f8f..40215c84b408a 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/TopSitesDimension.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/TopSitesDimension.swift @@ -68,6 +68,7 @@ class TopSitesDimensionImplementation: TopSitesDimension { /// needed to computer the tile number /// - Returns: The number of tiles per row the user will see private func getNumberOfTilesPerRow(for interface: TopSitesUIInterface) -> Int { + /* Ecosia: The design decided to have 4 tiles per row by default. let cellWidth = TopSitesViewModel.UX.cellEstimatedSize.width let leadingInset = HomepageViewModel.UX.leadingInset(traitCollection: interface.trait, interfaceIdiom: interface.interfaceIdiom) @@ -79,5 +80,7 @@ class TopSitesDimensionImplementation: TopSitesDimension { availableWidth = availableWidth - cellWidth - TopSitesViewModel.UX.cardSpacing } return numberOfTiles < TopSitesViewModel.UX.minCards ? TopSitesViewModel.UX.minCards : numberOfTiles + */ + 4 } } diff --git a/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift b/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift index 60b242942754f..68b54d0b2ed74 100644 --- a/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/TopSites/TopSitesViewModel.swift @@ -6,11 +6,16 @@ import Common import Foundation import Shared import Storage +import Ecosia class TopSitesViewModel { struct UX { - static let cellEstimatedSize = CGSize(width: 85, height: 94) - static let cardSpacing: CGFloat = 16 + // Ecosia: Update `cellEstimatedSize` + // static let cellEstimatedSize = CGSize(width: 85, height: 94) + static let cellEstimatedSize = CGSize(width: 100, height: 100) + // Ecosia: Update `cardSpacing` + // static let cardSpacing: CGFloat = 16 + static let cardSpacing: CGFloat = 8 static let minCards: Int = 4 } @@ -54,9 +59,31 @@ class TopSitesViewModel { func tilePressed(site: TopSite, position: Int) { topSitePressTracking(homeTopSite: site, position: position) + // Ecosia: Track Top Site click + trackTopSitePressed(topSite: site, position: position) tilePressedHandler?(site.site, site.isGoogleURL) } + // MARK: - Ecosia Top Sites Analytics + + private func ecosiaAnalyticsProperty(forSite site: Site) -> Analytics.Property.TopSite { + if (site as? PinnedSite) != nil { + return .pinned + } else { + return .mostVisited + } + } + + func trackTopSitePressed(topSite: TopSite, position: Int) { + let property = ecosiaAnalyticsProperty(forSite: topSite.site) + Analytics.shared.ntpTopSite(.click, property: property, position: position as NSNumber) + } + + func trackTopSiteMenuAction(site: Site, action: Analytics.Action.TopSite) { + let property = ecosiaAnalyticsProperty(forSite: site) + Analytics.shared.ntpTopSite(action, property: property) + } + // MARK: - Telemetry func sendImpressionTelemetry(_ homeTopSite: TopSite, position: Int) { @@ -131,15 +158,20 @@ extension TopSitesViewModel: HomepageViewModelProtocol, FeatureFlaggable { } var headerViewModel: LabelButtonHeaderViewModel { + /* Ecosia: no header for top sites return LabelButtonHeaderViewModel( title: nil, titleA11yIdentifier: AccessibilityIdentifiers.FirefoxHomepage.SectionTitles.topSites, isButtonHidden: true, textColor: wallpaperManager.currentWallpaper.textColor) + */ + .emptyHeader } var isEnabled: Bool { - return profile.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.TopSiteSection) ?? true + // Ecosia: Check against different value + // return profile.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.TopSiteSection) ?? true + User.shared.showTopSites } func numberOfItemsInSection() -> Int { @@ -165,9 +197,11 @@ extension TopSitesViewModel: HomepageViewModelProtocol, FeatureFlaggable { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: sectionDimension.numberOfTilesPerRow) - group.interItemSpacing = NSCollectionLayoutSpacing.fixed(UX.cardSpacing) + // Ecosia: Remove group spacing + // group.interItemSpacing = NSCollectionLayoutSpacing.fixed(UX.cardSpacing) let section = NSCollectionLayoutSection(group: group) + /* Ecosia: Use sectionType's section insets with updated bottom spacing let leadingInset = HomepageViewModel.UX.leadingInset(traitCollection: traitCollection) section.contentInsets = NSDirectionalEdgeInsets( top: 0, @@ -175,6 +209,9 @@ extension TopSitesViewModel: HomepageViewModelProtocol, FeatureFlaggable { bottom: HomepageViewModel.UX.spacingBetweenSections - TopSiteItemCell.UX.bottomSpace, trailing: leadingInset ) + */ + section.contentInsets = sectionType.sectionInsets(traitCollection, bottomSpacing: 24) + section.interGroupSpacing = UX.cardSpacing return section diff --git a/firefox-ios/Client/Frontend/Home/Wallpapers/v1/NetworkServices/WallpaperNetworkModule.swift b/firefox-ios/Client/Frontend/Home/Wallpapers/v1/NetworkServices/WallpaperNetworkModule.swift index 296bbf64b9a49..31051a8be5dfc 100644 --- a/firefox-ios/Client/Frontend/Home/Wallpapers/v1/NetworkServices/WallpaperNetworkModule.swift +++ b/firefox-ios/Client/Frontend/Home/Wallpapers/v1/NetworkServices/WallpaperNetworkModule.swift @@ -2,39 +2,47 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ +import Common import Foundation import Shared class WallpaperNetworkingModule: WallpaperNetworking { private var urlSession: URLSessionProtocol + private var logger: Logger - init(with urlSession: URLSessionProtocol = URLSession.sharedMPTCP) { + init( + with urlSession: URLSessionProtocol = URLSession.sharedMPTCP, + logger: Logger = DefaultLogger.shared + ) { self.urlSession = urlSession + self.logger = logger } - /// A basic async/await wrapper func data(from url: URL) async throws -> (Data, URLResponse) { - return try await withCheckedThrowingContinuation { continuation in - urlSession.dataTaskWith(url) { data, response, error in - if let error = error { - continuation.resume(throwing: error) - return - } - - guard let response = validatedHTTPResponse(response, statusCode: 200..<300) else { - continuation.resume(throwing: URLError(.badServerResponse)) - return - } - - guard let data = data, - !data.isEmpty - else { - continuation.resume(throwing: WallpaperServiceError.dataUnavailable) - return - } - - continuation.resume(returning: (data, response)) - }.resume() + do { + logger.log( + "Attempting to fetch wallpaper data", + level: .debug, + category: .wallpaper + ) + let (data, response) = try await urlSession.data(from: url) + + guard let response = validatedHTTPResponse( + response, + statusCode: 200..<300 + ) else { throw URLError(.badServerResponse) } + + guard !data.isEmpty else { throw WallpaperServiceError.dataUnavailable } + + logger.log( + "Wallpaper data fetched successfully", + level: .debug, + category: .wallpaper + ) + + return (data, response) + } catch { + throw error } } } diff --git a/firefox-ios/Client/Frontend/InternalSchemeHandler/ErrorPageHelper.swift b/firefox-ios/Client/Frontend/InternalSchemeHandler/ErrorPageHelper.swift index c5e51231e40b8..20012e283440a 100644 --- a/firefox-ios/Client/Frontend/InternalSchemeHandler/ErrorPageHelper.swift +++ b/firefox-ios/Client/Frontend/InternalSchemeHandler/ErrorPageHelper.swift @@ -199,13 +199,24 @@ class ErrorPageHandler: InternalSchemeResponse, FeatureFlaggable { "short_description": errDomain, ] + /* Ecosia: Update button text let tryAgain: String = .ErrorPageTryAgain + */ + let tryAgain: String = noConnectionErrorButtonTitle + // swiftlint:disable line_length var actions = "" // swiftlint:enable line_length + /* Ecosia: Add custom no internet screen if errDomain == kCFErrorDomainCFNetwork as String { + */ + if errDomain == NSURLErrorDomain { + asset = Bundle.main.path(forResource: "EcosiaNetError", ofType: "html") + variables["error_title"] = noConnectionErrorTitle + variables["short_description"] = noConnectionErrorMessage + } else if errDomain == kCFErrorDomainCFNetwork as String { if let code = CFNetworkErrors(rawValue: Int32(errCode)) { errDomain = cfErrorToName(code) } diff --git a/firefox-ios/Client/Frontend/InternalSchemeHandler/InternalSchemeHandler.swift b/firefox-ios/Client/Frontend/InternalSchemeHandler/InternalSchemeHandler.swift index b63316f819f67..08724cf4e0d10 100644 --- a/firefox-ios/Client/Frontend/InternalSchemeHandler/InternalSchemeHandler.swift +++ b/firefox-ios/Client/Frontend/InternalSchemeHandler/InternalSchemeHandler.swift @@ -33,6 +33,9 @@ class InternalSchemeHandler: NSObject, WKURLSchemeHandler { let allowedInternalResources = [ "/errorpage-resource/NetError.css", "/errorpage-resource/CertError.css", + // Ecosia: Allow EcosiaNetError resources + "/errorpage-resource/EcosiaNetError.css", + "/errorpage-resource/EcosiaErrorPlaceholderPath.png", // "/reader-mode/..." ] @@ -54,6 +57,13 @@ class InternalSchemeHandler: NSObject, WKURLSchemeHandler { urlSchemeTask.didReceive(data) urlSchemeTask.didFinish() return true + // Ecosia: Replace placeholder error image + } else if path.hasSuffix("EcosiaErrorPlaceholderPath.png"), + let data = UIImage(named: "noInternet")?.pngData() { + urlSchemeTask.didReceive(URLResponse(url: url, mimeType: nil, expectedContentLength: -1, textEncodingName: nil)) + urlSchemeTask.didReceive(data) + urlSchemeTask.didFinish() + return true } } diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksRefactorFeatureFlagProvider.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksRefactorFeatureFlagProvider.swift index e77716663c257..5884ed627b8bf 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksRefactorFeatureFlagProvider.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksRefactorFeatureFlagProvider.swift @@ -7,7 +7,11 @@ import Foundation protocol BookmarksRefactorFeatureFlagProvider {} extension BookmarksRefactorFeatureFlagProvider { + private var featureFlags: LegacyFeatureFlagsManager { + return LegacyFeatureFlagsManager.shared + } + var isBookmarkRefactorEnabled: Bool { - NimbusManager.shared.featureFlagLayer.checkNimbusConfigFor(.bookmarksRefactor) + return featureFlags.isFeatureEnabled(.bookmarksRefactor, checking: .buildOnly) } } diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift index f0cead646ebc1..c0a08fdb92a62 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/BookmarksPanelViewModel.swift @@ -6,11 +6,18 @@ import Foundation import Common import Storage import Shared +import Ecosia +/* Ecosia: Import all of MozillaAppServices for legacy code usage import class MozillaAppServices.BookmarkFolderData import enum MozillaAppServices.BookmarkRoots + */ +import MozillaAppServices +/* Ecosia: Inherit NSObject so it can extend UIDocumentPickerDelegate class BookmarksPanelViewModel { + */ +class BookmarksPanelViewModel: NSObject { enum BookmarksSection: Int, CaseIterable { case bookmarks } @@ -26,6 +33,11 @@ class BookmarksPanelViewModel { private var bookmarksHandler: BookmarksHandler private var flashLastRowOnNextReload = false private var logger: Logger + // Ecosia: Import Bookmarks Helper + private let bookmarksExchange: BookmarksExchangable + private var documentPickerPresentingViewController: UIViewController? + private var onImportDoneHandler: ((URL?, Error?) -> Void)? + private var onExportDoneHandler: ((Error?) -> Void)? /// By default our root folder is the mobile folder. Desktop folders are shown in the local desktop folders. init(profile: Profile, @@ -36,6 +48,8 @@ class BookmarksPanelViewModel { self.bookmarksHandler = bookmarksHandler self.bookmarkFolderGUID = bookmarkFolderGUID self.logger = logger + // Ecosia: BookmarksExchange + self.bookmarksExchange = BookmarksExchange(profile: profile) } var shouldFlashRow: Bool { @@ -114,9 +128,10 @@ class BookmarksPanelViewModel { self.bookmarkFolder = mobileFolder self.bookmarkNodes = mobileFolder.fxChildren ?? [] + /* Ecosia: remove desktop folder let desktopFolder = LocalDesktopFolder() self.bookmarkNodes.insert(desktopFolder, at: 0) - + */ completion() } } @@ -159,3 +174,121 @@ class BookmarksPanelViewModel { self.bookmarkNodes = [] } } + +// Ecosia: Import Bookmarks Helper +extension BookmarksPanelViewModel { + + func bookmarkExportSelected(in viewController: LegacyBookmarksPanel, onDone: @escaping (Error?) -> Void) { + Task { + self.onExportDoneHandler = onDone + do { + let bookmarks = try await getBookmarksForExport() + try await bookmarksExchange.export(bookmarks: bookmarks, in: viewController, barButtonItem: viewController.moreButton) + await notifyExportDone(nil) + } catch { + await notifyExportDone(error) + } + } + } + + func bookmarkImportSelected(in viewController: UIViewController, onDone: @escaping (URL?, Error?) -> Void) { + self.documentPickerPresentingViewController = viewController + self.onImportDoneHandler = onDone + let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.html"], in: .open) + documentPicker.allowsMultipleSelection = false + // Ecosia: Theming + let themeManager: ThemeManager = AppContainer.shared.resolve() + let theme = themeManager.getCurrentTheme(for: viewController.view.currentWindowUUID) + documentPicker.view.tintColor = theme.colors.ecosia.buttonBackgroundPrimary + documentPicker.delegate = self + viewController.present(documentPicker, animated: true) + } + + // MARK: - Private + private func getBookmarksForExport() async throws -> [Ecosia.BookmarkItem] { + return try await withCheckedThrowingContinuation { [weak self] continuation in + guard let self = self else { + return continuation.resume(returning: []) + } + + profile.places + .getBookmarksTree(rootGUID: BookmarkRoots.MobileFolderGUID, recursive: true) + .uponQueue(.main) { result in + guard let mobileFolder = result.successValue as? BookmarkFolderData else { + self.setErrorCase() + return + } + + self.bookmarkFolder = mobileFolder + let bookmarkNodes = mobileFolder.fxChildren ?? [] + + let items: [Ecosia.BookmarkItem] = bookmarkNodes + .compactMap { $0 as? BookmarkNodeData } + .compactMap { bookmarkNode in + self.exportNode(bookmarkNode) + } + + continuation.resume(returning: items) + } + } + } + + private func exportNode(_ node: BookmarkNodeData) -> Ecosia.BookmarkItem? { + if let folder = node as? BookmarkFolderData { + return .folder(folder.title, folder.children?.compactMap { exportNode($0) } ?? [], .empty) + } else if let bookmark = node as? BookmarkItemData { + return .bookmark(bookmark.title, bookmark.url, .empty) + } + assertionFailure("This should not happen") + return nil + } + + @MainActor + private func notifyExportDone(_ error: Error?) { + onExportDoneHandler?(error) + } +} + +extension BookmarksPanelViewModel: UIDocumentPickerDelegate { + func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { + self.onImportDoneHandler?(nil, nil) + } + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + guard + let firstHtmlUrl = urls.first, + let viewController = documentPickerPresentingViewController + else { return } + handlePickedUrl(firstHtmlUrl, in: viewController) + } + + func handlePickedUrl(_ url: URL, in viewController: UIViewController) { + let scopedResourceAccess = url.startAccessingSecurityScopedResource() + var error: NSError? + NSFileCoordinator().coordinate(readingItemAt: url, error: &error) { url in + Task { + defer { + if scopedResourceAccess { + url.stopAccessingSecurityScopedResource() + } + } + do { + try await bookmarksExchange.import(from: url, in: viewController) + await notifyImportDone(url, nil) + } catch { + await notifyImportDone(url, error) + } + } + } + if let error = error { + Task { + await notifyImportDone(url, error) + } + } + } + + @MainActor + private func notifyImportDone(_ url: URL, _ error: Error?) { + onImportDoneHandler?(url, error) + } +} diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarkDetailPanel.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarkDetailPanel.swift index f2e1120768942..ab65df5a13d99 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarkDetailPanel.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarkDetailPanel.swift @@ -248,6 +248,9 @@ class LegacyBookmarkDetailPanel: SiteTableViewController { deleteBookmarkButton.backgroundColor = currentTheme().colors.layer5 deleteBookmarkButton.setTitle(.Bookmarks.Menu.DeleteBookmark, for: .normal) deleteBookmarkButton.setTitleColor(currentTheme().colors.textCritical, for: .normal) + // Ecosia: Fix edit bookmark navigation items color + navigationItem.rightBarButtonItem?.tintColor = currentTheme().colors.actionPrimary + navigationItem.leftBarButtonItem?.tintColor = currentTheme().colors.actionPrimary } override func reloadData() { diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarksPanel.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarksPanel.swift index d0eb82099c267..37f5250cd6d6f 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarksPanel.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarksPanel.swift @@ -7,6 +7,7 @@ import UIKit import Storage import Shared import SiteImageView +import Ecosia import class MozillaAppServices.BookmarkItemData import class MozillaAppServices.BookmarkSeparatorData @@ -48,11 +49,21 @@ class LegacyBookmarksPanel: SiteTableViewController, private var toolbarButtonItems: [UIBarButtonItem] { switch state { + /* Ecosia: Split mainView and inFolder staes case .bookmarks(state: .mainView), .bookmarks(state: .inFolder): bottomRightButton.title = .BookmarksEdit return [flexibleSpace, bottomRightButton] + */ + case .bookmarks(state: .mainView): + bottomRightButton.title = .BookmarksEdit + return [moreButton, flexibleSpace, bottomRightButton] + case .bookmarks(state: .inFolder): + bottomRightButton.title = .BookmarksEdit + return [flexibleSpace, bottomRightButton] case .bookmarks(state: .inFolderEditMode): bottomRightButton.title = String.AppSettingsDone + // Ecosia: Enable the Done button after closing the panel details + bottomRightButton.isEnabled = true return [bottomLeftButton, flexibleSpace, bottomRightButton] case .bookmarks(state: .itemEditMode): bottomRightButton.title = String.AppSettingsDone @@ -78,6 +89,17 @@ class LegacyBookmarksPanel: SiteTableViewController, return button }() + // Ecosia: Import Bookmarks Helper + // Add `moreButton` + lazy var moreButton: UIBarButtonItem = { + let button = UIBarButtonItem(title: .localized(.bookmarksPanelMore), + style: .plain, + target: self, + action: #selector(showMoreDialog)) + button.accessibilityIdentifier = AccessibilityIdentifiers.LibraryPanels.bottomLeftButton + return button + }() + private lazy var bottomRightButton: UIBarButtonItem = { let button = UIBarButtonItem( title: .BookmarksEdit, @@ -89,6 +111,19 @@ class LegacyBookmarksPanel: SiteTableViewController, return button }() + // Ecosia: add bookmarks empty state + private lazy var emptyHeader = EmptyHeader(icon: "bookmarksEmpty", title: .localized(.noBookmarksYet), subtitle: .localized(.AddYourFavoritePages)) + + // Ecosia: Tooltip + private lazy var bookmarksTooltip: NTPTooltip = { + let tooltip = NTPTooltip() + tooltip.tailPosition = .leading + tooltip.applyTheme(theme: currentTheme()) + tooltip.setText(.localized(.bookmarksToolTipText)) + tooltip.setLinkTitle(.localized(.learnMore), theme: currentTheme()) + return tooltip + }() + // MARK: - Init init(viewModel: BookmarksPanelViewModel, @@ -127,6 +162,17 @@ class LegacyBookmarksPanel: SiteTableViewController, tableView.accessibilityIdentifier = AccessibilityIdentifiers.LibraryPanels.BookmarksPanel.tableView tableView.allowsSelectionDuringEditing = true tableView.dragInteractionEnabled = false + // Ecosia: Update TableView properties + tableView.contentInset.top = 32 + } + + // Ecosia: Tooltip + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if EcosiaInstallType.get() == .upgrade && User.shared.showsBookmarksImportExportTooltip { + showBookmarksTooltip() + } } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -146,6 +192,35 @@ class LegacyBookmarksPanel: SiteTableViewController, if self?.viewModel.shouldFlashRow ?? false { self?.flashRow() } + + // Ecosia: add bookmarks empty state + self?.updateEmptyView() + } + } + + // Ecosia: add bookmarks empty state + private func updateEmptyView() { + switch (viewModel.isRootNode, viewModel.bookmarkNodes.isEmpty) { + case (true, true): // is first level, no bookmarks -> show explainative empty view + // Ecosia: Add `bottomAnchorDelta` util to determine `bottomAnchor` margin + let navigationControllerHeight: CGFloat = (navigationController?.toolbar.bounds.size.height ?? 0) + let topAnchorDelta: CGFloat = UIDevice.current.userInterfaceIdiom == .pad ? -navigationControllerHeight : -navigationControllerHeight*3 + /* Update EmptyBookmarksView initialization + let emptyBookmarksView = EmptyBookmarksView( + initialBottomMargin: -(navigationController?.toolbar.bounds.size.height ?? 0) + ) + */ + let emptyBookmarksView = EmptyBookmarksView(initialBottomMargin: topAnchorDelta) + emptyBookmarksView.delegate = self + emptyBookmarksView.applyTheme(theme: currentTheme()) + tableView.tableHeaderView = nil + tableView.backgroundView = emptyBookmarksView + case (false, true): // is folder which is empty -> show "old" empty view + tableView.tableHeaderView = emptyHeader + tableView.backgroundView = nil + case (_, false): // got bookmarks, don't show any empty view + tableView.tableHeaderView = nil + tableView.backgroundView = nil } } @@ -640,3 +715,136 @@ extension LegacyBookmarksPanel { } } } + +// Ecosia: add bookmarks empty state +extension LegacyBookmarksPanel: EmptyBookmarksViewDelegate { + func emptyBookmarksViewLearnMoreTapped(_ view: EmptyBookmarksView) { + libraryPanelDelegate?.libraryPanel( + didSelectURL: EcosiaEnvironment.current.urlProvider.bookmarksHelp, + visitType: .link + ) + } + + func emptyBookmarksViewImportBookmarksTapped(_ view: EmptyBookmarksView) { + importBookmarksActionHandler() + } +} + +// Ecosia: Tooltip delegate +extension LegacyBookmarksPanel: NTPTooltipDelegate { + func ntpTooltipTapped(_ tooltip: NTPTooltip?) { + hideBookmarksTooltip() + } + + func ntpTooltipCloseTapped(_ tooltip: NTPTooltip?) { + hideBookmarksTooltip() + } + + func ntpTooltipLinkTapped(_ tooltip: NTPTooltip?) { + libraryPanelDelegate?.libraryPanel( + didSelectURL: EcosiaEnvironment.current.urlProvider.bookmarksHelp, + visitType: .link + ) + } +} + +// Ecosia: Import Bookmarks Helper +extension LegacyBookmarksPanel { + + func importBookmarksActionHandler() { + Analytics.shared.bookmarksPerformImportExport(.import) + viewModel.bookmarkImportSelected(in: self) { [weak self] url, error in + self?.moreButton.isEnabled = true + + guard error != nil else { + self?.reloadData() + return + } + + let alert = UIAlertController(title: .localized(.bookmarksImportFailedTitle), message: .localized(.bookmarksImportExportFailedMessage), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: .CancelString, style: .cancel)) + let retryAction = UIAlertAction(title: .localized(.retryMessage), style: .default) { [weak self] _ in + guard let self = self, let url = url else { return } + self.viewModel.handlePickedUrl(url, in: self) + } + alert.addAction(retryAction) + alert.preferredAction = retryAction + self?.present(alert, animated: true) + } + } + + func exportBookmarksActionHandler() { + Analytics.shared.bookmarksPerformImportExport(.export) + viewModel.bookmarkExportSelected(in: self) { [weak self] error in + self?.moreButton.isEnabled = true + + guard error != nil else { + self?.reloadData() + return + } + + let alert = UIAlertController(title: .localized(.bookmarksExportFailedTitle), message: .localized(.bookmarksImportExportFailedMessage), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: .CancelString, style: .cancel)) + let retryAction = UIAlertAction(title: .localized(.retryMessage), style: .default) { [weak self] _ in + self?.exportBookmarksActionHandler() + } + alert.addAction(retryAction) + self?.present(alert, animated: true) + } + } + + @objc private func showMoreDialog() { + hideBookmarksTooltip() + moreButton.isEnabled = false + let importAction = UIAlertAction(title: .localized(.importBookmarks), style: .default, handler: { [weak self] _ in self?.importBookmarksActionHandler() }) + let exportAction = UIAlertAction(title: .localized(.exportBookmarks), style: .default, handler: { [weak self] _ in self?.exportBookmarksActionHandler() }) + exportAction.isEnabled = !viewModel.bookmarkNodes.isEmpty + let cancelAction = UIAlertAction(title: .CancelString, style: .cancel) { [weak self] _ in + self?.moreButton.isEnabled = true + } + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + alert.view.tintColor = currentTheme().colors.ecosia.buttonBackgroundPrimary + alert.popoverPresentationController?.barButtonItem = moreButton + [importAction, exportAction, cancelAction].forEach(alert.addAction) + present(alert, animated: true) + } + + private func showBookmarksTooltip() { + guard bookmarksTooltip.superview == nil else { return } + + bookmarksTooltip.delegate = self + bookmarksTooltip.alpha = 0.0 + view.addSubview(self.bookmarksTooltip) + + var constraints = [ + bookmarksTooltip.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: 0), + bookmarksTooltip.trailingAnchor.constraint(lessThanOrEqualTo: view.layoutMarginsGuide.trailingAnchor, constant: 0), + bookmarksTooltip.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: 0), + ] + + if traitCollection.userInterfaceIdiom == .pad { + constraints.append( + bookmarksTooltip.widthAnchor.constraint(lessThanOrEqualToConstant: view.bounds.width / 2).priority(.defaultHigh) + ) + } + + NSLayoutConstraint.activate(constraints) + + UIView.animate(withDuration: 0.3) { + self.bookmarksTooltip.alpha = 1.0 + } + } + + private func hideBookmarksTooltip() { + User.shared.hideBookmarksImportExportTooltip() + + guard bookmarksTooltip.superview != nil else { return } + bookmarksTooltip.delegate = nil + + UIView.animate(withDuration: 0.3, animations: { + self.bookmarksTooltip.alpha = 0.0 + }) { _ in + self.bookmarksTooltip.removeFromSuperview() + } + } +} diff --git a/firefox-ios/Client/Frontend/Library/Downloads/DownloadsPanel.swift b/firefox-ios/Client/Frontend/Library/Downloads/DownloadsPanel.swift index ef2e1aaf9c060..295413181e9fa 100644 --- a/firefox-ios/Client/Frontend/Library/Downloads/DownloadsPanel.swift +++ b/firefox-ios/Client/Frontend/Library/Downloads/DownloadsPanel.swift @@ -231,9 +231,13 @@ class DownloadsPanel: UIViewController, if emptyStateOverlayView.superview == nil { view.addSubview(emptyStateOverlayView) view.bringSubviewToFront(emptyStateOverlayView) + // Ecosia: Add `topAnchorDelta` util to determine `topAnchor` margin + let topAnchorDelta: CGFloat = UIDevice.current.userInterfaceIdiom == .phone ? -50 : 0 NSLayoutConstraint.activate([ - emptyStateOverlayView.topAnchor.constraint(equalTo: view.topAnchor), + // Ecosia: Update reading list top anchor constant only if iPhone + // emptyStateOverlayView.topAnchor.constraint(equalTo: view.topAnchor), + emptyStateOverlayView.topAnchor.constraint(equalTo: view.topAnchor, constant: topAnchorDelta), emptyStateOverlayView.leadingAnchor.constraint(equalTo: view.leadingAnchor), emptyStateOverlayView.bottomAnchor.constraint(equalTo: view.bottomAnchor), emptyStateOverlayView.trailingAnchor.constraint(equalTo: view.trailingAnchor) @@ -253,7 +257,9 @@ class DownloadsPanel: UIViewController, let logoImageView: UIImageView = .build { imageView in imageView.image = UIImage.templateImageNamed(StandardImageIdentifiers.Large.download)? .withRenderingMode(.alwaysTemplate) - imageView.tintColor = theme.colors.iconSecondary + // Ecosia: Update icon color + // imageView.tintColor = theme.colors.iconSecondary + imageView.tintColor = theme.colors.textPrimary } let welcomeLabel: UILabel = .build { label in label.text = .TabsTray.DownloadsPanel.EmptyStateTitle diff --git a/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift b/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift index 8ee71b4eab3ce..0fbf69b0abc9a 100644 --- a/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift +++ b/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift @@ -75,24 +75,38 @@ class HistoryPanel: UIViewController, return [bottomDeleteButton, flexibleSpace] } - return [bottomDeleteButton, flexibleSpace, bottomSearchButton, flexibleSpace] + // Ecosia: Update button position + // return [bottomDeleteButton, flexibleSpace, bottomSearchButton, flexibleSpace] + return [flexibleSpace, bottomSearchButton, flexibleSpace, bottomDeleteButton] } // UI private lazy var bottomSearchButton: UIBarButtonItem = { + /* Ecosia: Updare bottom search button let button = UIBarButtonItem(image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.search), style: .plain, target: self, action: #selector(bottomSearchButtonAction)) + */ + let button = UIBarButtonItem(image: UIImage.templateImageNamed("searchFilled"), + style: .plain, + target: self, + action: #selector(bottomSearchButtonAction)) button.accessibilityIdentifier = AccessibilityIdentifiers.LibraryPanels.bottomSearchButton return button }() private lazy var bottomDeleteButton: UIBarButtonItem = { + /* Ecosia: Updare clear all button let button = UIBarButtonItem(image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.delete), style: .plain, target: self, action: #selector(bottomDeleteButtonAction)) + */ + let button = UIBarButtonItem(title: .localized(.clearAll), + style: .plain, + target: self, + action: #selector(bottomDeleteButtonAction)) button.accessibilityIdentifier = AccessibilityIdentifiers.LibraryPanels.bottomDeleteButton return button }() @@ -103,10 +117,18 @@ class HistoryPanel: UIViewController, searchbar.searchTextField.placeholder = self.viewModel.searchHistoryPlaceholder searchbar.returnKeyType = .go searchbar.delegate = self - } - - private lazy var tableView: UITableView = .build { [weak self] tableView in - guard let self = self else { return } + // Ecosia: Update SearchBar properties + searchbar.searchBarStyle = .prominent + searchbar.searchTextField.layer.cornerRadius = 18 + searchbar.searchTextField.layer.masksToBounds = true + searchbar.backgroundImage = .init() + } + + // Ecosia: Update TableView init to make it grouped + // private lazy var tableView: UITableView = .build { [weak self] tableView in + private lazy var tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.translatesAutoresizingMaskIntoConstraints = false tableView.dataSource = self.diffableDataSource tableView.addGestureRecognizer(self.longPressRecognizer) tableView.accessibilityIdentifier = a11yIds.tableView @@ -120,9 +142,12 @@ class HistoryPanel: UIViewController, forCellReuseIdentifier: OneLineTableViewCell.cellIdentifier) tableView.register(SiteTableViewHeader.self, forHeaderFooterViewReuseIdentifier: SiteTableViewHeader.cellIdentifier) - tableView.sectionHeaderTopPadding = 0 - } + + // Ecosia: Update tableView properties + tableView.contentInset.top = 32 + return tableView + }() lazy var longPressRecognizer: UILongPressGestureRecognizer = { UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGestureRecognized)) @@ -137,6 +162,8 @@ class HistoryPanel: UIViewController, label.numberOfLines = 0 label.adjustsFontSizeToFitWidth = true } + // Ecosia: Add Empty Header + private lazy var emptyHeader = EmptyHeader(icon: "libraryHistory", title: .localized(.noHistory), subtitle: .localized(.websitesYouHave)) var refreshControl: UIRefreshControl? var recentlyClosedCell: OneLineTableViewCell? @@ -224,9 +251,14 @@ class HistoryPanel: UIViewController, tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), - bottomStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + /* Ecosia: Update constraints + bottomStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + bottomStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + bottomStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor) + */ + bottomStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8), bottomStackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), - bottomStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor) + bottomStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8) ]) } @@ -267,7 +299,7 @@ class HistoryPanel: UIViewController, if let actionableItem = item as? HistoryActionablesModel { switch actionableItem.itemIdentity { case .clearHistory: - isEnabled = !viewModel.groupedSites.isEmpty + isEnabled = !viewModel.dateGroupedSites.isEmpty case .recentlyClosed: isEnabled = viewModel.hasRecentlyClosed recentlyClosedCell = cell @@ -334,10 +366,12 @@ class HistoryPanel: UIViewController, } break case .DynamicFontChanged: + /* Ecosia: Remove emptyStateOverlayView ref if emptyStateOverlayView.superview != nil { emptyStateOverlayView.removeFromSuperview() } emptyStateOverlayView = createEmptyStateOverlayView() + */ resyncHistory() break case .DatabaseWasReopened: @@ -498,7 +532,7 @@ class HistoryPanel: UIViewController, snapshot.sectionIdentifiers.forEach { section in if !viewModel.hiddenSections.contains(where: { $0 == section }) { snapshot.appendItems( - viewModel.groupedSites.itemsForSection(section.rawValue - 1), + viewModel.dateGroupedSites.itemsForSection(section.rawValue - 1), toSection: section ) } @@ -514,7 +548,7 @@ class HistoryPanel: UIViewController, let groupTimeInterval = TimeInterval.fromMicrosecondTimestamp(lastVisit.date) if let groupPlacedAfterItem = ( - viewModel.groupedSites.itemsForSection(groupSection.rawValue - 1) + viewModel.dateGroupedSites.itemsForSection(groupSection.rawValue - 1) ).first(where: { site in guard let lastVisit = site.latestVisit else { return false } return groupTimeInterval > TimeInterval.fromMicrosecondTimestamp(lastVisit.date) @@ -589,7 +623,10 @@ class HistoryPanel: UIViewController, if viewModel.shouldShowEmptyState(searchText: searchbar.text ?? "") { applyEmptyStateViewTheme(currentTheme()) welcomeLabel.text = viewModel.emptyStateText - tableView.tableFooterView = emptyStateOverlayView + // Ecosia: Replace empty header + // tableView.tableFooterView = emptyStateOverlayView + tableView.tableFooterView = emptyHeader + emptyHeader.applyTheme(theme: currentTheme()) } else { tableView.alwaysBounceVertical = true tableView.tableFooterView = nil @@ -640,18 +677,39 @@ class HistoryPanel: UIViewController, tableView.backgroundColor = theme.colors.layer1 emptyStateOverlayView.backgroundColor = theme.colors.layer1 + /* Ecosia: Search Bar and TableView with same color searchbar.backgroundColor = theme.colors.layer3 + */ + searchbar.barTintColor = tableView.backgroundColor + searchbar.backgroundColor = tableView.backgroundColor + searchbar.searchTextField.backgroundColor = theme.colors.ecosia.backgroundPrimary + + /* Ecosia: Update search bar image let tintColor = theme.colors.textPrimary let searchBarImage = UIImage(named: StandardImageIdentifiers.Large.history)? .withRenderingMode(.alwaysTemplate) .tinted(withColor: tintColor) + */ + let searchBarImage = UIImage(named: "search")?.tinted(withColor: theme.colors.ecosia.textSecondary).createScaled(.init(width: 16, height: 16)) + searchbar.setImage(searchBarImage, for: .search, state: .normal) + + /* Ecosia: Update search bar tint color searchbar.tintColor = theme.colors.textPrimary + */ + searchbar.tintColor = theme.colors.textPrimary + navigationController?.navigationBar.titleTextAttributes = [ NSAttributedString.Key.foregroundColor: theme.colors.textPrimary ] + + /* Ecosia: Update bottomSearchButton theming bottomSearchButton.tintColor = theme.colors.iconPrimary bottomDeleteButton.tintColor = theme.colors.iconPrimary + */ + bottomSearchButton.tintColor = theme.colors.ecosia.textPrimary + bottomDeleteButton.tintColor = theme.colors.ecosia.stateError + applyEmptyStateViewTheme(theme) tableView.reloadData() @@ -938,7 +996,7 @@ extension HistoryPanel: UITableViewDataSourcePrefetching { func shouldLoadRow(for indexPath: IndexPath) -> Bool { guard HistoryPanelSections(rawValue: indexPath.section) != .additionalHistoryActions else { return false } - return indexPath.row >= viewModel.groupedSites.numberOfItemsForSection( + return indexPath.row >= viewModel.dateGroupedSites.numberOfItemsForSection( indexPath.section - 1 ) - historyPanelPrefetchOffset } diff --git a/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift b/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift index 0c434ca1f9ead..9927b9fbcd216 100644 --- a/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift +++ b/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanelViewModel.swift @@ -19,6 +19,7 @@ private class FetchInProgressError: MaybeErrorType { class HistoryPanelViewModel: FeatureFlaggable { enum Sections: Int, CaseIterable { case additionalHistoryActions + case lastHour case today case yesterday case lastWeek @@ -28,6 +29,8 @@ class HistoryPanelViewModel: FeatureFlaggable { var title: String? { switch self { + case .lastHour: + return .LibraryPanel.Sections.LastHour case .today: return .LibraryPanel.Sections.Today case .yesterday: @@ -65,7 +68,7 @@ class HistoryPanelViewModel: FeatureFlaggable { // Groups items we should have a single datasource containing sites and groups var searchTermGroups: [ASGroup] = [] // Only individual sites - var groupedSites = DateGroupedTableData() + var dateGroupedSites = DateGroupedTableData(includeLastHour: true) var isFetchInProgress = false var shouldResetHistory = false // Collapsible sections @@ -133,7 +136,7 @@ class HistoryPanelViewModel: FeatureFlaggable { func createGroupedSites(sites: [Site]) { sites.forEach { site in if let latestVisit = site.latestVisit { - self.groupedSites.add(site, timestamp: TimeInterval.fromMicrosecondTimestamp(latestVisit.date)) + self.dateGroupedSites.add(site, timestamp: TimeInterval.fromMicrosecondTimestamp(latestVisit.date)) } } } @@ -166,7 +169,7 @@ class HistoryPanelViewModel: FeatureFlaggable { } func shouldShowEmptyState(searchText: String = "") -> Bool { - guard isSearchInProgress else { return groupedSites.isEmpty && searchTermGroups.isEmpty } + guard isSearchInProgress else { return dateGroupedSites.isEmpty && searchTermGroups.isEmpty } // if the search text is empty we show the regular history so the empty should not show return !searchText.isEmpty ? searchResultSites.isEmpty : false @@ -188,7 +191,7 @@ class HistoryPanelViewModel: FeatureFlaggable { currentFetchOffset = 0 searchTermGroups.removeAll() - groupedSites = DateGroupedTableData() + dateGroupedSites = DateGroupedTableData() buildVisibleSections() } @@ -201,9 +204,11 @@ class HistoryPanelViewModel: FeatureFlaggable { /// Based on the latest visit of the group items gets the section where the group should be added /// if the section is available (visible) and not hidden returns it if not returns nil /// - Parameter group: ASGroup + /// - Parameter comparisonDate: Comparison date is used to control unit tests outcome. /// - Returns: Section where group should be added - func shouldAddGroupToSections(group: ASGroup) -> HistoryPanelViewModel.Sections? { - guard let section = groupBelongsToSection(asGroup: group), + func shouldAddGroupToSections(group: ASGroup, + comparisonDate: Date = Date()) -> HistoryPanelViewModel.Sections? { + guard let section = groupBelongsToSection(asGroup: group, comparisonDate: comparisonDate), visibleSections.contains(section), !hiddenSections.contains(section) else { return nil @@ -213,7 +218,11 @@ class HistoryPanelViewModel: FeatureFlaggable { } /// This helps us place an ASGroup in the correct section. - func groupBelongsToSection(asGroup: ASGroup) -> HistoryPanelViewModel.Sections? { + /// - Parameters: + /// - asGroup: ASGroup + /// - comparisonDate: Comparison date is used to control unit tests outcome. + /// - Returns: Section where group should be added + func groupBelongsToSection(asGroup: ASGroup, comparisonDate: Date = Date()) -> HistoryPanelViewModel.Sections? { guard let individualItem = asGroup.groupedItems.last, let lastVisit = individualItem.latestVisit else { return nil } @@ -221,14 +230,15 @@ class HistoryPanelViewModel: FeatureFlaggable { let groupDate = TimeInterval.timeIntervalSince1970ToDate( timeInterval: TimeInterval.fromMicrosecondTimestamp(lastVisit.date) ) - - if groupDate.isToday() { + if groupDate.isWithinLastHour(comparisonDate: comparisonDate) { + return .lastHour + } else if groupDate.isToday() { return .today } else if groupDate.isYesterday() { return .yesterday - } else if groupDate.isWithinLast7Days() { + } else if groupDate.isWithinLast7Days(comparisonDate: comparisonDate) { return .lastWeek - } else if groupDate.isWithinLast14Days() { + } else if groupDate.isWithinLast14Days(comparisonDate: comparisonDate) { // Since two weeks falls within here, lastMonth will have an ASGroup, if it exists. return .lastMonth } @@ -249,43 +259,16 @@ class HistoryPanelViewModel: FeatureFlaggable { } func deleteGroupsFor(dateOption: HistoryDeletionUtilityDateOptions) { - if dateOption == .lastHour { - deleteLastHourHistory() - } else { - deleteHistory(dateOption: dateOption) - } - } - - private func deleteHistory(dateOption: HistoryDeletionUtilityDateOptions) { guard let deletableSections = getDeletableSection(for: dateOption) else { return } deletableSections.forEach { section in // Remove grouped items for delete section var sectionItems: [AnyHashable] = groupsForSection(section: section) - let singleItems = groupedSites.itemsForSection(section.rawValue - 1) + let singleItems = dateGroupedSites.itemsForSection(section.rawValue - 1) sectionItems.append(contentsOf: singleItems) removeHistoryItems(item: sectionItems, at: section.rawValue) } } - private func deleteLastHourHistory() { - // Get the sections in which history items from the last hour could exist - guard let deletableSections = getDeletableSection(for: .lastHour) else { return } - - deletableSections.forEach { section in - let allHistoryItemsInSection = groupedSites.itemsForSection(section.rawValue - 1) - - // Filter the history items to only include items from the last hour - let lastHourHistoryItems = allHistoryItemsInSection.filter { site in - guard let latestVisit = site.latestVisit else {return false} - let siteVisitTimeStamp = TimeInterval.fromMicrosecondTimestamp(latestVisit.date) - let oneHourAgoTimeStamp = TimeInterval.fromMicrosecondTimestamp( - Date(timeIntervalSinceNow: -(60 * 60)).toMicrosecondsSince1970()) - return siteVisitTimeStamp >= oneHourAgoTimeStamp - } - removeHistoryItems(item: lastHourHistoryItems, at: section.rawValue) - } - } - /// This handles removing either a Site or an ASGroup from the view. func removeHistoryItems(item historyItem: [AnyHashable], at section: Int) { historyItem.forEach { item in @@ -337,26 +320,26 @@ class HistoryPanelViewModel: FeatureFlaggable { private func buildGroupsVisibleSections() { self.visibleSections = Sections.allCases.filter { section in - return self.groupedSites.numberOfItemsForSection(section.rawValue - 1) > 0 + return self.dateGroupedSites.numberOfItemsForSection(section.rawValue - 1) > 0 || !self.groupsForSection(section: section).isEmpty } } private func buildVisibleSections() { self.visibleSections = Sections.allCases.filter { section in - self.groupedSites.numberOfItemsForSection(section.rawValue - 1) > 0 + self.dateGroupedSites.numberOfItemsForSection(section.rawValue - 1) > 0 } } /// Provide de-duplicated history and visible history sections. private func populateHistorySites(fetchedSites: [Site]) { - let allCurrentGroupedSites = self.groupedSites.allItems() + let allCurrentGroupedSites = self.dateGroupedSites.allItems() let allUniquedSitesToAdd = (allCurrentGroupedSites + fetchedSites) .filter { !allCurrentGroupedSites.contains($0) } allUniquedSitesToAdd.forEach { site in if let latestVisit = site.latestVisit { - self.groupedSites.add(site, timestamp: TimeInterval.fromMicrosecondTimestamp(latestVisit.date)) + self.dateGroupedSites.add(site, timestamp: TimeInterval.fromMicrosecondTimestamp(latestVisit.date)) } } } @@ -376,7 +359,7 @@ class HistoryPanelViewModel: FeatureFlaggable { } private func deleteSingle(site: Site) { - groupedSites.remove(site) + dateGroupedSites.remove(site) self.profile.places.deleteVisitsFor(url: site.url).uponQueue(.main) { _ in NotificationCenter.default.post(name: .TopSitesUpdated, object: nil) } @@ -389,11 +372,11 @@ class HistoryPanelViewModel: FeatureFlaggable { private func getDeletableSection(for dateOption: HistoryDeletionUtilityDateOptions) -> [Sections]? { switch dateOption { case .lastHour: - return [.today, .yesterday] + return [.lastHour] case .today: - return [.today] + return [.lastHour, .today] case .yesterday: - return [.today, .yesterday] + return [.lastHour, .today, .yesterday] default: return nil } diff --git a/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift b/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift index f1c4f120b15a1..11c98d0444014 100644 --- a/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift +++ b/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift @@ -50,8 +50,12 @@ extension LibraryPanel { enum LibraryPanelType: Int, CaseIterable { case bookmarks = 0 case history = 1 + /* Ecosia: Invert Download and Reading list positions in the LibraryViewController case downloads = 2 case readingList = 3 + */ + case readingList = 2 + case downloads = 3 var title: String { switch self { @@ -97,15 +101,26 @@ class LibraryPanelHelper { accessibilityIdentifier: AccessibilityIdentifiers.LibraryPanels.historyView, panelType: .history), - LibraryPanelDescriptor( + /* Ecosia: Invert Download and Reading list positions in the LibraryViewController + LibraryPanelDescriptor( accessibilityLabel: .LibraryPanelDownloadsAccessibilityLabel, accessibilityIdentifier: AccessibilityIdentifiers.LibraryPanels.downloadsView, panelType: .downloads), - + + LibraryPanelDescriptor( + accessibilityLabel: .LibraryPanelReadingListAccessibilityLabel, + accessibilityIdentifier: AccessibilityIdentifiers.LibraryPanels.readingListView, + panelType: .readingList), + */ LibraryPanelDescriptor( accessibilityLabel: .LibraryPanelReadingListAccessibilityLabel, accessibilityIdentifier: AccessibilityIdentifiers.LibraryPanels.readingListView, - panelType: .readingList) + panelType: .readingList), + + LibraryPanelDescriptor( + accessibilityLabel: .LibraryPanelDownloadsAccessibilityLabel, + accessibilityIdentifier: AccessibilityIdentifiers.LibraryPanels.downloadsView, + panelType: .downloads), ] }() } diff --git a/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewController.swift b/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewController.swift index 1d2e243adff61..715d48e153d3c 100644 --- a/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewController.swift +++ b/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewController.swift @@ -169,12 +169,20 @@ class LibraryViewController: UIViewController, Themeable { case 1: selectedPanel = .history eventValue = .historyPanel + /* Ecosia: Invert Download and Reading list positions in the LibraryViewController case 2: selectedPanel = .downloads eventValue = .downloadsPanel case 3: selectedPanel = .readingList eventValue = .readingListPanel + */ + case 2: + selectedPanel = .readingList + eventValue = .readingListPanel + case 3: + selectedPanel = .downloads + eventValue = .downloadsPanel default: return } @@ -317,7 +325,13 @@ class LibraryViewController: UIViewController, Themeable { let theme = themeManager.getCurrentTheme(for: windowUUID) let standardAppearance = UIToolbarAppearance() standardAppearance.configureWithDefaultBackground() + + /* Ecosia: Update theming standardAppearance.backgroundColor = theme.colors.layer1 + */ + standardAppearance.backgroundColor = theme.colors.ecosia.backgroundPrimary + standardAppearance.shadowColor = theme.colors.ecosia.borderDecorative + navigationController?.toolbar.standardAppearance = standardAppearance navigationController?.toolbar.compactAppearance = standardAppearance navigationController?.toolbar.scrollEdgeAppearance = standardAppearance @@ -333,6 +347,8 @@ class LibraryViewController: UIViewController, Themeable { navigationController?.navigationBar.shadowImage = UIImage() let theme = themeManager.getCurrentTheme(for: windowUUID) + + /* Ecosia: Update background from layer1 view.backgroundColor = theme.colors.layer1 navigationController?.navigationBar.barTintColor = theme.colors.layer1 navigationController?.navigationBar.tintColor = theme.colors.actionPrimary @@ -340,9 +356,21 @@ class LibraryViewController: UIViewController, Themeable { navigationController?.toolbar.barTintColor = theme.colors.layer1 navigationController?.toolbar.tintColor = theme.colors.actionPrimary segmentControlToolbar.barTintColor = theme.colors.layer1 - segmentControlToolbar.tintColor = theme.colors.textPrimary + */ + view.backgroundColor = theme.colors.layer3 + navigationController?.navigationBar.barTintColor = theme.colors.ecosia.backgroundPrimaryDecorative + navigationController?.navigationBar.tintColor = theme.colors.actionPrimary + navigationController?.navigationBar.backgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + navigationController?.toolbar.barTintColor = theme.colors.ecosia.backgroundPrimaryDecorative + navigationController?.toolbar.tintColor = theme.colors.actionPrimary + segmentControlToolbar.barTintColor = theme.colors.ecosia.backgroundPrimaryDecorative segmentControlToolbar.isTranslucent = false + // Ecosia: Add segmented control colors + librarySegmentControl.backgroundColor = theme.colors.ecosia.segmentedControlBackgroundRest + librarySegmentControl.selectedSegmentTintColor = theme.colors.ecosia.segmentedControlBackgroundActive + librarySegmentControl.tintColor = theme.colors.ecosia.buttonContentSecondary + setNeedsStatusBarAppearanceUpdate() setupToolBarAppearance() } diff --git a/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewModel.swift b/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewModel.swift index 2f50f4ee44e0c..c59f2b5906a40 100644 --- a/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewModel.swift +++ b/firefox-ios/Client/Frontend/Library/LibraryViewController/LibraryViewModel.swift @@ -11,10 +11,18 @@ class LibraryViewModel { var selectedPanel: LibraryPanelType? var segmentedControlItems: [UIImage] { + /* Ecosia: Update images and positions [UIImage(named: StandardImageIdentifiers.Large.bookmarkTrayFill) ?? UIImage(), UIImage(named: StandardImageIdentifiers.Large.history) ?? UIImage(), UIImage(named: StandardImageIdentifiers.Large.download) ?? UIImage(), UIImage(named: StandardImageIdentifiers.Large.readingList) ?? UIImage()] + */ + [ + UIImage(named: "libraryFavorites"), + UIImage(named: "libraryHistory"), + UIImage(named: "libraryReading"), + UIImage(named: "libraryDownloads") + ].compactMap({ $0 }) } init(withProfile profile: Profile) { diff --git a/firefox-ios/Client/Frontend/Library/Reader/ReaderPanel.swift b/firefox-ios/Client/Frontend/Library/Reader/ReaderPanel.swift index 3683f6816854a..c941ba8310435 100644 --- a/firefox-ios/Client/Frontend/Library/Reader/ReaderPanel.swift +++ b/firefox-ios/Client/Frontend/Library/Reader/ReaderPanel.swift @@ -292,13 +292,16 @@ class ReadingListPanel: UITableViewController, let scrollView = self.emptyStateViewA11YScroll let emptyView = self.emptyStateView - + // Ecosia: Add `topAnchorDelta` util to determine `topAnchor` margin + let topAnchorDelta: CGFloat = UIDevice.current.userInterfaceIdiom == .phone ? -50 : 0 if visible { guard scrollView.superview == nil else { return } scrollView.addSubview(emptyView) NSLayoutConstraint.activate([ emptyView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), - emptyView.topAnchor.constraint(equalTo: scrollView.topAnchor), + // Ecosia: Update reading list top anchor constant only if iPhone + // emptyView.topAnchor.constraint(equalTo: scrollView.topAnchor), + emptyView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: topAnchorDelta), emptyView.widthAnchor.constraint(equalTo: scrollView.widthAnchor), emptyView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor) ]) @@ -325,9 +328,14 @@ class ReadingListPanel: UITableViewController, return scrollView }() + private lazy var emptyStateView: EmptyReadingListView = { + EmptyReadingListView(windowUUID: self.windowUUID) + }() + /* Ecosia: Update Empty State View reference private lazy var emptyStateView: UIView = { return ReaderPanelEmptyStateView(windowUUID: self.windowUUID) }() + */ @objc fileprivate func longPress(_ longPressGestureRecognizer: UILongPressGestureRecognizer) { diff --git a/firefox-ios/Client/Frontend/Microsurvey/MicrosurveyTelemetry.swift b/firefox-ios/Client/Frontend/Microsurvey/MicrosurveyTelemetry.swift index 0072ea65cceb1..929a9be52bb33 100644 --- a/firefox-ios/Client/Frontend/Microsurvey/MicrosurveyTelemetry.swift +++ b/firefox-ios/Client/Frontend/Microsurvey/MicrosurveyTelemetry.swift @@ -3,31 +3,43 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ struct MicrosurveyTelemetry { func surveyViewed(surveyId: String) { + /* Ecosia: Remove Glean let surveyIdExtra = GleanMetrics.Microsurvey.ShownExtra(surveyId: surveyId) GleanMetrics.Microsurvey.shown.record(surveyIdExtra) + */ } func privacyNoticeTapped(surveyId: String) { + /* Ecosia: Remove Glean let surveyIdExtra = GleanMetrics.Microsurvey.PrivacyNoticeTappedExtra(surveyId: surveyId) GleanMetrics.Microsurvey.privacyNoticeTapped.record(surveyIdExtra) + */ } func dismissButtonTapped(surveyId: String) { + /* Ecosia: Remove Glean let surveyIdExtra = GleanMetrics.Microsurvey.DismissButtonTappedExtra(surveyId: surveyId) GleanMetrics.Microsurvey.dismissButtonTapped.record(surveyIdExtra) + */ } func userResponseSubmitted(surveyId: String, userSelection: String) { + /* Ecosia: Remove Glean let submitExtra = GleanMetrics.Microsurvey.SubmitButtonTappedExtra(surveyId: surveyId, userSelection: userSelection) GleanMetrics.Microsurvey.submitButtonTapped.record(submitExtra) + */ } func confirmationShown(surveyId: String) { + /* Ecosia: Remove Glean let surveyIdExtra = GleanMetrics.Microsurvey.ConfirmationShownExtra(surveyId: surveyId) GleanMetrics.Microsurvey.confirmationShown.record(surveyIdExtra) + */ } } diff --git a/firefox-ios/Client/Frontend/Settings/ClearPrivateDataTableViewController.swift b/firefox-ios/Client/Frontend/Settings/ClearPrivateDataTableViewController.swift index 5bd1719b3354e..207f6454c3e60 100644 --- a/firefox-ios/Client/Frontend/Settings/ClearPrivateDataTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/ClearPrivateDataTableViewController.swift @@ -6,6 +6,7 @@ import UIKit import Shared import Common import ComponentLibrary +import Ecosia class ClearPrivateDataTableViewController: ThemedTableViewController { private var clearButton: UITableViewCell? @@ -104,11 +105,18 @@ class ClearPrivateDataTableViewController: ThemedTableViewController { } else if indexPath.section == sectionToggles { cell.textLabel?.text = clearables[indexPath.item].clearable.label cell.textLabel?.numberOfLines = 0 + /* Ecosia: Use EcosiaThemedSwitch let control = ThemedSwitch() control.applyTheme(theme: currentTheme()) + */ + let control = EcosiaThemedSwitch() + /* Ecosia: No need for `onTintColor` with EcosiaThemedSwitch control.onTintColor = currentTheme().colors.actionPrimary + */ control.addTarget(self, action: #selector(switchValueChanged), for: .valueChanged) control.isOn = toggles[indexPath.item] + // Ecosia: Apply theme only after `isOn` to use correct color + control.applyTheme(theme: currentTheme()) cell.accessoryView = control cell.selectionStyle = .none control.tag = indexPath.item @@ -154,6 +162,8 @@ class ClearPrivateDataTableViewController: ThemedTableViewController { let view = WebsiteDataManagementViewController(windowUUID: windowUUID) navigationController?.pushViewController(view, animated: true) } else if indexPath.section == sectionButton { + // Ecosia: Track "Clear Private Data" button click + Analytics.shared.clearsDataFromSection(.main) let alert: UIAlertController if self.toggles[historyClearableIndex] && profile.hasAccount() { alert = clearSyncedHistoryAlert(okayCallback: clearPrivateData) diff --git a/firefox-ios/Client/Frontend/Settings/Clearables.swift b/firefox-ios/Client/Frontend/Settings/Clearables.swift index 5a6345f473045..aeac26d510335 100644 --- a/firefox-ios/Client/Frontend/Settings/Clearables.swift +++ b/firefox-ios/Client/Frontend/Settings/Clearables.swift @@ -8,6 +8,7 @@ import WebKit import CoreSpotlight import SiteImageView import Common +import Ecosia // A base protocol for something that can be cleared. protocol Clearable { @@ -153,6 +154,12 @@ class CookiesClearable: Clearable { WKWebsiteDataTypeIndexedDBDatabases ] ) + + // Ecosia: Trigger native logout when cookies are cleared + Task { + await DataClearingIntegration.handleEcosiaAuthCookieClearing() + } + WKWebsiteDataStore.default().removeData(ofTypes: dataTypes, modifiedSince: .distantPast, completionHandler: {}) logger.log("CookiesClearable succeeded.", diff --git a/firefox-ios/Client/Frontend/Settings/HomepageSettings/HomePageSettingViewController.swift b/firefox-ios/Client/Frontend/Settings/HomepageSettings/HomePageSettingViewController.swift index 8e4bc1fea5ba8..68d2af4050b1a 100644 --- a/firefox-ios/Client/Frontend/Settings/HomepageSettings/HomePageSettingViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/HomepageSettings/HomePageSettingViewController.swift @@ -5,6 +5,7 @@ import Foundation import Shared import Common +import Ecosia class HomePageSettingViewController: SettingsTableViewController, FeatureFlaggable { // MARK: - Variables @@ -261,12 +262,18 @@ extension HomePageSettingViewController { override var accessoryType: UITableViewCell.AccessoryType { return .disclosureIndicator } override var accessibilityIdentifier: String? { + /* Ecosia: Override accessibilityIdentifier return AccessibilityIdentifiers.Settings.Homepage.CustomizeFirefox.Shortcuts.settingsPage + */ + return CustomizableNTPSettingConfig.topSites.accessibilityIdentifierPrefix } override var style: UITableViewCell.CellStyle { return .value1 } override var status: NSAttributedString { + /* Ecosia: Update contidion let areShortcutsOn = profile.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.TopSiteSection) ?? true + */ + let areShortcutsOn = User.shared.showTopSites typealias Shortcuts = String.Settings.Homepage.Shortcuts let status: String = areShortcutsOn ? Shortcuts.ToggleOn : Shortcuts.ToggleOff return NSAttributedString(string: String(format: status)) @@ -275,7 +282,10 @@ extension HomePageSettingViewController { init(settings: SettingsTableViewController) { self.profile = settings.profile self.windowUUID = settings.windowUUID + /* Ecosia: rename to Top Sites super.init(title: NSAttributedString(string: .Settings.Homepage.Shortcuts.ShortcutsPageTitle)) + */ + super.init(title: NSAttributedString(string: .localized(.topSites))) } override func onClick(_ navigationController: UINavigationController?) { diff --git a/firefox-ios/Client/Frontend/Settings/HomepageSettings/TopSitesSettings/TopSitesSettingsViewController.swift b/firefox-ios/Client/Frontend/Settings/HomepageSettings/TopSitesSettings/TopSitesSettingsViewController.swift index 1880eb332d88c..042687abda612 100644 --- a/firefox-ios/Client/Frontend/Settings/HomepageSettings/TopSitesSettings/TopSitesSettingsViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/HomepageSettings/TopSitesSettings/TopSitesSettingsViewController.swift @@ -29,6 +29,7 @@ class TopSitesSettingsViewController: SettingsTableViewController, FeatureFlagga // MARK: - Methods override func generateSettings() -> [SettingSection] { var sections = [Setting]() + /* Ecosia: custom top site setting let topSitesSetting = BoolSetting( prefs: profile.prefs, theme: themeManager.getCurrentTheme(for: windowUUID), @@ -46,6 +47,11 @@ class TopSitesSettingsViewController: SettingsTableViewController, FeatureFlagga titleText: .Settings.Homepage.Shortcuts.SponsoredShortcutsToggle ) sections.append(sponsoredShortcutSetting) + */ + let topSitesSetting = NTPCustomizationSetting(prefs: profile.prefs, + theme: themeManager.getCurrentTheme(for: windowUUID), + config: .topSites) + sections.append(topSitesSetting) let toggleSection = SettingSection(title: nil, children: sections) diff --git a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift index 899d24957dcd3..8c59d43fac916 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift @@ -5,6 +5,7 @@ import Common import UIKit import Shared +import Ecosia // MARK: - Settings Flow Delegate Protocol @@ -64,7 +65,10 @@ class AppSettingsTableViewController: SettingsTableViewController, self.appAuthenticator = appAuthenticator self.applicationHelper = applicationHelper - super.init(windowUUID: tabManager.windowUUID) + // Ecosia: Update TableView to grouped style + // super.init(windowUUID: tabManager.windowUUID) + super.init(style: .insetGrouped, windowUUID: tabManager.windowUUID) + self.profile = profile self.tabManager = tabManager self.settingsDelegate = delegate @@ -82,12 +86,24 @@ class AppSettingsTableViewController: SettingsTableViewController, setupNavigationBar() configureAccessibilityIdentifiers() + + // Ecosia: Register Nudge Card if needed + if User.shared.shouldShowDefaultBrowserSettingNudgeCard { + tableView.register(DefaultBrowserSettingsNudgeCardHeaderView.self, + forHeaderFooterViewReuseIdentifier: DefaultBrowserSettingsNudgeCardHeaderView.cellIdentifier) + } } + /* Ecosia: Move settings reload to `viewWillAppear` override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) askedToReload() } + */ + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + askedToReload() + } // MARK: - Actions @@ -121,7 +137,7 @@ class AppSettingsTableViewController: SettingsTableViewController, case .creditCard: authenticateUserFor(route: route) case .rateApp: - RatingPromptManager.goToAppStoreReview() + openHelpDialogOrRateApp() default: break } @@ -137,7 +153,25 @@ class AppSettingsTableViewController: SettingsTableViewController, } } - // MARK: - User Authentication + // MARK: Ecosia + + private func openHelpDialogOrRateApp() { + let rateAction = UIAlertAction(title: .localized(.settingsRatingPromptYes), style: .default) { _ in + UIApplication.shared.open(EcosiaEnvironment.current.urlProvider.storeWriteReviewPage) + } + + let helpAction = UIAlertAction(title: .localized(.settingsRatingPromptNo), style: .destructive) { [weak self] _ in + self?.settingsDelegate?.settingsOpenURLInNewTab(EcosiaEnvironment.current.urlProvider.helpPage) + self?.dismissVC() + } + + let alertController = UIAlertController(title: .localized(.settingsRatingPromptTitle), message: nil, preferredStyle: .alert) + alertController.addAction(rateAction) + alertController.addAction(helpAction) + present(alertController, animated: true) + } + + // MARK: - User AutheSntication // Authenticates the user prior to allowing access to sensitive sections private func authenticateUserFor(route: Route.SettingsSection) { @@ -193,6 +227,7 @@ class AppSettingsTableViewController: SettingsTableViewController, // MARK: - Generate Settings override func generateSettings() -> [SettingSection] { + /* Ecosia: Review Settings var settings = [SettingSection]() settings += getDefaultBrowserSetting() settings += getAccountSetting() @@ -204,8 +239,10 @@ class AppSettingsTableViewController: SettingsTableViewController, if showDebugSettings { settings += getDebugSettings() } - + return settings + */ + getEcosiaSettingsSectionsShowingDebug(showDebugSettings) } private func getDefaultBrowserSetting() -> [SettingSection] { @@ -329,7 +366,11 @@ class AppSettingsTableViewController: SettingsTableViewController, prefKey: PrefsKeys.Settings.closePrivateTabs, defaultValue: true, titleText: .AppSettingsClosePrivateTabsTitle, - statusText: .AppSettingsClosePrivateTabsDescription) + statusText: .AppSettingsClosePrivateTabsDescription) { _ in + let action = TabTrayAction(windowUUID: self.windowUUID, + actionType: TabTrayActionType.closePrivateTabsSettingToggled) + store.dispatch(action) + } ] privacySettings.append(ContentBlockerSetting(settings: self, settingsDelegate: parentCoordinator)) @@ -455,6 +496,8 @@ class AppSettingsTableViewController: SettingsTableViewController, // MARK: - UITableViewDelegate + /* Ecosia: Set the header view for the table view with custom handling for the default browser nudge card + Adds other overrides after this one to modify the UI logic override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerView = super.tableView( tableView, @@ -462,4 +505,73 @@ class AppSettingsTableViewController: SettingsTableViewController, ) as! ThemedTableSectionHeaderFooterView return headerView } + */ + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + if shouldShowDefaultBrowserNudgeCardInSection(section), + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: DefaultBrowserSettingsNudgeCardHeaderView.cellIdentifier) + as? DefaultBrowserSettingsNudgeCardHeaderView { + header.onDismiss = { [weak self] in + User.shared.hideDefaultBrowserSettingNudgeCard() + Analytics.shared.defaultBrowserSettingsViaNudgeCardDismiss() + self?.hideDefaultBrowserNudgeCardInSection(section) + } + header.onTap = { [weak self] in + self?.showDefaultBrowserDetailView() + } + header.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + return header + } else if let headerView = super.tableView( + tableView, + viewForHeaderInSection: section + ) as? ThemedTableSectionHeaderFooterView { + return headerView + } + return nil + } + + override func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard shouldShowDefaultBrowserNudgeCardInSection(section) else { + return super.tableView(tableView, viewForFooterInSection: section) + } + return nil + } + + override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + guard shouldShowDefaultBrowserNudgeCardInSection(section) else { + return super.tableView(tableView, heightForFooterInSection: section) + } + return 0 + } + + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + if shouldShowDefaultBrowserNudgeCardInSection(section) { + return UITableView.automaticDimension + } + return super.tableView(tableView, heightForHeaderInSection: section) + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if shouldShowDefaultBrowserNudgeCardInSection(section) { + return 1 + } + return super.tableView(tableView, numberOfRowsInSection: section) + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + if shouldShowDefaultBrowserNudgeCardInSection(indexPath.section) { + let cell = UITableViewCell() + cell.isUserInteractionEnabled = false + cell.backgroundColor = .clear + cell.contentView.isHidden = true + return cell + } + return super.tableView(tableView, cellForRowAt: indexPath) + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if shouldShowDefaultBrowserNudgeCardInSection(indexPath.section) { + return .leastNonzeroMagnitude + } + return super.tableView(tableView, heightForRowAt: indexPath) + } } diff --git a/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift index ace760254eff6..7fe83a598b407 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift @@ -24,6 +24,13 @@ final class FeatureFlagsDebugViewController: SettingsTableViewController, Featur return SettingSection( title: nil, children: [ + FeatureFlagsBoolSetting( + with: .bookmarksRefactor, + titleText: format(string: "Enable Bookmarks Redesign"), + statusText: format(string: "Toggle to use the new bookmarks design") + ) { [weak self] _ in + self?.reloadView() + }, FeatureFlagsBoolSetting( with: .closeRemoteTabs, titleText: format(string: "Enable Close Remote Tabs"), diff --git a/firefox-ios/Client/Frontend/Settings/Main/Debug/OpenFiftyTabsDebugOption.swift b/firefox-ios/Client/Frontend/Settings/Main/Debug/OpenFiftyTabsDebugOption.swift index 116d94ebaf117..1cda50f24c702 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/Debug/OpenFiftyTabsDebugOption.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/Debug/OpenFiftyTabsDebugOption.swift @@ -15,10 +15,16 @@ class OpenFiftyTabsDebugOption: HiddenSetting { } override var title: NSAttributedString? { + /* Ecosia: Update Debug message return NSAttributedString( string: "Open 50 `mozilla.org` tabs ⚠️", attributes: [NSAttributedString.Key.foregroundColor: theme.colors.textPrimary] ) + */ + return NSAttributedString( + string: "Open 50 `ecosia.org` tabs ⚠️", + attributes: [NSAttributedString.Key.foregroundColor: theme.colors.textPrimary] + ) } override func onClick(_ navigationController: UINavigationController?) { diff --git a/firefox-ios/Client/Frontend/Settings/Main/DefaultBrowserSetting.swift b/firefox-ios/Client/Frontend/Settings/Main/DefaultBrowserSetting.swift index a8d43a0f09a1b..9d183eca62b87 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/DefaultBrowserSetting.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/DefaultBrowserSetting.swift @@ -10,12 +10,8 @@ class DefaultBrowserSetting: Setting { override var accessibilityIdentifier: String? { return "DefaultBrowserSettings" } init(theme: Theme) { - super.init( - title: NSAttributedString( - string: String.DefaultBrowserMenuItem, - attributes: [NSAttributedString.Key.foregroundColor: theme.colors.actionPrimary] - ) - ) + super.init(title: NSAttributedString(string: String.DefaultBrowserMenuItem, + attributes: [NSAttributedString.Key.foregroundColor: theme.colors.actionPrimary])) } override func onClick(_ navigationController: UINavigationController?) { diff --git a/firefox-ios/Client/Frontend/Settings/Main/Privacy/ContentBlockerSetting.swift b/firefox-ios/Client/Frontend/Settings/Main/Privacy/ContentBlockerSetting.swift index dae75de9eee02..d35fb475b5f3d 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/Privacy/ContentBlockerSetting.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/Privacy/ContentBlockerSetting.swift @@ -16,7 +16,7 @@ class ContentBlockerSetting: Setting { override var accessibilityIdentifier: String? { return AccessibilityIdentifiers.Settings.ContentBlocker.title } - +/* Ecosia: Remove `status` as there is a UI issue in showing the content in e.g. German override var status: NSAttributedString? { let defaultValue = ContentBlockingConfig.Defaults.NormalBrowsing let isOn = profile.prefs.boolForKey(ContentBlockingConfig.Prefs.EnabledKey) ?? defaultValue @@ -31,6 +31,7 @@ class ContentBlockerSetting: Setting { return NSAttributedString(string: .Settings.Homepage.Shortcuts.ToggleOff) } } + */ override var style: UITableViewCell.CellStyle { return .value1 } diff --git a/firefox-ios/Client/Frontend/Settings/Main/SettingDisclosureUtility.swift b/firefox-ios/Client/Frontend/Settings/Main/SettingDisclosureUtility.swift index dfcae7358ab89..8ebfaa69ef128 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/SettingDisclosureUtility.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/SettingDisclosureUtility.swift @@ -8,6 +8,7 @@ import Shared struct SettingDisclosureUtility { static func buildDisclosureIndicator(theme: Theme) -> UIImageView { + /* Ecosia: Custom chevron disclosure indicator let disclosureIndicator = UIImageView() disclosureIndicator.image = UIImage( named: StandardImageIdentifiers.Large.chevronRight @@ -16,5 +17,7 @@ struct SettingDisclosureUtility { disclosureIndicator.sizeToFit() disclosureIndicator.adjustsImageSizeForAccessibilityContentSizeCategory = true return disclosureIndicator + */ + ecosiaDisclosureIndicator(theme: theme) } } diff --git a/firefox-ios/Client/Frontend/Settings/SearchBar/SearchBarSettingsViewModel.swift b/firefox-ios/Client/Frontend/Settings/SearchBar/SearchBarSettingsViewModel.swift index 4dd8fcabd0162..ffc4975b5c9fb 100644 --- a/firefox-ios/Client/Frontend/Settings/SearchBar/SearchBarSettingsViewModel.swift +++ b/firefox-ios/Client/Frontend/Settings/SearchBar/SearchBarSettingsViewModel.swift @@ -5,6 +5,7 @@ import Common import Foundation import Shared +import Ecosia enum SearchBarPosition: String, FlaggableFeatureOptions { case bottom @@ -41,7 +42,10 @@ extension SearchBarLocationProvider { var searchBarPosition: SearchBarPosition { guard let position: SearchBarPosition = featureFlags.getCustomState(for: .searchBarPosition) else { + /* Ecosia: Search Bar stays on top by default return .bottom + */ + return .top } return position @@ -105,6 +109,9 @@ private extension SearchBarSettingsViewModel { let notificationObject = [PrefsKeys.FeatureFlags.SearchBarPosition: searchBarPosition] notificationCenter.post(name: .SearchBarPositionDidChange, withObject: notificationObject) + + // Ecosia: Track toolbar position change + Analytics.shared.searchbarChanged(to: searchBarPosition.rawValue) } func recordPreferenceChange(_ searchBarPosition: SearchBarPosition) { diff --git a/firefox-ios/Client/Frontend/Settings/SearchSettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/SearchSettingsTableViewController.swift index 073d85bb2fc62..565e04b8249e7 100644 --- a/firefox-ios/Client/Frontend/Settings/SearchSettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/SearchSettingsTableViewController.swift @@ -151,7 +151,9 @@ final class SearchSettingsTableViewController: ThemedTableViewController, Featur switch section { case .defaultEngine: engine = model.defaultEngine + /* Ecosia: Remove UI that let User indicate a detail scren cell.editingAccessoryType = .disclosureIndicator + */ cell.accessibilityLabel = .Settings.Search.AccessibilityLabels.DefaultSearchEngine cell.accessibilityValue = engine.shortName cell.textLabel?.text = engine.shortName @@ -368,6 +370,7 @@ final class SearchSettingsTableViewController: ThemedTableViewController, Featur switch section { case .defaultEngine: guard indexPath.item == 0 else { return nil } + /* Ecosia: Hide possibility to show Default Engine selection screen let searchEnginePicker = SearchEnginePicker(windowUUID: windowUUID) // Order alphabetically, so that picker is always consistently ordered. // Every engine is a valid choice for the default engine, even the current default engine. @@ -375,6 +378,8 @@ final class SearchSettingsTableViewController: ThemedTableViewController, Featur searchEnginePicker.delegate = self searchEnginePicker.selectedSearchEngineName = model.defaultEngine?.shortName navigationController?.pushViewController(searchEnginePicker, animated: true) + */ + tableView.deselectRow(at: indexPath, animated: false) case .alternateEngines: let isLastItem = indexPath.item + 1 == model.orderedEngines.count guard isLastItem else { return nil } diff --git a/firefox-ios/Client/Frontend/Settings/SettingsNavigationController.swift b/firefox-ios/Client/Frontend/Settings/SettingsNavigationController.swift index 79cb62d9ccde5..d96d05cd99475 100644 --- a/firefox-ios/Client/Frontend/Settings/SettingsNavigationController.swift +++ b/firefox-ios/Client/Frontend/Settings/SettingsNavigationController.swift @@ -52,8 +52,16 @@ class ThemedNavigationController: DismissableNavigationViewController, Themeable let standardAppearance = UINavigationBarAppearance() standardAppearance.configureWithDefaultBackground() standardAppearance.backgroundColor = theme.colors.layer1 + // Ecosia: Define `largeTitleTextAttributes` as done for ThemedDefaultNavigationController utilized in Widgets + standardAppearance.largeTitleTextAttributes = [.foregroundColor: theme.colors.textPrimary] standardAppearance.titleTextAttributes = [.foregroundColor: theme.colors.textPrimary] + // Ecosia: Update navigationBar properties + navigationBar.setBackgroundImage(UIImage(), for: .default) + standardAppearance.shadowImage = .init() + standardAppearance.shadowColor = nil + navigationBar.prefersLargeTitles = true + navigationBar.standardAppearance = standardAppearance navigationBar.compactAppearance = standardAppearance navigationBar.scrollEdgeAppearance = standardAppearance diff --git a/firefox-ios/Client/Frontend/Settings/SettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/SettingsTableViewController.swift index cae70f73d2fd1..8c1ceebdaa833 100644 --- a/firefox-ios/Client/Frontend/Settings/SettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/SettingsTableViewController.swift @@ -6,6 +6,7 @@ import Account import Common import Shared import UIKit +import Ecosia struct SettingsUX { static let TableViewHeaderFooterHeight = CGFloat(44) @@ -100,6 +101,23 @@ class Setting: NSObject { cell.selectionStyle = enabled ? .default : .none cell.accessibilityIdentifier = accessibilityIdentifier cell.imageView?.image = _image + + // Ecosia: Update identifier + if let accessibilityIdentifier { + cell.textLabel?.accessibilityIdentifier = "\(accessibilityIdentifier)_title" + cell.detailTextLabel?.accessibilityIdentifier = "\(accessibilityIdentifier)_detail" + cell.imageView?.accessibilityIdentifier = "\(accessibilityIdentifier)_image" + } + if let title = title?.string { + if let detailText = cell.detailTextLabel?.text { + cell.accessibilityLabel = "\(title), \(detailText)" + } else if let status = status?.string { + cell.accessibilityLabel = "\(title), \(status)" + } else { + cell.accessibilityLabel = title + } + } + cell.accessibilityTraits = UIAccessibilityTraits.button cell.indentationWidth = 0 cell.layoutMargins = .zero @@ -176,10 +194,16 @@ class PaddedSwitch: UIView { static let padding: CGFloat = 8 } + /* Ecosia: Use themed Switch let switchView: UISwitch + */ + let switchView: EcosiaThemedSwitch init() { + /* Ecosia: Use themed Switch self.switchView = UISwitch() + */ + self.switchView = EcosiaThemedSwitch() super.init(frame: .zero) addSubview(switchView) @@ -224,6 +248,8 @@ class BoolSetting: Setting, FeatureFlaggable { init( prefs: Prefs?, prefKey: String? = nil, + // Ecosia: Add accessibility identifier + accessibilityIdentifier: String? = nil, defaultValue: Bool?, attributedTitleText: NSAttributedString, attributedStatusText: NSAttributedString? = nil, @@ -237,12 +263,16 @@ class BoolSetting: Setting, FeatureFlaggable { self.statusText = attributedStatusText self.featureFlagName = featureFlagName super.init(title: attributedTitleText) + // Ecosia: Add accessibility identifier + self.accessibilityIdentifier = accessibilityIdentifier } convenience init( prefs: Prefs, theme: Theme, prefKey: String? = nil, + // Ecosia: Add accessibility identifier + accessibilityIdentifier: String? = nil, defaultValue: Bool, titleText: String, statusText: String? = nil, @@ -257,6 +287,8 @@ class BoolSetting: Setting, FeatureFlaggable { self.init( prefs: prefs, prefKey: prefKey, + // Ecosia: Add accessibility identifier + accessibilityIdentifier: accessibilityIdentifier, defaultValue: defaultValue, attributedTitleText: NSAttributedString( string: titleText, @@ -305,7 +337,12 @@ class BoolSetting: Setting, FeatureFlaggable { public lazy var control: PaddedSwitch = { let control = PaddedSwitch() + /* Ecosia: Update identifier control.switchView.accessibilityIdentifier = prefKey + */ + if let accessibilityIdentifier { + control.accessibilityIdentifier = "\(accessibilityIdentifier)_switch" + } control.switchView.addTarget(self, action: #selector(switchValueChanged), for: .valueChanged) return control }() @@ -318,6 +355,9 @@ class BoolSetting: Setting, FeatureFlaggable { isEnabled: enabled ) + // Ecosia: Apply Switch theme when cell is configured (triggered on theme change) + control.switchView.applyTheme(theme: theme) + displayBool(control.switchView) if let title = title { if let status = status { @@ -867,6 +907,8 @@ class WithoutAccountSetting: AccountSetting { protocol SettingsDelegate: AnyObject { func settingsOpenURLInNewTab(_ url: URL) func didFinish() + // Ecosia: Reload after ntp customization changes + func reloadHomepage() } // The base settings view controller. diff --git a/firefox-ios/Client/Frontend/Settings/ThemeSettings/ThemeSettingsController.swift b/firefox-ios/Client/Frontend/Settings/ThemeSettings/ThemeSettingsController.swift index 8ea5bd6538982..13a77bffa6442 100644 --- a/firefox-ios/Client/Frontend/Settings/ThemeSettings/ThemeSettingsController.swift +++ b/firefox-ios/Client/Frontend/Settings/ThemeSettings/ThemeSettingsController.swift @@ -7,6 +7,7 @@ import Foundation import Redux import Shared import ComponentLibrary +import Ecosia class ThemeSettingsController: ThemedTableViewController, StoreSubscriber { typealias SubscriberStateType = ThemeSettingsState @@ -236,12 +237,19 @@ class ThemeSettingsController: ThemedTableViewController, StoreSubscriber { cell.textLabel?.numberOfLines = 0 cell.textLabel?.lineBreakMode = .byWordWrapping + /* Ecosia: Use EcosiaThemedSwitch let control = ThemedSwitch() control.applyTheme(theme: theme) + */ + let control = EcosiaThemedSwitch() control.accessibilityIdentifier = "SystemThemeSwitchValue" + /* Ecosia: No need for `onTintColor` with EcosiaThemedSwitch control.onTintColor = theme.colors.actionPrimary + */ control.addTarget(self, action: #selector(systemThemeSwitchValueChanged), for: .valueChanged) control.isOn = themeState.useSystemAppearance + // Ecosia: Apply theme only after `isOn` to use correct color + control.applyTheme(theme: theme) cell.accessoryView = control case .automaticBrightness: diff --git a/firefox-ios/Client/Frontend/Settings/WebsiteDataManagement/WebsiteDataManagementViewController.swift b/firefox-ios/Client/Frontend/Settings/WebsiteDataManagement/WebsiteDataManagementViewController.swift index 26c465cad7844..885d0b9cc8d9a 100644 --- a/firefox-ios/Client/Frontend/Settings/WebsiteDataManagement/WebsiteDataManagementViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/WebsiteDataManagement/WebsiteDataManagementViewController.swift @@ -5,6 +5,7 @@ import UIKit import Shared import Common +import Ecosia class WebsiteDataManagementViewController: UIViewController, UITableViewDataSource, @@ -255,6 +256,8 @@ class WebsiteDataManagementViewController: UIViewController, viewModel.showMoreButtonPressed() tableView.reloadData() case .clearButton: + // Ecosia: Track "Clear Private Data" button click + Analytics.shared.clearsDataFromSection(.websites) let generator = UIImpactFeedbackGenerator(style: .heavy) generator.impactOccurred() let alert = viewModel.createAlertToRemove() diff --git a/firefox-ios/Client/Frontend/Strings.swift b/firefox-ios/Client/Frontend/Strings.swift index 904ce1b2e3b2f..39ace6fdc1254 100644 --- a/firefox-ios/Client/Frontend/Strings.swift +++ b/firefox-ios/Client/Frontend/Strings.swift @@ -1001,12 +1001,12 @@ extension String { public static let PeaceOfMindTitle = MZLocalizedString( key: "DefaultBrowserCard.PeaceOfMind.Title.v108", tableName: "Default Browser", - value: "Firefox Has Privacy Covered", + value: "Ecosia Has Privacy Covered", comment: "Title for small home tab banner shown that allows the user to switch their default browser to Firefox.") public static let PeaceOfMindDescription = MZLocalizedString( key: "DefaultBrowserCard.PeaceOfMind.Description.v108", tableName: "Default Browser", - value: "Firefox blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind.", + value: "Ecosia blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind.", comment: "Description for small home tab banner shown that allows the user to switch their default browser to Firefox.") public static let BetterInternetTitle = MZLocalizedString( key: "DefaultBrowserCard.BetterInternet.Title.v108", @@ -1332,6 +1332,11 @@ extension String { /// Identifiers of all new strings should begin with `LibraryPanel.{PanelName}.` public struct LibraryPanel { public struct Sections { + public static let LastHour = MZLocalizedString( + key: "LibraryPanel.Sections.LastHour.v134", + tableName: "LibraryPanel", + value: "Last Hour", + comment: "This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour.") public static let Today = MZLocalizedString( key: "Today", tableName: nil, @@ -1930,7 +1935,7 @@ extension String { public static let SuggestSectionTitle = MZLocalizedString( key: "Search.SuggestSectionTitle.v102", tableName: nil, - value: "Firefox Suggest", + value: "Ecosia Suggest", comment: "When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions.") public static let SponsoredSuggestionDescription = MZLocalizedString( key: "Search.SponsoredSuggestionDescription.v119", @@ -2165,7 +2170,7 @@ extension String { public static let FxHomepageWallpaperButton = MZLocalizedString( key: "FxHomepage.Wallpaper.ButtonLabel.v99", tableName: nil, - value: "Firefox logo, change the wallpaper.", + value: "Ecosia logo, change the wallpaper.", comment: "On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper") public static let ToggleButton = MZLocalizedString( key: "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton", @@ -2180,27 +2185,27 @@ extension String { public static let FxAmethystWallpaper = MZLocalizedString( key: "Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99", tableName: nil, - value: "Firefox wallpaper, amethyst pattern.", + value: "Ecosia wallpaper, amethyst pattern.", comment: "In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper.") public static let FxSunriseWallpaper = MZLocalizedString( key: "Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99", tableName: nil, - value: "Firefox wallpaper, sunrise pattern.", + value: "Ecosia wallpaper, sunrise pattern.", comment: "In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper.") public static let FxCeruleanWallpaper = MZLocalizedString( key: "Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99", tableName: nil, - value: "Firefox wallpaper, cerulean pattern.", + value: "Ecosia wallpaper, cerulean pattern.", comment: "In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper.") public static let FxBeachHillsWallpaper = MZLocalizedString( key: "Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100", tableName: nil, - value: "Firefox wallpaper, beach hills pattern.", + value: "Ecosia wallpaper, beach hills pattern.", comment: "In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper.") public static let FxTwilightHillsWallpaper = MZLocalizedString( key: "Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100", tableName: nil, - value: "Firefox wallpaper, twilight hills pattern.", + value: "Ecosia wallpaper, twilight hills pattern.", comment: "In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper.") } } @@ -2782,7 +2787,7 @@ extension String { public static let SettingsDisconnectSyncAlertBody = MZLocalizedString( key: "Settings.Disconnect.Body", tableName: nil, - value: "Firefox will stop syncing with your account, but won’t delete any of your browsing data on this device.", + value: "Ecosia will stop syncing with your account, but won’t delete any of your browsing data on this device.", comment: "Body of the alert when prompting the user asking to disconnect.") public static let SettingsDisconnectSyncButton = MZLocalizedString( key: "Settings.Disconnect.Button", @@ -2899,7 +2904,7 @@ extension String { public static let EmptySyncedTabsPanelStateTitle = MZLocalizedString( key: "HistoryPanel.EmptySyncedTabsState.Title", tableName: nil, - value: "Firefox Sync", + value: "Ecosia Sync", comment: "Title for the empty synced tabs state in the History Panel") public static let EmptySyncedTabsPanelNotSignedInStateDescription = MZLocalizedString( key: "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description", @@ -2924,7 +2929,7 @@ extension String { public static let FirefoxHomePage = MZLocalizedString( key: "Firefox.HomePage.Title", tableName: nil, - value: "Firefox Home Page", + value: "Ecosia Home Page", comment: "Title for firefox about:home page in tab history list") public static let HistoryPanelDelete = MZLocalizedString( key: "Delete", @@ -3186,7 +3191,7 @@ extension String { public static let SettingsNewTabTopSites = MZLocalizedString( key: "Settings.NewTab.Option.FirefoxHome", tableName: nil, - value: "Firefox Home", + value: "Ecosia Home", comment: "Option in settings to show Firefox Home when you open a new tab") public static let SettingsNewTabBlankPage = MZLocalizedString( key: "Settings.NewTab.Option.BlankPage", @@ -3430,7 +3435,7 @@ extension String { key: "Settings.OfferClipboardBar.Status.v128", tableName: nil, value: "When opening %@", - comment: "Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name.") + comment: "Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349") } // MARK: - Link Previews @@ -3476,7 +3481,7 @@ extension String { public static let UnableToOpenURLError = MZLocalizedString( key: "OpenURL.Error.Message", tableName: nil, - value: "Firefox cannot open the page because it has an invalid address.", + value: "Ecosia cannot open the page because it has an invalid address.", comment: "The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app.") public static let UnableToOpenURLErrorTitle = MZLocalizedString( key: "OpenURL.Error.Title", @@ -3687,7 +3692,7 @@ extension String { public static let PhotoLibraryFirefoxWouldLikeAccessTitle = MZLocalizedString( key: "PhotoLibrary.FirefoxWouldLikeAccessTitle", tableName: nil, - value: "Firefox would like to access your Photos", + value: "Ecosia would like to access your Photos", comment: "See http://mzl.la/1G7uHo7") public static let PhotoLibraryFirefoxWouldLikeAccessMessage = MZLocalizedString( key: "PhotoLibrary.FirefoxWouldLikeAccessMessage", @@ -3703,7 +3708,7 @@ extension String { public static let SentTab_NoTabArrivingNotification_title = MZLocalizedString( key: "SentTab.NoTabArrivingNotification.title", tableName: nil, - value: "Firefox Sync", + value: "Ecosia Sync", comment: "Title of notification received after a spurious message from FxA has been received.") public static let SentTab_NoTabArrivingNotification_body = MZLocalizedString( @@ -3822,7 +3827,7 @@ extension String { public static let FxAPush_DeviceConnected_body = MZLocalizedString( key: "FxAPush_DeviceConnected_body", tableName: nil, - value: "Firefox Sync has connected to %@", + value: "Ecosia Sync has connected to %@", comment: "Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device.") } @@ -3895,6 +3900,14 @@ extension String { // MARK: - Main Menu extension String { public struct MainMenu { + public struct AccessibilityLabels { + public static let OptionDisabledHint = MZLocalizedString( + key: "MainMenu.AccessibilityLabels.OptionDisabled.Hint.v133", + tableName: "MainMenu", + value: "Dimmed", + comment: "On the main menu, the accessibility label hint for any action/option inside the menu, that is disabled. For example: 'Save to Reading List' option, from Menu, in some cases is disabled and the voice over should indicate that. 'Save To Reading List dimmed'") + } + public struct Account { public static let SignedOutTitle = MZLocalizedString( key: "MainMenu.Account.SignedOut.Title.v131", @@ -4011,14 +4024,14 @@ extension String { value: "Find in page", comment: "On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page.") public static let Tools = MZLocalizedString( - key: "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132", + key: "MainMenu.ToolsSection.AccessibilityLabels.Tools.v133", tableName: "MainMenu", - value: "Tools", + value: "Tools submenu", comment: "On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu.") public static let Save = MZLocalizedString( - key: "MainMenu.ToolsSection.AccessibilityLabels.Save.v132", + key: "MainMenu.ToolsSection.AccessibilityLabels.Save.v133", tableName: "MainMenu", - value: "Save", + value: "Save submenu", comment: "On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on.") } } @@ -4178,9 +4191,9 @@ extension String { value: "Share", comment: "On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool.") public static let ReportBrokenSite = MZLocalizedString( - key: "MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131", + key: "MainMenu.Submenus.Tools.ReportBrokenSite.Title.v133", tableName: "MainMenu", - value: "Report Broken Site", + value: "Report Broken Site…", comment: "On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team.") public static let ReportBrokenSiteSubtitle = MZLocalizedString( key: "MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131", @@ -5218,7 +5231,7 @@ extension String { public static let SettingsStudiesToggleMessage = MZLocalizedString( key: "Settings.Studies.Toggle.Message", tableName: nil, - value: "Firefox may install and run studies from time to time.", + value: "Ecosia may install and run studies from time to time.", comment: "A short description that explains that Mozilla is running studies") } @@ -5307,12 +5320,12 @@ extension String { public static let ShareOpenInFirefox = MZLocalizedString( key: "ShareExtension.OpenInFirefoxAction.Title", tableName: nil, - value: "Open in Firefox", + value: "Open in Ecosia", comment: "Action label on share extension to immediately open page in Firefox.") public static let ShareSearchInFirefox = MZLocalizedString( key: "ShareExtension.SeachInFirefoxAction.Title", tableName: nil, - value: "Search in Firefox", + value: "Search in Ecosia", comment: "Action label on share extension to search for the selected text in Firefox.") public static let ShareLoadInBackground = MZLocalizedString( @@ -5323,7 +5336,7 @@ extension String { public static let ShareLoadInBackgroundDone = MZLocalizedString( key: "ShareExtension.LoadInBackgroundActionDone.Title", tableName: nil, - value: "Loading in Firefox", + value: "Loading in Ecosia", comment: "Share extension label shown after user has performed 'Load in Background' action.") } @@ -5874,7 +5887,7 @@ extension String { public static let QuickActionsGalleryTitlev2 = MZLocalizedString( key: "TodayWidget.QuickActionsGalleryTitleV2", tableName: "Today", - value: "Firefox Shortcuts", + value: "Ecosia Shortcuts", comment: "Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox.") // Quick Action - Medium Size Quick Action @@ -5910,7 +5923,7 @@ extension String { public static let SearchInFirefoxV2 = MZLocalizedString( key: "TodayWidget.SearchInFirefoxV2", tableName: "Today", - value: "Search in\nFirefox", + value: "Search in\nEcosia", comment: "Search in Firefox. Do not translate the word Firefox") public static let ClosePrivateTabsLabelV2 = MZLocalizedString( key: "TodayWidget.ClosePrivateTabsLabelV2", @@ -5951,7 +5964,7 @@ extension String { public static let OpenFirefoxLabel = MZLocalizedString( key: "TodayWidget.OpenFirefoxLabel", tableName: "Today", - value: "Open Firefox", + value: "Open Ecosia", comment: "Open Firefox when there are no tabs opened in tab tray i.e. Empty State") public static let NoOpenTabsLabel = MZLocalizedString( key: "TodayWidget.NoOpenTabsLabel", @@ -5997,7 +6010,7 @@ extension String { public static let DefaultBrowserOnboardingDescriptionStep3 = MZLocalizedString( key: "DefaultBrowserOnboarding.Description3", tableName: "Default Browser", - value: "3. Select Firefox", + value: "3. Select Ecosia", comment: "Description for default browser onboarding card.") public static let DefaultBrowserOnboardingButton = MZLocalizedString( key: "DefaultBrowserOnboarding.Button", @@ -7072,7 +7085,7 @@ extension String { public static let MenuHelperSearchWithFirefox = MZLocalizedString( key: "UIMenuItem.SearchWithFirefox", tableName: nil, - value: "Search with Firefox", + value: "Search with Ecosia", comment: "Search in New Tab Text selection menu item") } @@ -7447,6 +7460,13 @@ extension String { value: "Selected", comment: "After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected.") } + struct v131 { + public static let ReportBrokenSite = MZLocalizedString( + key: "MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131", + tableName: "MainMenu", + value: "Report Broken Site", + comment: "On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team.") + } struct v132 { public static let TabTrayToggleAccessibilityLabel = MZLocalizedString( key: "Private Mode", @@ -7465,6 +7485,16 @@ extension String { tableName: "AddressToolbar", value: "Search or enter address", comment: "Accessibility label for the address field in the address toolbar.") + public static let Tools = MZLocalizedString( + key: "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132", + tableName: "MainMenu", + value: "Tools", + comment: "On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu.") + public static let Save = MZLocalizedString( + key: "MainMenu.ToolsSection.AccessibilityLabels.Save.v132", + tableName: "MainMenu", + value: "Save", + comment: "On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on.") } } } diff --git a/firefox-ios/Client/Frontend/TabContentsScripts/UserScriptManager.swift b/firefox-ios/Client/Frontend/TabContentsScripts/UserScriptManager.swift index 40156694811da..1800eb498d00f 100644 --- a/firefox-ios/Client/Frontend/TabContentsScripts/UserScriptManager.swift +++ b/firefox-ios/Client/Frontend/TabContentsScripts/UserScriptManager.swift @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import WebKit +import Ecosia class UserScriptManager: FeatureFlaggable { // Scripts can use this to verify the *app* (not JS on the web) is calling into them. diff --git a/firefox-ios/Client/Frontend/Theme/ThemedTableViewCells/ThemedTableViewCell.swift b/firefox-ios/Client/Frontend/Theme/ThemedTableViewCells/ThemedTableViewCell.swift index 2fdefd84493a8..c1913dc29befe 100644 --- a/firefox-ios/Client/Frontend/Theme/ThemedTableViewCells/ThemedTableViewCell.swift +++ b/firefox-ios/Client/Frontend/Theme/ThemedTableViewCells/ThemedTableViewCell.swift @@ -13,6 +13,8 @@ class ThemedTableViewCell: UITableViewCell, ReusableCell, ThemeApplicable { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { self.cellStyle = style super.init(style: style, reuseIdentifier: reuseIdentifier) + // Ecosia: adjust layout margins + contentView.directionalLayoutMargins.leading = 16 } required init?(coder aDecoder: NSCoder) { @@ -73,7 +75,9 @@ class ThemedTableViewCell: UITableViewCell, ReusableCell, ThemeApplicable { // Take view model color if it exists, otherwise fallback to default colors textLabel?.textColor = viewModel?.textColor ?? theme.colors.textPrimary detailTextLabel?.textColor = viewModel?.detailTextColor ?? theme.colors.textSecondary - backgroundColor = viewModel?.backgroundColor ?? theme.colors.layer5 + // Ecosia: Update background color + // backgroundColor = viewModel?.backgroundColor ?? theme.colors.layer5 + backgroundColor = viewModel?.backgroundColor ?? theme.colors.ecosia.backgroundElevation1 tintColor = viewModel?.tintColor ?? theme.colors.actionPrimary } diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/LockButton.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/LockButton.swift index c8789a88378c1..03bbd5a7dd1c7 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/LockButton.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/LockButton.swift @@ -40,6 +40,8 @@ class LockButton: UIButton { setImage(UIImage.templateImageNamed(StandardImageIdentifiers.Large.lock), for: .normal) imageView?.contentMode = .scaleAspectFill configuration = .plain() + // Ecosia: Remove trailing image insets + configuration?.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 0) } required init?(coder aDecoder: NSCoder) { @@ -52,7 +54,10 @@ extension LockButton: ThemeApplicable { func applyTheme(theme: Theme) { selectedTintColor = theme.colors.actionPrimary disabledTintColor = theme.colors.iconDisabled + /* Ecosia: Set same tint as selected state unselectedTintColor = theme.colors.textPrimary + */ + unselectedTintColor = theme.colors.actionPrimary tintColor = isEnabled ? unselectedTintColor : disabledTintColor } } diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/OverlayModeManager.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/OverlayModeManager.swift index 5e12d78795a16..b0c8a51ff2d7b 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/OverlayModeManager.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/OverlayModeManager.swift @@ -44,6 +44,8 @@ protocol OverlayModeManager: OverlayStateProtocol { } class DefaultOverlayModeManager: OverlayModeManager { + // Ecosia: Add override to manage the overlay mode in case of other pages being shown + var overrideShouldEnterOverlayMode: Bool? private var urlBarView: URLBarViewProtocol? var inOverlayMode: Bool { @@ -82,10 +84,20 @@ class DefaultOverlayModeManager: OverlayModeManager { } private func shouldEnterOverlay(for url: URL?, newTabSettings: NewTabPage) -> Bool { + // Ecosia: Add override to manage the overlay mode in case of other pages being shown + guard overrideShouldEnterOverlayMode == nil else { + return overrideShouldEnterOverlayMode! + } // The NewTabPage cases are weird topSites = homepage // and homepage = customURL switch newTabSettings { - case .topSites: return url?.isFxHomeUrl ?? true + /* + Ecosia: Given the upper comment, we'll return `false` for `topSites` + so that it won't go in overlay mode as soon as we open a new tab + and present the New Tab Page. + */ + // case .topSites: return url?.isFxHomeUrl ?? true + case .topSites: return false case .blankPage: return true case .homePage: return false } diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationContainerView.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationContainerView.swift index 53cba4f88b09e..663a4b0a1615d 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationContainerView.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationContainerView.swift @@ -8,7 +8,9 @@ import Foundation // This subclass creates a strong shadow on the URLBar class TabLocationContainerView: UIView { private struct LocationContainerUX { - static let CornerRadius: CGFloat = 8 + // Ecosia: Update `CornerRadius` + // static let CornerRadius: CGFloat = 8 + static let CornerRadius: CGFloat = 22 } override init(frame: CGRect) { diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationView.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationView.swift index 1aeeb2f15d344..b6663818fe227 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationView.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabLocationView.swift @@ -116,6 +116,7 @@ class TabLocationView: UIView, FeatureFlaggable { trackingProtectionButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.trackingProtection trackingProtectionButton.showsLargeContentViewer = true trackingProtectionButton.largeContentImage = .templateImageNamed(StandardImageIdentifiers.Large.lock) + trackingProtectionButton.imageView?.contentMode = .scaleAspectFit trackingProtectionButton.largeContentTitle = .TabLocationLockButtonLargeContentTitle trackingProtectionButton.accessibilityLabel = .TabLocationLockButtonAccessibilityLabel } @@ -370,11 +371,10 @@ class TabLocationView: UIView, FeatureFlaggable { } func showTrackingProtectionButton(for url: URL?) { - guard let url else { return } ensureMainThread { let isValidHttpUrlProtocol = self.isValidHttpUrlProtocol(url) - let isReaderModeURL = url.isReaderModeURL - let isFxHomeUrl = url.isFxHomeUrl + let isReaderModeURL = url?.isReaderModeURL ?? false + let isFxHomeUrl = url?.isFxHomeUrl ?? false if !isFxHomeUrl, !isReaderModeURL, isValidHttpUrlProtocol, self.trackingProtectionButton.isHidden { self.trackingProtectionButton.transform = UX.trackingProtectionxOffset self.trackingProtectionButton.alpha = 0 @@ -526,9 +526,11 @@ extension TabLocationView: ThemeApplicable { func applyTheme(theme: Theme) { setURLTextfieldPlaceholder(theme: theme) urlTextField.textColor = theme.colors.textPrimary + readerModeButton.applyTheme(theme: theme) trackingProtectionButton.applyTheme(theme: theme) reloadButton.applyTheme(theme: theme) + shoppingButton.tintColor = theme.colors.textPrimary shoppingButton.setImage(UIImage(named: StandardImageIdentifiers.Large.shopping)? .withTintColor(theme.colors.actionPrimary), diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbar.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbar.swift index e01e845b4470a..10bb2df02786e 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbar.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbar.swift @@ -5,6 +5,7 @@ import Common import UIKit import Shared +import Ecosia class TabToolbar: UIView, SearchBarLocationProvider { // MARK: - Variables @@ -12,7 +13,10 @@ class TabToolbar: UIView, SearchBarLocationProvider { weak var tabToolbarDelegate: TabToolbarDelegate? let tabsButton = TabsButton() + /* Ecosia: Change `addNewTabButton` to configurable CircleButton let addNewTabButton = ToolbarButton() + */ + let circleButton = CircleButton(config: .search) let appMenuButton = ToolbarButton() let bookmarksButton = ToolbarButton() let forwardButton = ToolbarButton() @@ -32,7 +36,9 @@ class TabToolbar: UIView, SearchBarLocationProvider { // MARK: - Initializers override private init(frame: CGRect) { - actionButtons = [backButton, forwardButton, multiStateButton, addNewTabButton, tabsButton, appMenuButton] + // Ecosia: Update toolbar search button + // actionButtons = [backButton, forwardButton, multiStateButton, circleButton, tabsButton, appMenuButton] + actionButtons = [backButton, forwardButton, circleButton, tabsButton, appMenuButton] super.init(frame: frame) setupAccessibility() @@ -40,7 +46,8 @@ class TabToolbar: UIView, SearchBarLocationProvider { helper = TabToolbarHelper(toolbar: self) addButtons(actionButtons) - privateModeBadge.add(toParent: contentView) + // Ecosia: Remove private mode badge + // privateModeBadge.add(toParent: contentView) warningMenuBadge.add(toParent: contentView) contentView.axis = .horizontal @@ -55,7 +62,8 @@ class TabToolbar: UIView, SearchBarLocationProvider { // MARK: - View Setup override func updateConstraints() { - privateModeBadge.layout(onButton: tabsButton) + // Ecosia: Remove private mode badge + // privateModeBadge.layout(onButton: tabsButton) warningMenuBadge.layout(onButton: appMenuButton) NSLayoutConstraint.activate([ @@ -71,7 +79,7 @@ class TabToolbar: UIView, SearchBarLocationProvider { backButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.backButton forwardButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.forwardButton tabsButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.tabsButton - addNewTabButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.addNewTabButton + circleButton.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.TabToolbar.circleButton appMenuButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.settingsMenuButton accessibilityNavigationStyle = .combined accessibilityLabel = .TabToolbarNavigationToolbarAccessibilityLabel @@ -103,7 +111,8 @@ class TabToolbar: UIView, SearchBarLocationProvider { // MARK: - TabToolbarProtocol extension TabToolbar: TabToolbarProtocol { func privateModeBadge(visible: Bool) { - privateModeBadge.show(visible) + // Ecosia: Remove private mode badge + // privateModeBadge.show(visible) } func warningMenuBadge(setVisible: Bool) { @@ -138,14 +147,20 @@ extension TabToolbar: TabToolbarProtocol { // MARK: - Theme protocols extension TabToolbar: ThemeApplicable, PrivateModeUI { func applyTheme(theme: Theme) { + /* Ecosia: Update theme colors backgroundColor = theme.colors.layer1 + */ + backgroundColor = theme.colors.ecosia.backgroundPrimary + actionButtons.forEach { $0.applyTheme(theme: theme) } + // Ecosia: Badges are not used privateModeBadge.badge.tintBackground(color: theme.colors.layer1) warningMenuBadge.badge.tintBackground(color: theme.colors.layer1) } func applyUIMode(isPrivate: Bool, theme: Theme) { - privateModeBadge(visible: isPrivate) + // Ecosia: Remove private mode badge + // privateModeBadge(visible: isPrivate) } } diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift index 9924796fd05d5..17116e615c4ec 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift @@ -5,11 +5,14 @@ import UIKit import Shared import Common +import Ecosia protocol TabToolbarProtocol: AnyObject { var tabToolbarDelegate: TabToolbarDelegate? { get set } - + /* Ecosia: Change `addNewTabButton` to configurable CircleButton var addNewTabButton: ToolbarButton { get } + */ + var circleButton: CircleButton { get } var tabsButton: TabsButton { get } var appMenuButton: ToolbarButton { get } var bookmarksButton: ToolbarButton { get } @@ -53,9 +56,15 @@ enum MiddleButtonState { @objcMembers open class TabToolbarHelper: NSObject { let toolbar: TabToolbarProtocol + /* Ecosia: Updates images in batch. + Some of them are the same but easier to solve when rebasing/merging let ImageSearch = UIImage.templateImageNamed(StandardImageIdentifiers.Large.search) let ImageNewTab = UIImage.templateImageNamed(StandardImageIdentifiers.Large.plus) let ImageHome = UIImage.templateImageNamed(StandardImageIdentifiers.Large.home) + */ + let ImageSearch = UIImage.templateImageNamed("searchUrl") + let ImageNewTab = UIImage.templateImageNamed("nav-add") + let ImageHome = UIImage.templateImageNamed("menu-Home") let ImageBookmark = UIImage.templateImageNamed(StandardImageIdentifiers.Large.bookmarkTrayFill) let ImageFire = UIImage.templateImageNamed(StandardImageIdentifiers.Large.dataClearance) @@ -143,12 +152,17 @@ open class TabToolbarHelper: NSObject { toolbar.tabsButton.largeContentTitle = .TabsButtonShowTabsAccessibilityLabel longPressGestureRecognizers.append(longPressGestureTabsButton) + /* Ecosia: Change `addNewTabButton` to configurable CircleButton toolbar.addNewTabButton.setImage(UIImage.templateImageNamed(StandardImageIdentifiers.Large.plus), for: .normal) toolbar.addNewTabButton.accessibilityLabel = .AddTabAccessibilityLabel toolbar.addNewTabButton.showsLargeContentViewer = true toolbar.addNewTabButton.largeContentTitle = .AddTabAccessibilityLabel toolbar.addNewTabButton.addTarget(self, action: #selector(didClickAddNewTab), for: .touchUpInside) toolbar.addNewTabButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.addNewTabButton + */ + toolbar.circleButton.accessibilityLabel = .AddTabAccessibilityLabel + toolbar.circleButton.addTarget(self, action: #selector(didClickCircleButton), for: .touchUpInside) + toolbar.circleButton.accessibilityIdentifier = EcosiaAccessibilityIdentifiers.TabToolbar.circleButton let appMenuImage = UIImage.templateImageNamed(StandardImageIdentifiers.Large.appMenu) toolbar.appMenuButton.contentMode = .center @@ -233,10 +247,12 @@ open class TabToolbarHelper: NSObject { toolbar.tabToolbarDelegate?.tabToolbarDidPressBookmarks(toolbar, button: toolbar.appMenuButton) } + /* Ecosia: Change `addNewTabButton` to configurable CircleButton func didClickAddNewTab() { TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .addNewTabButton) toolbar.tabToolbarDelegate?.tabToolbarDidPressAddNewTab(toolbar, button: toolbar.addNewTabButton) } + */ func didPressMultiStateButton() { switch middleButtonState { @@ -249,6 +265,18 @@ open class TabToolbarHelper: NSObject { toolbar.tabToolbarDelegate?.tabToolbarDidPressDataClearance(toolbar, button: toolbar.multiStateButton) } } + + // Ecosia: Change addNewTabButton to configurable CircleButton + func didClickCircleButton() { + switch toolbar.circleButton.config { + case .search: + TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .startSearchButton) + toolbar.tabToolbarDelegate?.tabToolbarDidPressSearch(toolbar, button: toolbar.circleButton) + case .newTab: + TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .addNewTabButton) + toolbar.tabToolbarDelegate?.tabToolbarDidPressAddNewTab(toolbar, button: toolbar.circleButton) + } + } } extension TabToolbarHelper: UIGestureRecognizerDelegate { diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/ToolbarButton.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/ToolbarButton.swift index 9d561646f5aba..2f5a66ffc54de 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/ToolbarButton.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/ToolbarButton.swift @@ -6,11 +6,18 @@ import Common import UIKit import Shared +/* Ecosia: Update ToolbarButton to allow ThemeApplicable + functions to be overridden class ToolbarButton: UIButton { + */ +class ToolbarButton: UIButton, ThemeApplicable { // MARK: - Variables - private var selectedTintColor: UIColor! - private var unselectedTintColor: UIColor! + // Ecosia: Modify accessors + // private var selectedTintColor: UIColor! + // private var unselectedTintColor: UIColor! + var selectedTintColor: UIColor! + var unselectedTintColor: UIColor! private var disabledTintColor: UIColor! // Optionally can associate a separator line that hide/shows along with the button @@ -53,6 +60,8 @@ class ToolbarButton: UIButton { required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } +/* Ecosia: Update ToolbarButton to allow ThemeApplicable + functions to be overridden } // MARK: - Theme protocols @@ -66,3 +75,19 @@ extension ToolbarButton: ThemeApplicable { imageView?.tintColor = tintColor } } +*/ + // MARK: - Theme protocols + + func applyTheme(theme: Theme) { + /* Ecosia: Update colors + selectedTintColor = theme.colors.actionPrimary + disabledTintColor = theme.colors.iconDisabled + */ + selectedTintColor = theme.colors.ecosia.brandPrimary + disabledTintColor = UIColor.Photon.Grey30 + unselectedTintColor = theme.colors.iconPrimary + tintColor = isEnabled ? unselectedTintColor : disabledTintColor + // Ecosia: Unneded as set in didSet{} accessor + // imageView?.tintColor = tintColor + } +} diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift index c7da00bc40974..53f0542016acd 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift @@ -6,6 +6,7 @@ import Common import Shared import SnapKit import UIKit +import Ecosia private struct URLBarViewUX { static let LocationLeftPadding: CGFloat = 8 @@ -13,11 +14,17 @@ private struct URLBarViewUX { static let LocationHeight: CGFloat = 40 static let ButtonHeight: CGFloat = 44 static let LocationContentOffset: CGFloat = 8 - static let TextFieldCornerRadius: CGFloat = 8 + // Ecosia: Update Corner Radius + // static let TextFieldCornerRadius: CGFloat = 8 + static let TextFieldCornerRadius: CGFloat = 22 static let TextFieldBorderWidth: CGFloat = 0 - static let TextFieldBorderWidthSelected: CGFloat = 4 + // Ecosia: Update Border width in selected state + // static let TextFieldBorderWidthSelected: CGFloat = 4 + static let TextFieldBorderWidthSelected: CGFloat = 2 static let ProgressBarHeight: CGFloat = 3 - static let SearchIconImageWidth: CGFloat = 30 + // Ecosia: Update search icon size + static let SearchIconWidthSmall: CGFloat = 16 + static let SearchIconWidthMedium: CGFloat = 24 static let TabsButtonRotationOffset: CGFloat = 1.5 static let TabsButtonHeight: CGFloat = 18.0 static let ToolbarButtonInsets = UIEdgeInsets(equalInset: Padding) @@ -114,11 +121,21 @@ class URLBarView: UIView, /// and the Cancel button is visible (allowing the user to leave overlay mode). var inOverlayMode = false + // Ecosia: Helper flags + var isPrivate = false + var isHome: Bool { + guard let url = currentURL else { return true } + return InternalURL(url)?.isAboutHomeURL == true + } + lazy var locationView: TabLocationView = { let locationView = TabLocationView(windowUUID: windowUUID) locationView.layer.cornerRadius = URLBarViewUX.TextFieldCornerRadius locationView.translatesAutoresizingMaskIntoConstraints = false locationView.delegate = self + // Ecosia: Update LocationView properties + locationView.layer.masksToBounds = true + locationView.clipsToBounds = true return locationView }() @@ -164,8 +181,12 @@ class URLBarView: UIView, fileprivate lazy var showQRScannerButton: InsetButton = { let button = InsetButton() + /* Ecosia: Update QR Code image let qrCodeImage = UIImage.templateImageNamed(StandardImageIdentifiers.Large.qrCode) button.setImage(qrCodeImage, for: .normal) + */ + let qrCodeImage = UIImage.templateImageNamed("menu-ScanQRCode") + button.setImage(qrCodeImage, for: .normal) button.accessibilityIdentifier = AccessibilityIdentifiers.Browser.UrlBar.scanQRCodeButton button.accessibilityLabel = .ScanQRCodeViewTitle button.showsLargeContentViewer = true @@ -200,7 +221,10 @@ class URLBarView: UIView, var appMenuButton = ToolbarButton() var bookmarksButton = ToolbarButton() + /* Ecosia: Change `addNewTabButton` to configurable CircleButton var addNewTabButton = ToolbarButton() + */ + var circleButton = CircleButton(config: .search) var forwardButton = ToolbarButton() var multiStateButton = ToolbarButton() @@ -214,7 +238,7 @@ class URLBarView: UIView, self.tabsButton, self.bookmarksButton, self.appMenuButton, - self.addNewTabButton, + self.circleButton, self.forwardButton, self.backButton, self.multiStateButton] @@ -225,17 +249,31 @@ class URLBarView: UIView, } set(newURL) { + /* Ecosia: Change setter to update URL accordingly locationView.url = newURL + */ + var updatedUrl = newURL + // Ecosia: Ecosify if needed + if updatedUrl?.shouldEcosify() ?? false { + updatedUrl = newURL?.ecosified(isIncognitoEnabled: isPrivate) + } + locationView.url = updatedUrl + // Ecosia: Update constraints if needed + if !inOverlayMode { + setNeedsUpdateConstraints() + } } } var profile: Profile let windowUUID: WindowUUID + /* Ecosia: Remove private mode badge fileprivate lazy var privateModeBadge = BadgeWithBackdrop( imageName: StandardImageIdentifiers.Medium.privateModeCircleFillPurple, isPrivateBadge: true ) + */ fileprivate let warningMenuBadge = BadgeWithBackdrop( imageName: StandardImageIdentifiers.Large.warningFill, @@ -264,6 +302,9 @@ class URLBarView: UIView, extras: [TelemetryWrapper.EventExtraKey.recordSearchEngineID.rawValue: engineID] ) + // Ecosia: Do not update if private mode enabled + guard isPrivate == false else { return } + self.searchIconImageView.image = profile.searchEnginesManager.defaultEngine?.image self.searchIconImageView.largeContentTitle = profile.searchEnginesManager.defaultEngine?.shortName self.searchIconImageView.largeContentImage = nil @@ -281,7 +322,10 @@ class URLBarView: UIView, showQRScannerButton, bookmarksButton, appMenuButton, + /* Ecosia: Change `addNewTabButton` to configurable CircleButton addNewTabButton, + */ + circleButton, forwardButton, backButton, multiStateButton, @@ -291,9 +335,15 @@ class URLBarView: UIView, addSubview($0) } + // Ecosia: ProgressBar sits at the back of the LocationView + locationView.addSubview(progressBar) + locationView.sendSubviewToBack(progressBar) + profile.searchEnginesManager.delegate = self + /* Ecosia: Remove private mode badge privateModeBadge.add(toParent: self) + */ warningMenuBadge.add(toParent: self) helper = TabToolbarHelper(toolbar: self) @@ -309,6 +359,11 @@ class URLBarView: UIView, make.left.right.equalTo(locationContainer) } + // Ecosia: ProgressBar fills the entire URL Bar + progressBar.snp.makeConstraints { make in + make.edges.equalTo(locationView) + } + locationView.snp.makeConstraints { make in make.edges.equalTo(self.locationContainer) } @@ -332,16 +387,23 @@ class URLBarView: UIView, } searchIconImageView.snp.remakeConstraints { make in + /* Ecosia: Update search icon constraints let heightMin = URLBarViewUX.LocationHeight + (URLBarViewUX.TextFieldBorderWidthSelected * 2) make.height.greaterThanOrEqualTo(heightMin) make.centerY.equalTo(self) make.leading.equalTo(self.cancelButton.snp.trailing).offset(URLBarViewUX.LocationLeftPadding) make.width.equalTo(URLBarViewUX.SearchIconImageWidth) + */ + make.centerY.equalTo(self) + make.leading.equalTo(self.locationContainer.snp.leading).offset(12) + make.size.equalTo(URLBarViewUX.SearchIconWidthSmall) } multiStateButton.snp.makeConstraints { make in make.leading.equalTo(self.forwardButton.snp.trailing) - make.centerY.equalTo(self) + // Ecosia: Update center Y constraint + // make.centerY.equalTo(self) + make.centerY.equalTo(self.locationContainer) make.size.equalTo(URLBarViewUX.ButtonHeight) } @@ -357,7 +419,7 @@ class URLBarView: UIView, make.size.equalTo(URLBarViewUX.ButtonHeight) } - addNewTabButton.snp.makeConstraints { make in + circleButton.snp.makeConstraints { make in make.trailing.equalTo(self.tabsButton.snp.leading) make.centerY.equalTo(self) make.size.equalTo(URLBarViewUX.ButtonHeight) @@ -375,7 +437,9 @@ class URLBarView: UIView, make.size.equalTo(URLBarViewUX.ButtonHeight) } + /* Ecosia: Remove private mode badge privateModeBadge.layout(onButton: tabsButton) + */ warningMenuBadge.layout(onButton: appMenuButton) } @@ -393,6 +457,7 @@ class URLBarView: UIView, make.height.equalTo(URLBarViewUX.urlBarLineHeight) } + /* Ecosia: custom progress bar progressBar.snp.remakeConstraints { make in if isBottomSearchBar { make.bottom.equalTo(snp.top).inset(URLBarViewUX.ProgressBarHeight / 2) @@ -403,9 +468,12 @@ class URLBarView: UIView, make.height.equalTo(URLBarViewUX.ProgressBarHeight) make.left.right.equalTo(self) } + */ + /* Ecosia: Update Overlay / Standard constraints if inOverlayMode { - searchIconImageView.alpha = 1 + // Ecosia: Customise alpha + // searchIconImageView.alpha = 1 // In overlay mode, we always show the location view full width self.locationContainer.layer.borderWidth = URLBarViewUX.TextFieldBorderWidthSelected self.locationContainer.snp.remakeConstraints { make in @@ -434,7 +502,8 @@ class URLBarView: UIView, ) } } else { - searchIconImageView.alpha = 0 + // Ecosia: Customise alpha + // searchIconImageView.alpha = 0 self.locationContainer.snp.remakeConstraints { make in if self.toolbarIsShowing { // If we are showing a toolbar, show the text field next to the forward button @@ -442,7 +511,7 @@ class URLBarView: UIView, if self.topTabsIsShowing { make.trailing.equalTo(self.bookmarksButton.snp.leading).offset(-URLBarViewUX.Padding) } else { - make.trailing.equalTo(self.addNewTabButton.snp.leading).offset(-URLBarViewUX.Padding) + make.trailing.equalTo(self.circleButton.snp.leading).offset(-URLBarViewUX.Padding) } } else { // Otherwise, left align the location view @@ -467,6 +536,69 @@ class URLBarView: UIView, ) } } + */ + if inOverlayMode { + // In overlay mode, we always show the location view full width + self.locationContainer.layer.borderWidth = URLBarViewUX.TextFieldBorderWidthSelected + self.locationContainer.snp.remakeConstraints { make in + let height = URLBarViewUX.LocationHeight + (URLBarViewUX.TextFieldBorderWidthSelected * 2) + make.height.equalTo(height) + make.trailing.equalTo(self.showQRScannerButton.snp.leading).offset(-URLBarViewUX.Padding) + make.leading.equalTo(self.cancelButton.snp.trailing) + make.centerY.equalTo(self) + } + self.locationView.snp.remakeConstraints { make in + make.top.bottom.trailing.equalTo(self.locationContainer).inset(UIEdgeInsets(equalInset: URLBarViewUX.TextFieldBorderWidthSelected)) + make.leading.equalTo(self.searchIconImageView.snp.trailing) + } + self.locationTextField?.snp.remakeConstraints { make in + make.edges.equalTo(self.locationView).inset(UIEdgeInsets(top: 0, left: URLBarViewUX.LocationLeftPadding, bottom: 0, right: URLBarViewUX.LocationLeftPadding)) + } + } else { + self.locationContainer.snp.remakeConstraints { make in + if self.toolbarIsShowing { + // If we are showing a toolbar, show the text field next to the forward button + make.leading.equalTo(self.forwardButton.snp.trailing).offset(URLBarViewUX.Padding) + if topTabsIsShowing { + make.trailing.equalTo(self.bookmarksButton.snp.leading).offset(-URLBarViewUX.Padding) + } else { + make.trailing.equalTo(self.circleButton.snp.leading).offset(-URLBarViewUX.Padding) + } + } else { + // Otherwise, left align the location view + make.leading.trailing.equalTo(self).inset(UIEdgeInsets(top: 0, left: URLBarViewUX.LocationLeftPadding-1, bottom: 0, right: URLBarViewUX.LocationLeftPadding-1)) + } + make.height.greaterThanOrEqualTo(URLBarViewUX.LocationHeight+2) + make.centerY.equalTo(self) + } + + self.locationView.snp.remakeConstraints { make in + make.top.bottom.trailing.equalTo(self.locationContainer) + if isHome { + make.leading.equalTo(searchIconImageView.snp.trailing).offset(8) + } else { + make.leading.equalTo(self.locationContainer) + } + } + + self.multiStateButton.snp.remakeConstraints { make in + if self.toolbarIsShowing { + make.trailing.equalTo(self.circleButton.snp.leading) + } else { + make.trailing.equalTo(self.safeArea.trailing) + } + make.centerY.equalTo(self.locationContainer) + make.size.equalTo(URLBarViewUX.ButtonHeight).priority(.high) + } + } + + let width = inOverlayMode ? URLBarViewUX.SearchIconWidthMedium : URLBarViewUX.SearchIconWidthSmall + searchIconImageView.snp.updateConstraints { make in + make.size.equalTo(width) + } + + // Ecosia: Update search engine image after constraints update + updateSearchEngineImage() } @objc @@ -521,7 +653,9 @@ class URLBarView: UIView, func updateProgressBar(_ progress: Float) { progressBar.alpha = 1 - progressBar.isHidden = false + // Ecosia: Do not show loading state when user has selected the url bar and is potentially typing + // progressBar.isHidden = false + progressBar.isHidden = inOverlayMode progressBar.setProgress(progress, animated: !isTransitioning) } @@ -612,7 +746,7 @@ class URLBarView: UIView, cancelButton.isHidden = false showQRScannerButton.isHidden = false progressBar.isHidden = false - addNewTabButton.isHidden = !toolbarIsShowing || topTabsIsShowing + circleButton.isHidden = !toolbarIsShowing || topTabsIsShowing appMenuButton.isHidden = !toolbarIsShowing bookmarksButton.isHidden = !toolbarIsShowing || !topTabsIsShowing forwardButton.isHidden = !toolbarIsShowing @@ -629,10 +763,12 @@ class URLBarView: UIView, tabsButton.alpha = inOverlayMode ? 0 : 1 appMenuButton.alpha = inOverlayMode ? 0 : 1 bookmarksButton.alpha = inOverlayMode ? 0 : 1 - addNewTabButton.alpha = inOverlayMode ? 0 : 1 + circleButton.alpha = inOverlayMode ? 0 : 1 forwardButton.alpha = inOverlayMode ? 0 : 1 backButton.alpha = inOverlayMode ? 0 : 1 - multiStateButton.alpha = inOverlayMode ? 0 : 1 + // Ecosia: always hide multi state button + // multiStateButton.alpha = inOverlayMode ? 0 : 1 + multiStateButton.alpha = 0 let borderColor = inOverlayMode ? locationActiveBorderColor : locationBorderColor locationContainer.layer.borderColor = borderColor.cgColor @@ -657,7 +793,7 @@ class URLBarView: UIView, cancelButton.isHidden = !inOverlayMode showQRScannerButton.isHidden = !inOverlayMode progressBar.isHidden = inOverlayMode - addNewTabButton.isHidden = !toolbarIsShowing || topTabsIsShowing || inOverlayMode + circleButton.isHidden = !toolbarIsShowing || topTabsIsShowing || inOverlayMode appMenuButton.isHidden = !toolbarIsShowing || inOverlayMode bookmarksButton.isHidden = !toolbarIsShowing || inOverlayMode || !topTabsIsShowing forwardButton.isHidden = !toolbarIsShowing || inOverlayMode @@ -666,7 +802,10 @@ class URLBarView: UIView, multiStateButton.isHidden = !toolbarIsShowing || inOverlayMode // badge isHidden is tied to private mode on/off, use alpha to hide in this case + /* Ecosia: Remove private mode badge [privateModeBadge, warningMenuBadge].forEach { + */ + [warningMenuBadge].forEach { $0.badge.alpha = (!toolbarIsShowing || inOverlayMode) ? 0 : 1 $0.backdrop.alpha = (!toolbarIsShowing || inOverlayMode) ? 0 : BadgeWithBackdrop.UX.backdropAlpha } @@ -714,9 +853,11 @@ class URLBarView: UIView, extension URLBarView: TabToolbarProtocol { func privateModeBadge(visible: Bool) { + /* Ecosia: Remove private mode badge if UIDevice.current.userInterfaceIdiom != .pad { privateModeBadge.show(visible) } + */ } func warningMenuBadge(setVisible: Bool) { @@ -761,7 +902,10 @@ extension URLBarView: TabToolbarProtocol { tabsButton, bookmarksButton, appMenuButton, + /* Ecosia: Change `addNewTabButton` to configurable CircleButton addNewTabButton, + */ + circleButton, progressBar ] } else { @@ -909,30 +1053,55 @@ extension URLBarView: ThemeApplicable { actionButtons.forEach { $0.applyTheme(theme: theme) } tabsButton.applyTheme(theme: theme) - addNewTabButton.applyTheme(theme: theme) + circleButton.applyTheme(theme: theme) cancelTintColor = theme.colors.textPrimary showQRButtonTintColor = theme.colors.textPrimary + /* Ecosia: update background backgroundColor = theme.colors.layer1 + */ + backgroundColor = theme.colors.ecosia.backgroundPrimary line.backgroundColor = theme.colors.borderPrimary + /* Ecosia: Remove color for non-active location border state locationBorderColor = theme.colors.borderPrimary + */ + /* Ecosia: Take into account overlay mode for `locationView` background locationView.backgroundColor = theme.colors.layer3 locationContainer.backgroundColor = theme.colors.layer3 + */ + locationView.backgroundColor = inOverlayMode ? theme.colors.ecosia.backgroundPrimary : theme.colors.ecosia.backgroundTertiary + locationContainer.backgroundColor = locationView.backgroundColor + /* Ecosia: Remove private mode badge privateModeBadge.badge.tintBackground(color: theme.colors.layer1) + */ + + // Ecosia: Update Search Image Color + searchIconImageView.tintColor = isPrivate ? theme.colors.ecosia.textPrimary : theme.colors.ecosia.buttonBackgroundPrimary + warningMenuBadge.badge.tintBackground(color: theme.colors.layer1) + // Ecosia: Update UI Elements with helper + updateUIElementsWithTheme(theme) + // Ecosia: Update Search Engine Image + updateSearchEngineImage() } } // MARK: - PrivateModeUI extension URLBarView: PrivateModeUI { func applyUIMode(isPrivate: Bool, theme: Theme) { + + // Ecosia: Save `isPrivate` state + self.isPrivate = isPrivate + + /* Ecosia: Remove private mode badge if UIDevice.current.userInterfaceIdiom != .pad { privateModeBadge.show(isPrivate) - } + }*/ - let gradientStartColor = isPrivate ? theme.colors.borderAccentPrivate : theme.colors.borderAccent + /* Ecosia: Update UI Elements with helper + let gradientStartColor = isPrivate ? theme.colors.borderAccentPrivate : theme.colors.borderAccent let gradientMiddleColor = isPrivate ? nil : theme.colors.iconAccentPink let gradientEndColor = isPrivate ? theme.colors.borderAccentPrivate : theme.colors.iconAccentYellow locationActiveBorderColor = isPrivate ? theme.colors.layerAccentPrivateNonOpaque : theme.colors.layerAccentNonOpaque @@ -941,8 +1110,42 @@ extension URLBarView: PrivateModeUI { middleColor: gradientMiddleColor, endColor: gradientEndColor ) + */ + updateUIElementsWithTheme(theme) + } +} + +// Ecosia: Apply Theme via helper +extension URLBarView { + + func updateUIElementsWithTheme(_ theme: Theme) { + progressBar.backgroundColor = theme.colors.ecosia.backgroundTertiary + progressBar.setGradientColors(startColor: theme.colors.ecosia.highlighter, + middleColor: theme.colors.ecosia.highlighter, + endColor: theme.colors.ecosia.highlighter) locationTextField?.applyUIMode(isPrivate: isPrivate, theme: theme) locationTextField?.applyTheme(theme: theme) - applyTheme(theme: theme) + locationActiveBorderColor = theme.colors.ecosia.buttonBackgroundPrimary + } +} + +// Ecosia: Update Search Engine Image +extension URLBarView { + + func updateSearchEngineImage() { + if inOverlayMode { + if isPrivate { + searchIconImageView.image = .templateImageNamed(StandardImageIdentifiers.Large.privateMode)?.tinted(withColor: locationActiveBorderColor) + } else { + searchIconImageView.image = .init(named: "iconLogo", in: .ecosia, with: nil) + // Ecosia: Add Accesibility ID + searchIconImageView.accessibilityIdentifier = "urlbar_logo_image" + } + } else { + searchIconImageView.image = .templateImageNamed("searchUrl") + // Ecosia: Add Accesibility ID + searchIconImageView.accessibilityIdentifier = "urlbar_search_image" + } + searchIconImageView.isHidden = !isHome && !inOverlayMode } } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionButton.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionButton.swift index b39af850937a2..e04ac39f6fd8b 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionButton.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionButton.swift @@ -96,7 +96,7 @@ class TrackingProtectionButton: ResizableButton, ThemeApplicable { // MARK: ThemeApplicable func applyTheme(theme: Theme) { - backgroundColorNormal = theme.colors.layer2 + self.backgroundColor = theme.colors.layer2 foregroundColor = theme.colors.textPrimary borderColor = theme.colors.borderPrimary setNeedsUpdateConfiguration() diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift index e08b1931728bb..22c860efd0e20 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionHelpers/TrackingProtectionToggleView.swift @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Common +import Ecosia final class TrackingProtectionToggleView: UIView, ThemeApplicable { private struct UX { @@ -25,7 +26,10 @@ final class TrackingProtectionToggleView: UIView, ThemeApplicable { label.adjustsFontForContentSizeCategory = true } + /* Ecosia: Use themed Switch private let toggleSwitch: UISwitch = .build { toggleSwitch in + */ + private let toggleSwitch: EcosiaThemedSwitch = .build { toggleSwitch in toggleSwitch.isEnabled = true } @@ -131,8 +135,11 @@ final class TrackingProtectionToggleView: UIView, ThemeApplicable { func applyTheme(theme: Theme) { self.backgroundColor = theme.colors.layer2 + /* Ecosia: Use applyTheme from EcosiaThemedSwitch toggleSwitch.tintColor = theme.colors.actionPrimary toggleSwitch.onTintColor = theme.colors.actionPrimary + */ + toggleSwitch.applyTheme(theme: theme) toggleStatusLabel.textColor = theme.colors.textSecondary } } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift index 1c5036a8092b1..1f15d3af36de8 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift @@ -58,8 +58,10 @@ class TrackingProtectionViewController: UIViewController, // MARK: UI components Header View private var headerContainer: HeaderView = .build() + /* Ecosia: Remove Firefox branded header view // MARK: Connection Details View private var connectionDetailsHeaderView: TrackingProtectionConnectionDetailsView = .build() + */ // MARK: Blocked Trackers View private var trackersView: TrackingProtectionBlockedTrackersView = .build() @@ -68,6 +70,11 @@ class TrackingProtectionViewController: UIViewController, stack.distribution = .fillProportionally stack.alignment = .leading stack.axis = .vertical + + // Ecosia: Remove Firefox branded header view (had to move corner radius to this view) + stack.layer.cornerRadius = TPMenuUX.UX.viewCornerRadius + stack.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + stack.layer.masksToBounds = true } // MARK: Connection Status View @@ -278,6 +285,7 @@ class TrackingProtectionViewController: UIViewController, // MARK: Connection Status Header Setup private func setupConnectionHeaderView() { + /* Ecosia: Remove Firefox branded header view baseView.addSubviews(connectionDetailsHeaderView) let connectionHeaderConstraints = [ connectionDetailsHeaderView.leadingAnchor.constraint( @@ -293,6 +301,7 @@ class TrackingProtectionViewController: UIViewController, constant: TPMenuUX.UX.connectionDetailsHeaderMargins), ] constraints.append(contentsOf: connectionHeaderConstraints) + */ } // MARK: Trackers Connection Setup @@ -312,7 +321,10 @@ class TrackingProtectionViewController: UIViewController, equalTo: view.trailingAnchor, constant: -TPMenuUX.UX.horizontalMargin ), + /* Ecosia: Remove Firefox branded header view trackersConnectionContainer.topAnchor.constraint(equalTo: connectionDetailsHeaderView.bottomAnchor), + */ + trackersConnectionContainer.topAnchor.constraint(equalTo: baseView.topAnchor), connectionHorizontalLine.topAnchor.constraint(equalTo: trackersConnectionContainer.bottomAnchor), connectionHorizontalLine.leadingAnchor.constraint(equalTo: trackersConnectionContainer.leadingAnchor), connectionHorizontalLine.trailingAnchor.constraint(equalTo: trackersConnectionContainer.trailingAnchor), @@ -412,9 +424,11 @@ class TrackingProtectionViewController: UIViewController, title: model.displayTitle, icon: headerIcon) + /* Ecosia: Remove Firefox branded header view connectionDetailsHeaderView.setupDetails(title: model.connectionDetailsTitle, status: model.connectionDetailsHeader, image: model.connectionDetailsImage) + */ updateBlockedTrackersCount() connectionStatusView.setupDetails(image: model.getConnectionStatusImage(themeType: currentTheme().type), @@ -462,7 +476,9 @@ class TrackingProtectionViewController: UIViewController, // MARK: Accessibility private func setupAccessibilityIdentifiers() { + /* Ecosia: Remove Firefox branded header view connectionDetailsHeaderView.setupAccessibilityIdentifiers(foxImageA11yId: model.foxImageA11yId) + */ trackersView.setupAccessibilityIdentifiers( arrowImageA11yId: model.arrowImageA11yId, trackersBlockedLabelA11yId: model.trackersBlockedLabelA11yId, @@ -483,7 +499,9 @@ class TrackingProtectionViewController: UIViewController, headerContainer.adjustLayout(isWebsiteIcon: true) trackersView.adjustLayout() connectionStatusView.adjustLayout() + /* Ecosia: Remove Firefox branded header view connectionDetailsHeaderView.adjustLayout() + */ toggleView.adjustLayout() configureProtectionSettingsView() @@ -596,10 +614,12 @@ class TrackingProtectionViewController: UIViewController, model.isProtectionEnabled = false } toggleView.setToggleSwitchVisibility(with: !isContentBlockingConfigEnabled) + /* Ecosia: Remove Firefox branded header view connectionDetailsHeaderView.setupDetails(color: model.getConnectionDetailsBackgroundColor(theme: currentTheme()), title: model.connectionDetailsTitle, status: model.connectionDetailsHeader, image: model.connectionDetailsImage) + */ adjustLayout() } } @@ -611,7 +631,9 @@ extension TrackingProtectionViewController { overrideUserInterfaceStyle = theme.type.getInterfaceStyle() view.backgroundColor = theme.colors.layer1 headerContainer.applyTheme(theme: theme) + /* Ecosia: Remove Firefox branded header view connectionDetailsHeaderView.backgroundColor = theme.colors.layer2 + */ trackersView.applyTheme(theme: theme) connectionStatusView.applyTheme(theme: theme) connectionStatusView.setConnectionStatus(image: model.getConnectionStatusImage(themeType: theme.type), diff --git a/firefox-ios/Client/Frontend/Widgets/AutocompleteTextField.swift b/firefox-ios/Client/Frontend/Widgets/AutocompleteTextField.swift index ca46681fe460f..25b0f4677a35a 100644 --- a/firefox-ios/Client/Frontend/Widgets/AutocompleteTextField.swift +++ b/firefox-ios/Client/Frontend/Widgets/AutocompleteTextField.swift @@ -276,7 +276,9 @@ class AutocompleteTextField: UITextField, autocompleteTextLabel = nil if let theme { - let color = isPrivateMode ? theme.colors.layerAccentPrivateNonOpaque : theme.colors.layerAccentNonOpaque + // Ecosia: Update color + // let color = isPrivateMode ? theme.colors.layerAccentPrivateNonOpaque : theme.colors.layerAccentNonOpaque + let color = UIColor(red: 0, green: 0.495, blue: 0.66, alpha: 1).withAlphaComponent(0.25) autocompleteText.addAttribute(NSAttributedString.Key.backgroundColor, value: color, range: NSRange(location: 0, length: suggestionText.count)) @@ -394,7 +396,10 @@ class AutocompleteTextField: UITextField, let autocompleteText = NSMutableAttributedString( string: self.autocompleteTextLabel?.attributedText?.string ?? "" ) + /* Ecosia: Update color let color = isPrivateMode ? theme.colors.layerAccentPrivateNonOpaque : theme.colors.layerAccentNonOpaque + */ + let color = UIColor(red: 0, green: 0.495, blue: 0.66, alpha: 1).withAlphaComponent(0.25) autocompleteText.addAttribute(NSAttributedString.Key.backgroundColor, value: color, range: NSRange(location: 0, length: autocompleteText.length)) @@ -411,14 +416,22 @@ class AutocompleteTextField: UITextField, attributedPlaceholder = NSAttributedString(string: .TabLocationURLPlaceholder, attributes: attributes) + /* Ecosia: update color backgroundColor = theme.colors.layer3 textColor = theme.colors.textPrimary tintColor = theme.colors.actionPrimary + */ + textColor = theme.colors.textSecondary + backgroundColor = .clear // Only refresh if an autocomplete label is presented to the user if autocompleteTextLabel?.attributedText != nil { + /* Ecosia: update background and color autocompleteTextLabel?.backgroundColor = theme.colors.layer3 autocompleteTextLabel?.textColor = theme.colors.textPrimary + */ + autocompleteTextLabel?.backgroundColor = UIColor(red: 0, green: 0.495, blue: 0.66, alpha: 0.25) + autocompleteTextLabel?.textColor = theme.colors.textSecondary } } diff --git a/firefox-ios/Client/Frontend/Widgets/EcosiaTabTrayButtonExtensions.swift b/firefox-ios/Client/Frontend/Widgets/EcosiaTabTrayButtonExtensions.swift new file mode 100644 index 0000000000000..a881e05501e72 --- /dev/null +++ b/firefox-ios/Client/Frontend/Widgets/EcosiaTabTrayButtonExtensions.swift @@ -0,0 +1,52 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit +import Shared + +class PrivateModeButton: ToggleButton, PrivateModeUI { + var offTint = UIColor.black + var onTint = UIColor.black + var isPrivate = false + + override init(frame: CGRect) { + super.init(frame: frame) + accessibilityLabel = .TabTrayToggleAccessibilityLabel + accessibilityHint = .TabTrayToggleAccessibilityHint + setTitle(.localized(.privateTab), for: .normal) + setTitleColor(LegacyThemeManager.instance.current.tabTray.tabTitleText, for: .normal) + titleLabel?.font = .preferredFont(forTextStyle: .body) + titleLabel?.adjustsFontForContentSizeCategory = true + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func applyUIMode(isPrivate: Bool, theme: Theme) { + // isPrivate == isSelected + self.isPrivate = isPrivate + accessibilityValue = isSelected ? .TabTrayToggleAccessibilityValueOn : .TabTrayToggleAccessibilityValueOff + updateColors() + } + + func applyTheme(theme: Theme) { + updateColors() + } +} + +extension PrivateModeButton { + + private func updateColors() { + let color = isPrivate + ? UIColor.legacyTheme.ecosia.primaryBackground + : UIColor.legacyTheme.ecosia.primaryText + + setTitleColor(color, for: .normal) + backgroundLayer.backgroundColor = isPrivate + ? UIColor.legacyTheme.ecosia.privateButtonBackground.cgColor + : UIColor.clear.cgColor + } +} diff --git a/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift b/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift index 8546af3ab9367..072b6046d5e70 100644 --- a/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift +++ b/firefox-ios/Client/Frontend/Widgets/OneLineTableViewCell.swift @@ -51,6 +51,8 @@ class OneLineTableViewCell: UITableViewCell, private lazy var bottomSeparatorView: UIView = .build { separatorLine in // separator hidden by default separatorLine.isHidden = true + // Ecosia: Add separator color + separatorLine.backgroundColor = UIColor.Photon.Grey40 } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -137,10 +139,16 @@ class OneLineTableViewCell: UITableViewCell, override func prepareForReuse() { super.prepareForReuse() - selectionStyle = .default + /* Ecosia: Update properties + separatorInset = defaultSeparatorInset + titleLabel.text = nil + */ + accessoryView = nil + accessoryType = .none separatorInset = defaultSeparatorInset titleLabel.text = nil + leftImageView.image = nil } // To simplify setup, OneLineTableViewCell now has a viewModel @@ -162,10 +170,16 @@ class OneLineTableViewCell: UITableViewCell, func applyTheme(theme: Theme) { selectedView.backgroundColor = theme.colors.layer5Hover + /* Ecosia: Update custom theming backgroundColor = theme.colors.layer5 + */ + backgroundColor = theme.colors.ecosia.backgroundElevation1 titleLabel.textColor = theme.colors.textPrimary bottomSeparatorView.backgroundColor = theme.colors.borderPrimary accessoryView?.tintColor = theme.colors.iconSecondary leftImageView.tintColor = theme.colors.textPrimary + + // Ecosia: update tint color + tintColor = theme.colors.ecosia.buttonBackgroundPrimary } } diff --git a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift index 976b9e4dbbc03..31df0375bc8ce 100644 --- a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift +++ b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift @@ -23,6 +23,39 @@ extension PhotonActionSheetProtocol { // TODO: Regression testing needed here. guard let uuid = view.currentWindowUUID else { return } + // Ecosia: Ecosia: custom UI/UX for main menu + guard !viewModel.isMainMenu else { + + // main menu should only be opened from the browser + guard let browser = self as? BrowserViewController else { return } + let sheet = PageActionMenu(viewModel: viewModel, delegate: browser, windowUUID: uuid) + sheet.modalPresentationStyle = viewModel.modalStyle + + // iPhone + if #available(iOS 15.0, *), let sheet = sheet.sheetPresentationController { + sheet.detents = [.medium(), .large()] + } + + // ipad + if let popoverVC = sheet.popoverPresentationController, sheet.modalPresentationStyle == .popover { + popoverVC.delegate = viewController + popoverVC.sourceView = view + popoverVC.sourceRect = view.bounds + + let trait = viewController.traitCollection + if viewModel.isMainMenu { + let margins = viewModel.getMainMenuPopOverMargins(trait: trait, view: view, presentedOn: viewController) + popoverVC.popoverLayoutMargins = margins + } + // Ecosia: Update permitted arrow dimensions + // popoverVC.permittedArrowDirections = [.up] + popoverVC.permittedArrowDirections = viewModel.getPossibleArrowDirections(trait: trait) + } + + viewController.present(sheet, animated: true, completion: nil) + return + } + let sheet = PhotonActionSheet(viewModel: viewModel, windowUUID: uuid) sheet.modalPresentationStyle = viewModel.modalStyle sheet.photonTransitionDelegate = PhotonActionSheetAnimator() diff --git a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift index d7d0a4418a0cd..9a26cf76abbc3 100644 --- a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift +++ b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/SingleActionViewModel.swift @@ -40,6 +40,8 @@ class SingleActionViewModel { private(set) var needsIconActionableTint: Bool var isEnabled: Bool // Used by toggles like night mode to switch tint color + // Ecosia: Add `isNew` bool + var isNew: Bool // Used to display the "New" badge private(set) var bold = false private(set) var tabCount: String? private(set) var tapHandler: ((SingleActionViewModel) -> Void)? @@ -72,6 +74,7 @@ class SingleActionViewModel { badgeIconNamed: String? = nil, bold: Bool? = false, tabCount: String? = nil, + isNew: Bool = false, // Ecosia: Add `isNew` bool tapHandler: ((SingleActionViewModel) -> Void)? = nil) { self.title = title self.iconString = iconString @@ -85,6 +88,7 @@ class SingleActionViewModel { self.text = text self.bold = bold ?? false self.tabCount = tabCount + self.isNew = isNew // Ecosia: Add `isNew` bool self.badgeIconName = badgeIconNamed } diff --git a/firefox-ios/Client/Frontend/Widgets/SiteTableViewController.swift b/firefox-ios/Client/Frontend/Widgets/SiteTableViewController.swift index e1ec5ba2caa1a..124a05a812087 100644 --- a/firefox-ios/Client/Frontend/Widgets/SiteTableViewController.swift +++ b/firefox-ios/Client/Frontend/Widgets/SiteTableViewController.swift @@ -27,9 +27,18 @@ class SiteTableViewController: UIViewController, let windowUUID: WindowUUID var currentWindowUUID: UUID? { windowUUID } + // Ecosia: Branding + let style: UITableView.Style + var data = Cursor(status: .success, msg: "No data set") + /* Ecosia: Update TableView init to make it grouped lazy var tableView: UITableView = .build { [weak self] table in guard let self = self else { return } + */ + lazy var tableView: UITableView = { + let table = UITableView(frame: .zero, style: self.style) + table.translatesAutoresizingMaskIntoConstraints = false + table.delegate = self table.dataSource = self table.register( @@ -44,7 +53,11 @@ class SiteTableViewController: UIViewController, SiteTableViewHeader.self, forHeaderFooterViewReuseIdentifier: SiteTableViewHeader.cellIdentifier ) + + /* Enable layout margins table.layoutMargins = .zero + */ + table.keyboardDismissMode = .onDrag table.accessibilityIdentifier = "SiteTable" table.cellLayoutMarginsFollowReadableWidth = false @@ -58,7 +71,9 @@ class SiteTableViewController: UIViewController, // Set an empty footer to prevent empty cells from appearing in the list. table.tableFooterView = UIView() table.sectionHeaderTopPadding = 0 - } + // Ecosia: Update TableView init to make it grouped + return table + }() override private init(nibName: String?, bundle: Bundle?) { fatalError("init(coder:) has not been implemented") @@ -68,12 +83,17 @@ class SiteTableViewController: UIViewController, windowUUID: WindowUUID, notificationCenter: NotificationProtocol = NotificationCenter.default, windowManager: WindowManager = AppContainer.shared.resolve(), - themeManager: ThemeManager = AppContainer.shared.resolve()) { + // Ecosia: Update init adding `style` + // themeManager: ThemeManager = AppContainer.shared.resolve() + themeManager: ThemeManager = AppContainer.shared.resolve(), + style: UITableView.Style = .plain) { self.profile = profile self.windowUUID = windowUUID self.notificationCenter = notificationCenter self.themeManager = themeManager self.windowManager = windowManager + // Ecosia: Update init adding `style` + self.style = style super.init(nibName: nil, bundle: nil) listenForThemeChange(view) applyTheme() @@ -157,8 +177,12 @@ class SiteTableViewController: UIViewController, func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { if let header = view as? UITableViewHeaderFooterView { + /* Ecosia: Update header text label color header.textLabel?.textColor = themeManager.getCurrentTheme(for: windowUUID).colors.textPrimary header.contentView.backgroundColor = themeManager.getCurrentTheme(for: windowUUID).colors.layer1 + */ + header.textLabel?.textColor = themeManager.getCurrentTheme(for: windowUUID).colors.ecosia.textSecondary + header.contentView.backgroundColor = .clear } } diff --git a/firefox-ios/Client/Frontend/Widgets/SiteTableViewHeader.swift b/firefox-ios/Client/Frontend/Widgets/SiteTableViewHeader.swift index c18d7782013a8..f019889c5c3be 100644 --- a/firefox-ios/Client/Frontend/Widgets/SiteTableViewHeader.swift +++ b/firefox-ios/Client/Frontend/Widgets/SiteTableViewHeader.swift @@ -99,9 +99,15 @@ class SiteTableViewHeader: UITableViewHeaderFooterView, ThemeApplicable, Reusabl } func applyTheme(theme: Theme) { + /* Ecosia: Change theme titleLabel.textColor = theme.colors.textPrimary backgroundView?.backgroundColor = theme.colors.layer1 collapsibleImageView.image = collapsibleState?.image?.tinted(withColor: theme.colors.iconAccent) + */ + titleLabel.textColor = theme.colors.ecosia.textSecondary + backgroundView?.backgroundColor = .clear + collapsibleImageView.image = collapsibleState?.image?.tinted(withColor: theme.colors.ecosia.textSecondary) + bordersHelper.applyTheme(theme: theme) } diff --git a/firefox-ios/Client/Frontend/Widgets/TabsButton.swift b/firefox-ios/Client/Frontend/Widgets/TabsButton.swift index 4be0e0063ef17..817ac9a7bf2bc 100644 --- a/firefox-ios/Client/Frontend/Widgets/TabsButton.swift +++ b/firefox-ios/Client/Frontend/Widgets/TabsButton.swift @@ -27,7 +27,17 @@ class TabsButton: UIButton, ThemeApplicable { private var selectedTintColor: UIColor! private var unselectedTintColor: UIColor! + /* Ecosia: Update so theme is not nil on `createTabsButton` + (required so colors are present for the animated cloned button) private var theme: Theme? + */ + private var theme: Theme? { + guard traitCollection.userInterfaceIdiom != .pad else { + return nil + } + let themeManager: ThemeManager = AppContainer.shared.resolve() + return themeManager.getCurrentTheme(for: currentWindowUUID) + } // When all animations are completed, this is the most-recently assigned tab count that is shown. // updateTabCount() can be called in rapid succession, this ensures only final tab count is displayed. diff --git a/firefox-ios/Client/Frontend/Widgets/ThemedDefaultNavigationController.swift b/firefox-ios/Client/Frontend/Widgets/ThemedDefaultNavigationController.swift index 6f0119b7e4555..2b70c9832587b 100644 --- a/firefox-ios/Client/Frontend/Widgets/ThemedDefaultNavigationController.swift +++ b/firefox-ios/Client/Frontend/Widgets/ThemedDefaultNavigationController.swift @@ -48,7 +48,10 @@ class ThemedDefaultNavigationController: DismissableNavigationViewController, Th navigationBar.compactAppearance = standardAppearance navigationBar.scrollEdgeAppearance = standardAppearance navigationBar.compactScrollEdgeAppearance = standardAppearance + /* Ecosia: Update bar tint navigationBar.tintColor = theme.colors.textPrimary + */ + navigationBar.tintColor = theme.colors.actionPrimary } private func setupToolBarAppearance() { diff --git a/firefox-ios/Client/Frontend/Widgets/ToggleButton.swift b/firefox-ios/Client/Frontend/Widgets/ToggleButton.swift index 3270e3d8a6ccc..203871d9ae4f0 100644 --- a/firefox-ios/Client/Frontend/Widgets/ToggleButton.swift +++ b/firefox-ios/Client/Frontend/Widgets/ToggleButton.swift @@ -5,7 +5,11 @@ import UIKit private struct UX { + /* Ecosia: Update background color static let BackgroundColor = UIColor.Photon.Purple60 + */ + // TODO: Do we also need to handle `white` on Dark Theme? + static let BackgroundColor = UIColor.Photon.Grey60 // The amount of pixels the toggle button will expand over the normal size. // This results in the larger -> contract animation. @@ -14,6 +18,8 @@ private struct UX { static let HideDuration: TimeInterval = 0.2 static let BackgroundSize = CGSize(width: 32, height: 32) + // Ecosia: Add Insets to modify button margins + static let Insets = UIEdgeInsets(top: 6, left: 8, bottom: 6, right: 8) } class ToggleButton: UIButton { @@ -23,7 +29,7 @@ class ToggleButton: UIButton { animateSelection(selected) } } - + /* Ecosia: Restore animation like in vanilla v104 fileprivate func updateMaskPathForSelectedState(_ selected: Bool) { let path = CGMutablePath() if selected { @@ -79,6 +85,67 @@ class ToggleButton: UIButton { self.maskShapeLayer.add(animation, forKey: "shrink") } } + */ + + fileprivate func updateMaskPathForSelectedState(_ selected: Bool) { + let path = CGMutablePath() + if selected { + var rect = CGRect(size: bounds.size) + // Fix crash for negative rect size + rect.center = maskShapeLayer.position + let corner = floor(rect.size.height / 2.0) + guard rect.size.height > 0, rect.size.width > 0, corner > 0 else { return } + path.addRoundedRect(in: rect, cornerWidth: corner, cornerHeight: corner) + } else { + path.addRoundedRect(in: CGRect(origin: maskShapeLayer.position, size: .zero), cornerWidth: 0, cornerHeight: 0) + } + self.maskShapeLayer.path = path + } + + fileprivate func animateSelection(_ selected: Bool) { + var endFrame = CGRect(size: bounds.size) + endFrame.center = maskShapeLayer.position + let corner = max(floor(endFrame.size.height / 2.0), 0) + + if selected { + let animation = CAKeyframeAnimation(keyPath: "path") + + let startPath = CGMutablePath() + startPath.addRoundedRect(in: CGRect(origin: maskShapeLayer.position, size: .zero), cornerWidth: 0, cornerHeight: 0) + + let largerPath = CGMutablePath() + let largerBounds = endFrame.insetBy(dx: -UX.ExpandDelta, dy: -UX.ExpandDelta) + largerPath.addRoundedRect(in: largerBounds, cornerWidth: corner, cornerHeight: corner) + + let endPath = CGMutablePath() + endPath.addRoundedRect(in: endFrame, cornerWidth: corner, cornerHeight: corner) + + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) + animation.values = [ + startPath, + largerPath, + endPath + ] + animation.duration = UX.ShowDuration + self.maskShapeLayer.path = endPath + self.maskShapeLayer.add(animation, forKey: "grow") + } else { + let animation = CABasicAnimation(keyPath: "path") + animation.duration = UX.HideDuration + animation.fillMode = CAMediaTimingFillMode.forwards + + let fromPath = CGMutablePath() + fromPath.addRoundedRect(in: endFrame, cornerWidth: corner, cornerHeight: corner) + animation.fromValue = fromPath + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) + + let toPath = CGMutablePath() + toPath.addRoundedRect(in: CGRect(origin: self.maskShapeLayer.bounds.center, size: .zero), cornerWidth: 0, cornerHeight: 0) + + self.maskShapeLayer.path = toPath + self.maskShapeLayer.add(animation, forKey: "shrink") + } + } fileprivate lazy var backgroundView: UIView = { let view = UIView() @@ -92,7 +159,9 @@ class ToggleButton: UIButton { return circle }() - fileprivate lazy var backgroundLayer: CALayer = { + // Ecosia: Make `backgroundLayer` accessible externally + // Remove `fileprivate` + lazy var backgroundLayer: CALayer = { let backgroundLayer = CALayer() backgroundLayer.backgroundColor = UX.BackgroundColor.cgColor backgroundLayer.mask = self.maskShapeLayer @@ -102,6 +171,8 @@ class ToggleButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) contentMode = .redraw + // Ecosia: Add `contentEdgeInsets` + contentEdgeInsets = UX.Insets insertSubview(backgroundView, belowSubview: imageView!) } diff --git a/firefox-ios/Client/Frontend/Widgets/TwoLineImageOverlayCell.swift b/firefox-ios/Client/Frontend/Widgets/TwoLineImageOverlayCell.swift index 9db06c5b7ed6e..7dc401117f035 100644 --- a/firefox-ios/Client/Frontend/Widgets/TwoLineImageOverlayCell.swift +++ b/firefox-ios/Client/Frontend/Widgets/TwoLineImageOverlayCell.swift @@ -102,7 +102,8 @@ class TwoLineImageOverlayCell: UITableViewCell, containerView.addSubview(leftImageView) containerView.addSubview(stackView) - containerView.addSubview(leftOverlayImageView) + // Ecosia: Remove overlay image + // containerView.addSubview(leftOverlayImageView) contentView.addSubview(containerView) bringSubviewToFront(containerView) @@ -122,13 +123,14 @@ class TwoLineImageOverlayCell: UITableViewCell, leftImageView.trailingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: -16), + /* Ecosia: Remove overlay image leftOverlayImageView.trailingAnchor.constraint(equalTo: leftImageView.trailingAnchor, constant: 8), leftOverlayImageView.bottomAnchor.constraint(equalTo: leftImageView.bottomAnchor, constant: 8), leftOverlayImageView.heightAnchor.constraint(equalToConstant: 22), leftOverlayImageView.widthAnchor.constraint(equalToConstant: 22), - + */ stackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8), stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, @@ -146,7 +148,13 @@ class TwoLineImageOverlayCell: UITableViewCell, titleLabel.textColor = theme.colors.textPrimary descriptionLabel.textColor = theme.colors.textSecondary leftImageView.layer.borderColor = theme.colors.borderPrimary.cgColor + + /* Ecosia: Update image view properties accessoryView?.tintColor = theme.colors.iconSecondary + */ + leftImageView.tintColor = theme.colors.textPrimary + accessoryView?.tintColor = theme.colors.actionSecondary + topSeparatorView.backgroundColor = theme.colors.borderPrimary bottomSeparatorView.backgroundColor = theme.colors.borderPrimary } diff --git a/firefox-ios/Client/Info.plist b/firefox-ios/Client/Info.plist index 5a335dece8fdf..aa120081578f4 100644 --- a/firefox-ios/Client/Info.plist +++ b/firefox-ios/Client/Info.plist @@ -2,6 +2,8 @@ + AUTH0_CLIENT_ID + $(AUTH0_CLIENT_ID) AdjustAppToken $(ADJUST_APP_TOKEN) AppIdentifierPrefix @@ -13,6 +15,8 @@ org.mozilla.ios.surface.notification.refresh org.mozilla.ios.firefox.suggest.ingest + BRAZE_API_KEY + $(BRAZE_API_KEY) CFBundleDevelopmentRegion en CFBundleDisplayName @@ -28,14 +32,14 @@ CFBundlePackageType APPL CFBundleShortVersionString - 133.0 + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleURLTypes CFBundleURLName - org.mozilla.Client + com.ecosia.ecosiaapp CFBundleURLSchemes $(MOZ_PUBLIC_URL_SCHEME) @@ -47,6 +51,10 @@ CFBundleVersion 1 + CF_ACCESS_CLIENT_ID + $(CF_ACCESS_CLIENT_ID) + CF_ACCESS_CLIENT_SECRET + $(CF_ACCESS_CLIENT_SECRET) INIntentsSupported QuickActionIntent @@ -105,7 +113,8 @@ NSUserActivityTypes QuickActionIntent - org.mozilla.ios.firefox.browsing + QuickLinkSelectionIntent + com.ecosia.ecosiaapp.browsing NimbusURL $(NIMBUS_URL) @@ -119,11 +128,12 @@ NewYorkMedium-BoldItalic.otf NewYorkMedium-Regular.otf NewYorkMedium-RegularItalic.otf + FoundersGroteskCondensed-Semibold.ttf UIApplicationSceneManifest UIApplicationSupportsMultipleScenes - + UISceneConfigurations UIWindowSceneSessionRoleApplication @@ -151,7 +161,7 @@ UIApplicationShortcutItemIconFile - privateModeLarge + privateSearch UIApplicationShortcutItemTitle New Private Tab UIApplicationShortcutItemType @@ -159,7 +169,7 @@ UIApplicationShortcutItemIconFile - qrCodeLarge + menu-ScanQRCode UIApplicationShortcutItemTitle Scan QR Code UIApplicationShortcutItemType @@ -177,7 +187,7 @@ UIFileSharingEnabled UILaunchStoryboardName - LaunchScreen + EcosiaLaunchScreen UIRequiredDeviceCapabilities armv7 diff --git a/firefox-ios/Client/IntroScreenManager.swift b/firefox-ios/Client/IntroScreenManager.swift index 85f5562e55767..6e2d1d8876f62 100644 --- a/firefox-ios/Client/IntroScreenManager.swift +++ b/firefox-ios/Client/IntroScreenManager.swift @@ -4,12 +4,22 @@ import Foundation import Shared +import Ecosia struct IntroScreenManager { var prefs: Prefs var shouldShowIntroScreen: Bool { - prefs.intForKey(PrefsKeys.IntroSeen) == nil + /* Ecosia + The `PrefsKeys.IntroSeen` is also used to flag the DefaultBrowser card showing + for some reason. So the in the version based on top of the v104, the check to + show the IntroScreen/Onboarding is perfomed at AppDelegate level + checking against the `User.shared.firstTime` flag. + We are now performing the check here so the whole coordinators/managers patters will work + as usual. + */ + // prefs.intForKey(PrefsKeys.IntroSeen) == nil + User.shared.firstTime } func didSeeIntroScreen() { diff --git a/firefox-ios/Client/Nimbus/NimbusSearchBarLayer.swift b/firefox-ios/Client/Nimbus/NimbusSearchBarLayer.swift index 589938f5032cf..92aa1406be713 100644 --- a/firefox-ios/Client/Nimbus/NimbusSearchBarLayer.swift +++ b/firefox-ios/Client/Nimbus/NimbusSearchBarLayer.swift @@ -7,8 +7,11 @@ import Foundation final class NimbusSearchBarLayer { // MARK: - Public methods public func getDefaultPosition(from nimbus: FxNimbus = FxNimbus.shared) -> SearchBarPosition { + /* Ecosia: Search Bar stays on top by default let isAtBottom = nimbus.features.search.value().awesomeBar.position.isBottom return isAtBottom ? .bottom : .top + */ + .top } } diff --git a/firefox-ios/Client/PrivacyWindowHelper.swift b/firefox-ios/Client/PrivacyWindowHelper.swift new file mode 100644 index 0000000000000..b0f7cc90ccafe --- /dev/null +++ b/firefox-ios/Client/PrivacyWindowHelper.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +final class PrivacyWindowHelper { + private var privacyWindow: UIWindow? + + func showWindow(withThemedColor color: UIColor) { + guard let windowScene = UIWindow.attachedKeyWindow?.windowScene else { return } + + privacyWindow = UIWindow(windowScene: windowScene) + privacyWindow?.rootViewController = UIViewController() + privacyWindow?.rootViewController?.view.backgroundColor = color + // Set the privacy window level to be above alert windows (highest in importance). + privacyWindow?.windowLevel = .alert + 1 + privacyWindow?.makeKeyAndVisible() + } + + func removeWindow() { + privacyWindow?.isHidden = true + privacyWindow = nil + } +} diff --git a/firefox-ios/Client/Protocols/URLSession/URLSessionProtocol.swift b/firefox-ios/Client/Protocols/URLSession/URLSessionProtocol.swift index fdfaeff275d18..d2642b8c24e4b 100644 --- a/firefox-ios/Client/Protocols/URLSession/URLSessionProtocol.swift +++ b/firefox-ios/Client/Protocols/URLSession/URLSessionProtocol.swift @@ -7,6 +7,8 @@ import Foundation protocol URLSessionProtocol { typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void + func data(from url: URL) async throws -> (Data, URLResponse) + func dataTaskWith(_ url: URL, completionHandler: @escaping DataTaskResult ) -> URLSessionDataTaskProtocol @@ -18,6 +20,10 @@ protocol URLSessionProtocol { } extension URLSession: URLSessionProtocol { + public func data(from url: URL) async throws -> (Data, URLResponse) { + try await data(from: url, delegate: nil) + } + func dataTaskWith( request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void diff --git a/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift b/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift index ed16f8232961a..d6a0d4f53e5ed 100644 --- a/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift +++ b/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift @@ -7,6 +7,8 @@ import Foundation import WebKit import Storage import Shared +import Ecosia +import Combine // MARK: - TabManagerDelegate protocol TabManagerDelegate: AnyObject { @@ -83,7 +85,10 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler let delaySelectingNewPopupTab: TimeInterval = 0.1 var normalTabs: [Tab] { - return tabs.filter { !$0.isPrivate } + /* Ecosia: filter out invisible tabs + return tabs.filter { !$0.isPrivate } + */ + return tabs.filter { !$0.isPrivate && !$0.isInvisible } } var normalActiveTabs: [Tab] { @@ -99,7 +104,10 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler } var privateTabs: [Tab] { + /* Ecosia: filter out invisible tabs return tabs.filter { $0.isPrivate } + */ + return tabs.filter { $0.isPrivate && !$0.isInvisible } } var isInactiveTabsEnabled: Bool { @@ -185,6 +193,26 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler selector: #selector(blockPopUpDidChange), name: .BlockPopup, object: nil) + // Ecosia: Cookie and settings observing + configuration.websiteDataStore.httpCookieStore.add(self) + searchSettingsObserver = NotificationCenter.default + .publisher(for: .searchSettingsChanged) + .sink { [privateConfiguration, configuration] _ in + Cookie.makeSearchSettingsObserverCookies(isPrivate: false).forEach { cookie in + configuration.websiteDataStore.httpCookieStore.setCookie(cookie) + } + Cookie.makeSearchSettingsObserverCookies(isPrivate: true).forEach { cookie in + privateConfiguration.websiteDataStore.httpCookieStore.setCookie(cookie) + } + } + } + + // MARK: Ecosia: Observing Cookies and Search setting changes + var searchSettingsObserver: Cancellable? + + deinit { + configuration.websiteDataStore.httpCookieStore.remove(self) + searchSettingsObserver?.cancel() } // MARK: - Delegates @@ -231,13 +259,19 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler // We do this to go against the configuration of the // tag to behave the same way as Safari :-( configuration.ignoresViewportScaleLimits = true - if isPrivate { - configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() - } else { - configuration.websiteDataStore = WKWebsiteDataStore.default() + if isPrivate { + configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() + } else { + configuration.websiteDataStore = WKWebsiteDataStore.default() + } + configuration.setURLSchemeHandler(InternalSchemeHandler(), forURLScheme: InternalURL.scheme) + + // Ecosia: inject all required cookies when config is created to make sure they are present + let cookies = Cookie.makeRequiredCookies(isPrivate: isPrivate) + cookies.forEach { cookie in + configuration.websiteDataStore.httpCookieStore.setCookie(cookie) } - configuration.setURLSchemeHandler(InternalSchemeHandler(), forURLScheme: InternalURL.scheme) return configuration } @@ -284,8 +318,22 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler func preserveTabs() { fatalError("should never be called") } func shouldClearPrivateTabs() -> Bool { + /* Ecosia: [MOB-3352] Change default behaviour to false and migrate old key value // FXIOS-9519: By default if no bool value is set we close the private tabs and mark it true return profile.prefs.boolForKey(PrefsKeys.Settings.closePrivateTabs) ?? true + */ + var currentValue = profile.prefs.boolForKey(PrefsKeys.Settings.closePrivateTabs) + + // Ecosia: Only necessary for migration to version 11.x.x - remove after enough users migrated + let oldKey = "settings.closePrivateTabs" + let oldClosePrivateTabsValue = profile.prefs.boolForKey(oldKey) + if let oldValue = oldClosePrivateTabsValue, currentValue == nil { + currentValue = oldValue + profile.prefs.setBool(oldValue, forKey: PrefsKeys.Settings.closePrivateTabs) + profile.prefs.removeObjectForKey(oldKey) + } + + return currentValue ?? PrefsKeysDefaultValues.Settings.closePrivateTabs } func cleanupClosedTabs(_ closedTabs: [Tab], previous: Tab?, isPrivate: Bool = false) { @@ -778,10 +826,18 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler } // Toast + /* Ecosia: Update Toast to look like v104 + let viewModel = ButtonToastViewModel( + labelText: String.localizedStringWithFormat( + .TabsTray.CloseTabsToast.Title, + recentlyClosedTabs.count), + buttonText: .TabsTray.CloseTabsToast.Action) + */ let viewModel = ButtonToastViewModel( labelText: String.localizedStringWithFormat( .TabsTray.CloseTabsToast.Title, recentlyClosedTabs.count), + imageName: "tabs", buttonText: .TabsTray.CloseTabsToast.Action) // Passing nil theme because themeManager is not available, // calling to applyTheme with proper theme before showing @@ -1007,7 +1063,10 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler ) -> Tab? { let page = NewTabAccessors.getHomePage(profilePreferences) let customUrl = HomeButtonHomePageAccessors.getHomePage(profilePreferences) - let homeUrl = URL(string: "internal://local/about/home") + /* Ecosia: Use homepage url constant + let homeUrl = URL(string: "internal://local/about/home") + */ + let homeUrl = URL(string: URL.homepageUrlString) if page == .homePage, let customUrl = customUrl { return existingTab ?? addTab(URLRequest(url: customUrl), isPrivate: privateMode) @@ -1072,3 +1131,14 @@ extension LegacyTabManager: WKNavigationDelegate { } } } + +// Ecosia: Cookie observer +extension LegacyTabManager: WKHTTPCookieStoreObserver { + func cookiesDidChange(in cookieStore: WKHTTPCookieStore) { + cookieStore.getAllCookies { cookies in + DispatchQueue.main.async { + Cookie.received(cookies, in: cookieStore) + } + } + } +} diff --git a/firefox-ios/Client/TabManagement/Tab.swift b/firefox-ios/Client/TabManagement/Tab.swift index f63fc91e418d1..9b8cebdb902e6 100644 --- a/firefox-ios/Client/TabManagement/Tab.swift +++ b/firefox-ios/Client/TabManagement/Tab.swift @@ -585,7 +585,12 @@ class Tab: NSObject, ThemeApplicable, FeatureFlaggable { func restore(_ webView: WKWebView, interactionState: Data? = nil) { if let url = url { - webView.load(URLRequest(url: url)) + if let internalURL = InternalURL(url), + internalURL.isAboutHomeURL { + webView.load(PrivilegedRequest(url: url) as URLRequest) + } else { + webView.load(URLRequest(url: url)) + } } if let interactionState = interactionState { @@ -674,7 +679,16 @@ class Tab: NSObject, ThemeApplicable, FeatureFlaggable { if let url = request.url, url.isFileURL, request.isPrivileged { return webView.loadFileURL(url, allowingReadAccessTo: url) } - return webView.load(request) + // Ecosia: updating the request with auth parameters and language header if needed + // return webView.load(request) + var ecosiaUpdatedRequest = request + // Inject auth parameters if needed + ecosiaUpdatedRequest = ecosiaUpdatedRequest.withCloudFlareAuthParameters() + // Enriching the search request (showing SERP page) with a language region header for market selection options + if ecosiaUpdatedRequest.url?.isEcosiaSearchQuery() == true { + ecosiaUpdatedRequest.addLanguageRegionHeader() + } + return webView.load(ecosiaUpdatedRequest) } return nil } @@ -703,7 +717,8 @@ class Tab: NSObject, ThemeApplicable, FeatureFlaggable { } } - if let webView, webView.url != nil { + // Do not reload from origin for homepage internal URLs + if let webView, webView.url != nil && !(InternalURL(webView.url)?.isAboutHomeURL ?? false) { webView.reloadFromOrigin() logger.log("Reloaded zombified tab from origin", level: .debug, diff --git a/firefox-ios/Client/TabManagement/TabManagerImplementation.swift b/firefox-ios/Client/TabManagement/TabManagerImplementation.swift index ba6af34a56a41..e7933c856cde3 100644 --- a/firefox-ios/Client/TabManagement/TabManagerImplementation.swift +++ b/firefox-ios/Client/TabManagement/TabManagerImplementation.swift @@ -24,7 +24,10 @@ class TabManagerImplementation: LegacyTabManager, Notifiable, WindowSimpleTabsPr override var normalActiveTabs: [Tab] { let inactiveTabs = getInactiveTabs() + /* Ecosia: filter out invisible tabs let activeTabs = tabs.filter { $0.isPrivate == false && !inactiveTabs.contains($0) } + */ + let activeTabs = tabs.filter { $0.isPrivate == false && !$0.isInvisible && !inactiveTabs.contains($0) } return activeTabs } diff --git a/firefox-ios/Client/Telemetry/AdsTelemetryHelper.swift b/firefox-ios/Client/Telemetry/AdsTelemetryHelper.swift index 7a5c73d6fe8c0..7b20c9ddf7595 100644 --- a/firefox-ios/Client/Telemetry/AdsTelemetryHelper.swift +++ b/firefox-ios/Client/Telemetry/AdsTelemetryHelper.swift @@ -5,7 +5,9 @@ import Foundation import Shared import WebKit +/* Ecosia: Remove Glean import Glean + */ public enum BasicSearchProvider: String { case google @@ -141,10 +143,14 @@ class AdsTelemetryHelper: TabContentScript { // Tracking public static func trackAdsFoundOnPage(providerName: String) { + /* Ecosia: Remove Glean GleanMetrics.BrowserSearch.withAds["provider-\(providerName)"].add() + */ } public static func trackAdsClickedOnPage(providerName: String) { + /* Ecosia: Remove Glean GleanMetrics.BrowserSearch.adClicks["provider-\(providerName)"].add() + */ } } diff --git a/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift b/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift index 5dd25a41c9b26..dec07873a87cf 100644 --- a/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift +++ b/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ import Storage import Common diff --git a/firefox-ios/Client/Telemetry/GleanWrapper.swift b/firefox-ios/Client/Telemetry/GleanWrapper.swift index 605a336318f7a..483adede62290 100644 --- a/firefox-ios/Client/Telemetry/GleanWrapper.swift +++ b/firefox-ios/Client/Telemetry/GleanWrapper.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ protocol GleanWrapper { func handleDeeplinkUrl(url: URL) @@ -16,12 +18,18 @@ struct DefaultGleanWrapper: GleanWrapper { public static let shared = DefaultGleanWrapper() func handleDeeplinkUrl(url: URL) { + /* Ecosia: Remove Glean Glean.shared.handleCustomUrl(url: url) + */ } func setUpload(isEnabled: Bool) { + /* Ecosia: Remove Glean Glean.shared.setUploadEnabled(isEnabled) + */ } func submitPing() { + /* Ecosia: Remove Glean GleanMetrics.Pings.shared.firstSession.submit() + */ } } diff --git a/firefox-ios/Client/Telemetry/HomepageTelemetry.swift b/firefox-ios/Client/Telemetry/HomepageTelemetry.swift index 88ec8162a6283..ec8d22c041702 100644 --- a/firefox-ios/Client/Telemetry/HomepageTelemetry.swift +++ b/firefox-ios/Client/Telemetry/HomepageTelemetry.swift @@ -3,11 +3,15 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ struct HomepageTelemetry { func sendHomepageTappedTelemetry(enteringPrivateMode: Bool) { + /* Ecosia: Remove Glean let isPrivateModeExtra = GleanMetrics.Homepage.PrivateModeToggleExtra(isPrivateMode: enteringPrivateMode) GleanMetrics.Homepage.privateModeToggle.record(isPrivateModeExtra) + */ } } diff --git a/firefox-ios/Client/Telemetry/PrivateBrowsingTelemetry.swift b/firefox-ios/Client/Telemetry/PrivateBrowsingTelemetry.swift index 22c9d6cd1ba94..58259c57a204c 100644 --- a/firefox-ios/Client/Telemetry/PrivateBrowsingTelemetry.swift +++ b/firefox-ios/Client/Telemetry/PrivateBrowsingTelemetry.swift @@ -3,11 +3,15 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ struct PrivateBrowsingTelemetry { func sendDataClearanceTappedTelemetry(didConfirm: Bool) { + /* Ecosia: Remove Glean let didConfirmExtra = GleanMetrics.PrivateBrowsing.DataClearanceIconTappedExtra(didConfirm: didConfirm) GleanMetrics.PrivateBrowsing.dataClearanceIconTapped.record(didConfirmExtra) + */ } } diff --git a/firefox-ios/Client/Telemetry/SearchTelemetry.swift b/firefox-ios/Client/Telemetry/SearchTelemetry.swift index bf3d44eec8cef..934c5d8271caf 100644 --- a/firefox-ios/Client/Telemetry/SearchTelemetry.swift +++ b/firefox-ios/Client/Telemetry/SearchTelemetry.swift @@ -4,7 +4,9 @@ import Foundation import WebKit +/* Ecosia: Remove Glean import Glean + */ import Storage /// Type-specific information to record in telemetry about a visible search @@ -170,31 +172,41 @@ class SearchTelemetry { // sap: directly from search access point func trackSAP() { + /* Ecosia: Remove Glean GleanMetrics.Search.inContent["\(provider).in-content.sap.\(code)"].add() + */ } // sap-follow-on: user continues to search from an existing sap search func trackSAPFollowOn() { + /* Ecosia: Remove Glean GleanMetrics.Search.inContent["\(provider).in-content.sap-follow-on.\(code)"].add() + */ } // organic: search that didn't come from a SAP func trackOrganic() { + /* Ecosia: Remove Glean GleanMetrics.Search.inContent["\(provider).organic.none"].add() + */ } // MARK: Google Top Site SAP // Note: This tracks google top site tile tap which opens a google search page func trackGoogleTopSiteTap() { + /* Ecosia: Remove Glean GleanMetrics.Search.googleTopsitePressed["\(SearchEngine.google).\(code)"].add() + */ } // Note: This tracks SAP follow-on search. Also, the first search that the user performs is considered // a follow-on where OQ query item in google url is present but has no data in it // Flow: User taps google top site tile -> google page opens -> user types item to search in the page func trackGoogleTopSiteFollowOn() { + /* Ecosia: Remove Glean GleanMetrics.Search.inContent["\(SearchEngine.google).in-content.google-topsite-follow-on.\(code)"].add() + */ } // MARK: Track Regular and Follow-on SAP from Tab and TopSite diff --git a/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift b/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift index 7ef2a313e456b..1f1ef72c61292 100644 --- a/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift +++ b/firefox-ios/Client/Telemetry/SponsoredTileTelemetry.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ // Telemetry for the Sponsored tiles located in the Top sites on the Firefox home page // Using Pings to send the telemetry events @@ -12,6 +14,7 @@ struct SponsoredTileTelemetry { static let source = "newtab" static func sendImpressionTelemetry(tile: SponsoredTile, position: Int) { + /* Ecosia: remove Glean dependency let extra = GleanMetrics.TopSites.ContileImpressionExtra( position: Int32(position), source: SponsoredTileTelemetry.source @@ -22,9 +25,11 @@ struct SponsoredTileTelemetry { GleanMetrics.TopSites.contileAdvertiser.set(tile.title) GleanMetrics.TopSites.contileReportingUrl.set(tile.impressionURL) GleanMetrics.Pings.shared.topsitesImpression.submit() + */ } static func sendClickTelemetry(tile: SponsoredTile, position: Int) { + /* Ecosia: remove Glean dependency let extra = GleanMetrics.TopSites.ContileClickExtra( position: Int32(position), source: SponsoredTileTelemetry.source @@ -35,5 +40,6 @@ struct SponsoredTileTelemetry { GleanMetrics.TopSites.contileAdvertiser.set(tile.title) GleanMetrics.TopSites.contileReportingUrl.set(tile.clickURL) GleanMetrics.Pings.shared.topsitesImpression.submit() + */ } } diff --git a/firefox-ios/Client/Telemetry/TabErrorTelemetryHelper.swift b/firefox-ios/Client/Telemetry/TabErrorTelemetryHelper.swift index 1d23bc1a52ab6..faed0351129a6 100644 --- a/firefox-ios/Client/Telemetry/TabErrorTelemetryHelper.swift +++ b/firefox-ios/Client/Telemetry/TabErrorTelemetryHelper.swift @@ -53,6 +53,9 @@ final class TabErrorTelemetryHelper { // MARK: - Internal Utility private func getTabCount(window: WindowUUID) -> Int { + // This check indicates that the app was backgrounded while in the onboarding state, + // and the TabManager has not been initialized still. + guard windowManager.windows.first?.value.tabManager != nil else { return 0 } return windowManager.tabManager(for: window).normalTabs.count } diff --git a/firefox-ios/Client/Telemetry/TabsTelemetry.swift b/firefox-ios/Client/Telemetry/TabsTelemetry.swift index c99b410cb8c9f..ec0c0f1678a1c 100644 --- a/firefox-ios/Client/Telemetry/TabsTelemetry.swift +++ b/firefox-ios/Client/Telemetry/TabsTelemetry.swift @@ -3,21 +3,29 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ final class TabsTelemetry { + /* Ecosia: Remove Glean /// Measure with a time distribution https://mozilla.github.io/glean/book/reference/metrics/timing_distribution.html /// how long it takes to switch to a new tab private var tabSwitchTimerId: GleanTimerId? + */ func startTabSwitchMeasurement() { + /* Ecosia: Remove Glean tabSwitchTimerId = GleanMetrics.Tabs.tabSwitch.start() + */ } func stopTabSwitchMeasurement() { + /* Ecosia: Remove Glean guard let timerId = tabSwitchTimerId else { return } GleanMetrics.Tabs.tabSwitch.stopAndAccumulate(timerId) tabSwitchTimerId = nil + */ } static func trackTabsQuantity(tabManager: TabManager) { diff --git a/firefox-ios/Client/Telemetry/TelemetryContextualIdentifier.swift b/firefox-ios/Client/Telemetry/TelemetryContextualIdentifier.swift index 1a43608081d2b..2a874c6a294c0 100644 --- a/firefox-ios/Client/Telemetry/TelemetryContextualIdentifier.swift +++ b/firefox-ios/Client/Telemetry/TelemetryContextualIdentifier.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ // Contextual identifier used for the sponsored tiles in top sites and the suggestions in the search view struct TelemetryContextualIdentifier { @@ -23,10 +25,14 @@ struct TelemetryContextualIdentifier { static func setupContextId() { // Use existing client UUID, if doesn't exists create a new one if let stringContextId = contextId, let clientUUID = UUID(uuidString: stringContextId) { + /* Ecosia: Remove Glean GleanMetrics.TopSites.contextId.set(clientUUID) + */ } else { let newUUID = UUID() + /* Ecosia: Remove Glean GleanMetrics.TopSites.contextId.set(newUUID) + */ contextId = newUUID.uuidString } } diff --git a/firefox-ios/Client/Telemetry/TelemetryWrapper.swift b/firefox-ios/Client/Telemetry/TelemetryWrapper.swift index fd0f9efd877dc..49abb0b5daeef 100644 --- a/firefox-ios/Client/Telemetry/TelemetryWrapper.swift +++ b/firefox-ios/Client/Telemetry/TelemetryWrapper.swift @@ -5,7 +5,9 @@ // Disabling `line_length` for this whole file because it is going to get refactored/replaced soon. // swiftlint:disable line_length import Common +/* Ecosia: Remove Glean import Glean + */ import Shared import Account import Sync @@ -45,7 +47,10 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable { static let shared = TelemetryWrapper() + /* Ecosia: remove Glean dependency let glean = Glean.shared + */ + // Boolean flag to temporarily remember if we crashed during the // last run of the app. We cannot simply use `Sentry.crashedLastLaunch` // because we want to clear this flag after we've already reported it @@ -92,6 +97,8 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable { } func initGlean(_ profile: Profile, sendUsageData: Bool) { + /* Ecosia: remove Glean dependency + // Record default search engine setting to avoid sending a `null` value. // If there's no default search engine, (there's not, at this point), we will // send "unavailable" in order not to send `null`, but still differentiate @@ -138,10 +145,12 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable { name: UIApplication.didFinishLaunchingNotification, object: nil ) + */ } @objc func recordFinishedLaunchingPreferenceMetrics(notification: NSNotification) { + /* Ecosia: remove Glean dependency guard let profile = self.profile else { return } // Pocket stories visible if let pocketStoriesVisible = profile.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.ASPocketStories) { @@ -149,12 +158,14 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable { } else { GleanMetrics.FirefoxHomePage.pocketStoriesVisible.set(true) } + */ } // Function for recording metrics that are better recorded when going to background due // to the particular measurement, or availability of the information. @objc func recordEnteredBackgroundPreferenceMetrics(notification: NSNotification) { + /* Ecosia: remove Glean dependency guard let profile = self.profile else { assertionFailure("Error unwrapping profile") return @@ -268,6 +279,7 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable { let startAtHomeOption = prefs.stringForKey(PrefsKeys.UserFeatureFlagPrefs.StartAtHome) ?? StartAtHomeSetting.afterFourHours.rawValue GleanMetrics.Preferences.openingScreen.set(startAtHomeOption) + */ } } @@ -443,6 +455,8 @@ extension TelemetryWrapper { case creditCardDeleted = "creditCard-deleted" case creditCardModified = "creditCard-modified" case notificationPermission = "notificationPermission" + case defaultBrowser = "defaultBrowser" + case choiceScreenAcquisition = "choiceScreenAcquisition" case engagementNotification = "engagementNotification" // MARK: New Onboarding case onboardingCardView = "onboarding-card-view" @@ -653,6 +667,9 @@ extension TelemetryWrapper { } public enum EventExtraKey: String, CustomStringConvertible { + case isDefaultBrowser = "is-default-browser" + case didComeFromBrowserChoiceScreen = "did-come-from-browser-choice-screen" + case topSitePosition = "tilePosition" case topSiteTileType = "tileType" case contextualMenuType = "contextualMenuType" @@ -794,6 +811,7 @@ extension TelemetryWrapper { } static func gleanRecordEvent(category: EventCategory, method: EventMethod, object: EventObject, value: EventValue? = nil, extras: [String: Any]? = nil) { + /* Ecosia: remove Glean dependency switch (category, method, object, value, extras) { // MARK: Bookmarks case (.action, .view, .bookmarksPanel, let from?, _): @@ -1256,6 +1274,14 @@ extension TelemetryWrapper { value: value, extras: extras) } + case (.action, .open, .defaultBrowser, _, let extras): + if let isDefaultBrowser = extras?[EventExtraKey.isDefaultBrowser.rawValue] as? Bool { + GleanMetrics.App.defaultBrowser.set(isDefaultBrowser) + } + case (.action, .open, .choiceScreenAcquisition, _, let extras): + if let choiceScreen = extras?[EventExtraKey.didComeFromBrowserChoiceScreen.rawValue] as? Bool { + GleanMetrics.App.choiceScreenAcquisition.set(choiceScreen) + } case(.action, .tap, .engagementNotification, _, _): GleanMetrics.Onboarding.engagementNotificationTapped.record() case(.action, .cancel, .engagementNotification, _, _): @@ -2128,6 +2154,7 @@ extension TelemetryWrapper { default: recordUninstrumentedMetrics(category: category, method: method, object: object, value: value, extras: extras) } + */ } private static func recordUninstrumentedMetrics( diff --git a/firefox-ios/Client/Telemetry/WebviewTelemetry.swift b/firefox-ios/Client/Telemetry/WebviewTelemetry.swift index 3994849209b27..60e63152e8e5a 100644 --- a/firefox-ios/Client/Telemetry/WebviewTelemetry.swift +++ b/firefox-ios/Client/Telemetry/WebviewTelemetry.swift @@ -3,26 +3,36 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: Remove Glean import Glean + */ /// Measure with a time distribution https://mozilla.github.io/glean/book/reference/metrics/timing_distribution.html /// how long it takes to load a webpage final class WebViewLoadMeasurementTelemetry { + /* Ecosia: Remove Glean private var loadTimerId: GleanTimerId? + */ func start() { + /* Ecosia: Remove Glean loadTimerId = GleanMetrics.Webview.pageLoad.start() + */ } func stop() { + /* Ecosia: Remove Glean guard let timerId = loadTimerId else { return } GleanMetrics.Webview.pageLoad.stopAndAccumulate(timerId) loadTimerId = nil + */ } func cancel() { + /* Ecosia: Remove Glean if let loadTimerId { GleanMetrics.Webview.pageLoad.cancel(loadTimerId) } + */ } } diff --git a/firefox-ios/Client/Utils/LocaleInterface.swift b/firefox-ios/Client/Utils/LocaleInterface.swift new file mode 100644 index 0000000000000..882ffe3fccd23 --- /dev/null +++ b/firefox-ios/Client/Utils/LocaleInterface.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol LocaleInterface { + var localeRegionCode: String? { get } +} + +extension Locale: LocaleInterface { + var localeRegionCode: String? { + return self.regionCode + } +} diff --git a/firefox-ios/Client/Utils/UIApplicationInterface.swift b/firefox-ios/Client/Utils/UIApplicationInterface.swift new file mode 100644 index 0000000000000..305eb88c74b6b --- /dev/null +++ b/firefox-ios/Client/Utils/UIApplicationInterface.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol UIApplicationInterface { + @available(iOS 18.2, *) + @available(visionOS, unavailable) + @available(macCatalyst, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + @MainActor + @preconcurrency + func isDefault(_ category: UIApplication.Category) throws -> Bool +} + +extension UIApplication: UIApplicationInterface {} diff --git a/firefox-ios/Client/ab.lproj/InfoPlist.strings b/firefox-ios/Client/ab.lproj/InfoPlist.strings new file mode 100644 index 0000000000000..e07e4a82b1219 --- /dev/null +++ b/firefox-ios/Client/ab.lproj/InfoPlist.strings @@ -0,0 +1,24 @@ +/* (No Comment) */ +"New Private Tab" = "Ахатә абӷьыц ҿыц"; + +/* (No Comment) */ +"New Tab" = "Абларҭа ҿыц"; + +/* Privacy - Camera Usage Description */ +"NSCameraUsageDescription" = "Ecosia акамера ахархәоит QR-кодқәа рыҭыхразы, насгьы афотосахьақәеи авидеоқәеи рҭыхразы."; + +/* Privacy - Face ID Usage Description */ +"NSFaceIDUsageDescription" = "Ecosia иаҭахуп Face ID еиқәырханы иҟоу алогинқәеи ишәарҭоу акартақәеи рахь анеиразы."; + +/* Privacy - Location When In Use Usage Description */ +"NSLocationWhenInUseUsageDescription" = "Уахьнеиуа асаитқәа уахьыҟоу рҳәар рылшоит."; + +/* Privacy - Microphone Usage Description */ +"NSMicrophoneUsageDescription" = "Ecosia аудио аҭаҩреи ашьҭыхреи рзы умикрофон ахархәоит."; + +/* Privacy - Photo Library Additions Usage Description */ +"NSPhotoLibraryAddUsageDescription" = "Уи шәара ишәылнаршоит афотосахьақәа рыхьчара."; + +/* (No Comment) */ +"Scan QR Code" = "Асканер QR акод"; + diff --git a/firefox-ios/Client/an.lproj/InfoPlist.strings b/firefox-ios/Client/an.lproj/InfoPlist.strings index 74b38ff377df2..b82b4cc13b0cb 100644 --- a/firefox-ios/Client/an.lproj/InfoPlist.strings +++ b/firefox-ios/Client/an.lproj/InfoPlist.strings @@ -5,10 +5,10 @@ "New Tab" = "Nueva pestanya"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa a tuya camara ta escanear QRs y fer fotos y video."; +"NSCameraUsageDescription" = "Ecosia usa a tuya camara ta escanear QRs y fer fotos y video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox necesita Face ID pa acceder a os tuyos logins guardaus."; +"NSFaceIDUsageDescription" = "Ecosia necesita Face ID pa acceder a os tuyos logins guardaus."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Los puestos web que vesites pueden solicitar conoixer lo tuyo plazamiento."; diff --git a/firefox-ios/Client/ar.lproj/InfoPlist.strings b/firefox-ios/Client/ar.lproj/InfoPlist.strings index a5d368f3146dd..16426eb2edf51 100644 --- a/firefox-ios/Client/ar.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ar.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "لسان جديد"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "يستعمل Firefox كاميراتك لمسح رموز QR رقميًا ولالتقاط الصور والڤِديوهات."; +"NSCameraUsageDescription" = "يستعمل Ecosia كاميراتك لمسح رموز QR رقميًا ولالتقاط الصور والڤِديوهات."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "يتطلب Firefox تفعيل بصمة الوجه للوصول إلى كلمات المرور وطرق الدفع المحفوظة لديك."; +"NSFaceIDUsageDescription" = "يتطلب Ecosia تفعيل بصمة الوجه للوصول إلى كلمات المرور وطرق الدفع المحفوظة لديك."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "يمكن أن تطلب منك المواقع التي تزورها معرفة مكانك."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "يستعمل Firefox الميكروفون لتسجيل الصوت ورفعه."; +"NSMicrophoneUsageDescription" = "يستعمل Ecosia الميكروفون لتسجيل الصوت ورفعه."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "يتيح لك هذا حفظ الصور."; diff --git a/firefox-ios/Client/ast.lproj/InfoPlist.strings b/firefox-ios/Client/ast.lproj/InfoPlist.strings index 9d298bb548b45..1a433b9970c76 100644 --- a/firefox-ios/Client/ast.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ast.lproj/InfoPlist.strings @@ -5,13 +5,13 @@ "New Tab" = "Llingüeta nueva"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa la cámara pa escaniar códigos QR y facer semeyes y vídeos."; +"NSCameraUsageDescription" = "Ecosia usa la cámara pa escaniar códigos QR y facer semeyes y vídeos."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Los sitios web que visites puen solicitate l'allugamientu."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox usa'l micrófonu pa grabar y xubir audiu."; +"NSMicrophoneUsageDescription" = "Ecosia usa'l micrófonu pa grabar y xubir audiu."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Esto déxate guardar les semeyes."; diff --git a/firefox-ios/Client/be.lproj/InfoPlist.strings b/firefox-ios/Client/be.lproj/InfoPlist.strings index 0837cc53b72d4..e401e74154550 100644 --- a/firefox-ios/Client/be.lproj/InfoPlist.strings +++ b/firefox-ios/Client/be.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Новая картка"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox выкарыстоўвае камеру, каб сканаваць QR-коды, здымаць фота і відэа."; +"NSCameraUsageDescription" = "Ecosia выкарыстоўвае камеру, каб сканаваць QR-коды, здымаць фота і відэа."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox патрабуе Face ID для доступу да захаваных пароляў і спосабаў аплаты."; +"NSFaceIDUsageDescription" = "Ecosia патрабуе Face ID для доступу да захаваных пароляў і спосабаў аплаты."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Вэб-сайты, якія вы наведваеце, могуць запытваць ваша месцазнаходжанне."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox выкарыстоўвае мікрафон для запісу і зацягвання аўдыя."; +"NSMicrophoneUsageDescription" = "Ecosia выкарыстоўвае мікрафон для запісу і зацягвання аўдыя."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Гэта дазваляе захоўваць фота."; diff --git a/firefox-ios/Client/bn.lproj/InfoPlist.strings b/firefox-ios/Client/bn.lproj/InfoPlist.strings index fc9417919bdb6..2c4930d20f233 100644 --- a/firefox-ios/Client/bn.lproj/InfoPlist.strings +++ b/firefox-ios/Client/bn.lproj/InfoPlist.strings @@ -5,7 +5,7 @@ "New Tab" = "নতুন ট্যাব"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox আপনার ক্যামেরা ব্যবহার করবে QR কোড স্ক্যান করার জন্য এবং ছবি তোলা ও ভিডিও করার জন্য।"; +"NSCameraUsageDescription" = "Ecosia আপনার ক্যামেরা ব্যবহার করবে QR কোড স্ক্যান করার জন্য এবং ছবি তোলা ও ভিডিও করার জন্য।"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "যে ওয়েবসাইট আপনি ব্রাউজ করছেন, তা আপনার অবস্থান জানতে চাইতে পারে।"; diff --git a/firefox-ios/Client/br.lproj/InfoPlist.strings b/firefox-ios/Client/br.lproj/InfoPlist.strings index 699380833ad8e..c304a88b482ff 100644 --- a/firefox-ios/Client/br.lproj/InfoPlist.strings +++ b/firefox-ios/Client/br.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Gant se e c’hallit kemer ha pellgas luc’hskeudennoù."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Face ID a zo rekis evit ma c’hallfe kaout Firefox ho kerioù-tremen enrollet hag ho toareoù paeañ."; +"NSFaceIDUsageDescription" = "Face ID a zo rekis evit ma c’hallfe kaout Ecosia ho kerioù-tremen enrollet hag ho toareoù paeañ."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Lec’hiennoù web a weladennit a c’hall azgoulenn ho lec’hiadur."; diff --git a/firefox-ios/Client/bs.lproj/InfoPlist.strings b/firefox-ios/Client/bs.lproj/InfoPlist.strings index 8dfea74a7a25b..8933929bc877a 100644 --- a/firefox-ios/Client/bs.lproj/InfoPlist.strings +++ b/firefox-ios/Client/bs.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Novi tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Ovo vam omogućava slikanje i otpremanje fotografija."; +"NSCameraUsageDescription" = "Ecosia koristi vašu kameru za skeniranje QR kodova i snimanje fotografija i video zapisa."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox zahtijeva Face ID za pristup vašim sačuvanim lozinkama i načinima plaćanja."; +"NSFaceIDUsageDescription" = "Ecosia zahtijeva Face ID za pristup vašim sačuvanim lozinkama i načinima plaćanja."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Web stranice koje posjećujete mogu zahtjevati vašu lokaciju."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Ovo vam omogućava snimanje i otpremanje videa."; +"NSMicrophoneUsageDescription" = "Ecosia koristi vaš mikrofon za snimanje i prijenos zvuka."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Ovo vam omogućava da sačuvate fotografije."; diff --git a/firefox-ios/Client/ca.lproj/InfoPlist.strings b/firefox-ios/Client/ca.lproj/InfoPlist.strings index 249811a8c01c1..3a4adbd6f4470 100644 --- a/firefox-ios/Client/ca.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ca.lproj/InfoPlist.strings @@ -5,10 +5,10 @@ "New Tab" = "Pestanya nova"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "El Firefox usa la càmera per a escanejar codis QR i fer fotos i vídeos."; +"NSCameraUsageDescription" = "El Ecosia usa la càmera per a escanejar codis QR i fer fotos i vídeos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "El Firefox necessita el Face ID per accedir a les contrasenyes i formes de pagament que heu desat."; +"NSFaceIDUsageDescription" = "El Ecosia necessita el Face ID per accedir a les contrasenyes i formes de pagament que heu desat."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Els llocs web que visiteu poden sol·licitar la vostra ubicació."; diff --git a/firefox-ios/Client/co.lproj/InfoPlist.strings b/firefox-ios/Client/co.lproj/InfoPlist.strings index 45050da915526..ab6937c5c2913 100644 --- a/firefox-ios/Client/co.lproj/InfoPlist.strings +++ b/firefox-ios/Client/co.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nova unghjetta"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox impiega u vostru apparechju-fotò per analizà i codici QR è per piglià fotò è video."; +"NSCameraUsageDescription" = "Ecosia impiega u vostru apparechju-fotò per analizà i codici QR è per piglià fotò è video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox richiede Face ID per accede à e vostre parolle d’intesa è metode di pagamentu arregistrate."; +"NSFaceIDUsageDescription" = "Ecosia richiede Face ID per accede à e vostre parolle d’intesa è metode di pagamentu arregistrate."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "I siti web chì vò visitate ponu richiede à cunnosce a vostra lucalizazione."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox impiega u vostru microfonu per arregistrà è incaricà schedarii audio."; +"NSMicrophoneUsageDescription" = "Ecosia impiega u vostru microfonu per arregistrà è incaricà schedarii audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Quessu vi permette d’arregistrà fotò."; diff --git a/firefox-ios/Client/cs.lproj/InfoPlist.strings b/firefox-ios/Client/cs.lproj/InfoPlist.strings index eca515271bf5e..798d75361d456 100644 --- a/firefox-ios/Client/cs.lproj/InfoPlist.strings +++ b/firefox-ios/Client/cs.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nový panel"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox používá váš fotoaparát ke skenování QR kódů a pořizování fotografií a videí."; +"NSCameraUsageDescription" = "Ecosia používá váš fotoaparát ke skenování QR kódů a pořizování fotografií a videí."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox vyžaduje Face ID pro přístup k uloženým heslům a platebním metodám."; +"NSFaceIDUsageDescription" = "Ecosia vyžaduje Face ID pro přístup k uloženým heslům a platebním metodám."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Servery, které navštěvujete, mohou chtít znát vaše umístění."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox používá váš mikrofon k zaznamenávání a odesílání zvuku."; +"NSMicrophoneUsageDescription" = "Ecosia používá váš mikrofon k zaznamenávání a odesílání zvuku."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Toto vám umožní ukládat fotografie."; diff --git a/firefox-ios/Client/cy.lproj/InfoPlist.strings b/firefox-ios/Client/cy.lproj/InfoPlist.strings index eebe1d48a7724..fb074af707c07 100644 --- a/firefox-ios/Client/cy.lproj/InfoPlist.strings +++ b/firefox-ios/Client/cy.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Mae hwn yn caniatáu i chi gymryd lluniau a'u llwytho i fyny."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Mae Firefox angen Face ID i gael mynediad i'ch cyfrineiriau a'ch dulliau talu sydd wedi'u cadw."; +"NSFaceIDUsageDescription" = "Mae Ecosia angen Face ID i gael mynediad i'ch cyfrineiriau a'ch dulliau talu sydd wedi'u cadw."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Efallai y bydd gwefannau rydych yn ymweld â nhw'n gofyn am eich lleoliad."; diff --git a/firefox-ios/Client/da.lproj/InfoPlist.strings b/firefox-ios/Client/da.lproj/InfoPlist.strings index 42a38512de903..697e525d7f76c 100644 --- a/firefox-ios/Client/da.lproj/InfoPlist.strings +++ b/firefox-ios/Client/da.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nyt faneblad"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox bruger dit kamera til at skanne QR-koder, tage billeder og optage video."; +"NSCameraUsageDescription" = "Ecosia bruger dit kamera til at skanne QR-koder, tage billeder og optage video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox kræver Face ID for at få adgang til dine gemte adgangskoder og betalingsmetoder."; +"NSFaceIDUsageDescription" = "Ecosia kræver Face ID for at få adgang til dine gemte adgangskoder og betalingsmetoder."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Websteder du besøger kan bede om din position."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox bruger din mikrofon til at optage og uploade lyd."; +"NSMicrophoneUsageDescription" = "Ecosia bruger din mikrofon til at optage og uploade lyd."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Lader dig gemme fotos."; diff --git a/firefox-ios/Client/de.lproj/InfoPlist.strings b/firefox-ios/Client/de.lproj/InfoPlist.strings index 3053eb30bbc8f..df1eaae01053d 100644 --- a/firefox-ios/Client/de.lproj/InfoPlist.strings +++ b/firefox-ios/Client/de.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Neuer Tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox verwendet Ihre Kamera, um QR-Codes zu scannen sowie Fotos und Videos aufzunehmen."; +"NSCameraUsageDescription" = "Ecosia verwendet Ihre Kamera, um QR-Codes zu scannen sowie Fotos und Videos aufzunehmen."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox benötigt Face ID, um auf Ihre gespeicherten Passwörter und Zahlungsmethoden zuzugreifen."; +"NSFaceIDUsageDescription" = "Ecosia benötigt Face ID, um auf Ihre gespeicherten Passwörter und Zahlungsmethoden zuzugreifen."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Von Ihnen besuchte Websites können Ihren Standort abfragen."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox verwendet Ihr Mikrofon, um Sprachaufnahmen aufzunehmen und hochzuladen."; +"NSMicrophoneUsageDescription" = "Ecosia verwendet Ihr Mikrofon, um Sprachaufnahmen aufzunehmen und hochzuladen."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Damit können Sie Ihre Fotos speichern."; diff --git a/firefox-ios/Client/dsb.lproj/InfoPlist.strings b/firefox-ios/Client/dsb.lproj/InfoPlist.strings index 6b54297cdafae..62fe4a81757ba 100644 --- a/firefox-ios/Client/dsb.lproj/InfoPlist.strings +++ b/firefox-ios/Client/dsb.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nowy rejtarik"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox wašu kameru wužywa, aby QR-kody skannował a fota cynił a wideo nagrał."; +"NSCameraUsageDescription" = "Ecosia wašu kameru wužywa, aby QR-kody skannował a fota cynił a wideo nagrał."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox Face ID trjeba, aby pśistup k wašym skłaźonym gronidłam a płaśeńskim metodam měł."; +"NSFaceIDUsageDescription" = "Ecosia Face ID trjeba, aby pśistup k wašym skłaźonym gronidłam a płaśeńskim metodam měł."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Websedła, kótarež se woglědujośo, pominaju snaź wašo městno."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox waš mikrofon wužywa, aby awdio registrěrował a nagrał."; +"NSMicrophoneUsageDescription" = "Ecosia waš mikrofon wužywa, aby awdio registrěrował a nagrał."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Z tym móžośo fota składowaś."; diff --git a/firefox-ios/Client/el.lproj/InfoPlist.strings b/firefox-ios/Client/el.lproj/InfoPlist.strings index 119eefa73666d..36e34be1cdf5e 100644 --- a/firefox-ios/Client/el.lproj/InfoPlist.strings +++ b/firefox-ios/Client/el.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Νέα καρτέλα"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Το Firefox χρησιμοποιεί την κάμερα για τη σάρωση κωδικών QR και τη λήψη φωτογραφιών και βίντεο."; +"NSCameraUsageDescription" = "Το Ecosia χρησιμοποιεί την κάμερα για τη σάρωση κωδικών QR και τη λήψη φωτογραφιών και βίντεο."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Το Firefox απαιτεί το Face ID για την πρόσβαση σε αποθηκευμένους κωδικούς πρόσβασης και μεθόδους πληρωμής."; +"NSFaceIDUsageDescription" = "Το Ecosia απαιτεί το Face ID για την πρόσβαση σε αποθηκευμένους κωδικούς πρόσβασης και μεθόδους πληρωμής."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Οι ιστότοποι που επισκέπτεστε ενδέχεται να ζητήσουν την τοποθεσία σας."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Το Firefox χρησιμοποιεί το μικρόφωνο για την καταγραφή και τη μεταφόρτωση ήχου."; +"NSMicrophoneUsageDescription" = "Το Ecosia χρησιμοποιεί το μικρόφωνο για την καταγραφή και τη μεταφόρτωση ήχου."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Επιτρέπει την αποθήκευση φωτογραφιών."; diff --git a/firefox-ios/Client/en-CA.lproj/InfoPlist.strings b/firefox-ios/Client/en-CA.lproj/InfoPlist.strings index 4518590f4cac5..0613c073c61f5 100644 --- a/firefox-ios/Client/en-CA.lproj/InfoPlist.strings +++ b/firefox-ios/Client/en-CA.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "New Tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox uses your camera to scan QR codes and take photos and video."; +"NSCameraUsageDescription" = "Ecosia uses your camera to scan QR codes and take photos and video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requires Face ID to access your saved passwords and payment methods."; +"NSFaceIDUsageDescription" = "Ecosia requires Face ID to access your saved passwords and payment methods."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Websites you visit may request your location."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox uses your microphone to record and upload audio."; +"NSMicrophoneUsageDescription" = "Ecosia uses your microphone to record and upload audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "This lets you save photos."; diff --git a/firefox-ios/Client/en-GB.lproj/InfoPlist.strings b/firefox-ios/Client/en-GB.lproj/InfoPlist.strings index 02956331cfa41..d3a9f90528061 100644 --- a/firefox-ios/Client/en-GB.lproj/InfoPlist.strings +++ b/firefox-ios/Client/en-GB.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "New Tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox uses your camera to scan QR codes and take photos and video."; +"NSCameraUsageDescription" = "Ecosia uses your camera to scan QR codes and take photos and video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requires Face ID to access your saved passwords and payment methods."; +"NSFaceIDUsageDescription" = "Ecosia requires Face ID to access your saved passwords and payment methods."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Web sites you visit may request your location."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox uses your microphone to record and upload audio."; +"NSMicrophoneUsageDescription" = "Ecosia uses your microphone to record and upload audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "This lets you save photos."; diff --git a/firefox-ios/Client/en-US.lproj/InfoPlist.strings b/firefox-ios/Client/en-US.lproj/InfoPlist.strings index 4518590f4cac5..0613c073c61f5 100644 --- a/firefox-ios/Client/en-US.lproj/InfoPlist.strings +++ b/firefox-ios/Client/en-US.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "New Tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox uses your camera to scan QR codes and take photos and video."; +"NSCameraUsageDescription" = "Ecosia uses your camera to scan QR codes and take photos and video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requires Face ID to access your saved passwords and payment methods."; +"NSFaceIDUsageDescription" = "Ecosia requires Face ID to access your saved passwords and payment methods."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Websites you visit may request your location."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox uses your microphone to record and upload audio."; +"NSMicrophoneUsageDescription" = "Ecosia uses your microphone to record and upload audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "This lets you save photos."; diff --git a/firefox-ios/Client/en.lproj/InfoPlist.strings b/firefox-ios/Client/en.lproj/InfoPlist.strings index 4405ef18660d8..07d062c4f71fa 100644 --- a/firefox-ios/Client/en.lproj/InfoPlist.strings +++ b/firefox-ios/Client/en.lproj/InfoPlist.strings @@ -1,8 +1,8 @@ NSLocationWhenInUseUsageDescription = "Websites you visit may request your location."; NSPhotoLibraryAddUsageDescription = "This lets you save photos."; -NSCameraUsageDescription = "Firefox uses your camera to scan QR codes and take photos and video."; -NSMicrophoneUsageDescription = "Firefox uses your microphone to record and upload audio."; +NSCameraUsageDescription = "Ecosia uses your camera to scan QR codes and take photos and video."; +NSMicrophoneUsageDescription = "Ecosia uses your microphone to record and upload audio."; "New Tab" = "New Tab"; "New Private Tab" = "New Private Tab"; "Scan QR Code" = "Scan QR Code"; diff --git a/firefox-ios/Client/eo.lproj/InfoPlist.strings b/firefox-ios/Client/eo.lproj/InfoPlist.strings index 1633f424f5126..fee43be2a7354 100644 --- a/firefox-ios/Client/eo.lproj/InfoPlist.strings +++ b/firefox-ios/Client/eo.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Tio ĉi permesas al vi preni kaj alŝuti fotojn."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox postulas Face ID por aliri viajn konservitajn legitimilojn kaj pagmetodojn."; +"NSFaceIDUsageDescription" = "Ecosia postulas Face ID por aliri viajn konservitajn legitimilojn kaj pagmetodojn."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Retejoj vizititaj de vi povas peti vian lokon."; diff --git a/firefox-ios/Client/es-AR.lproj/InfoPlist.strings b/firefox-ios/Client/es-AR.lproj/InfoPlist.strings index 3373767a12a96..07b5c9da72bab 100644 --- a/firefox-ios/Client/es-AR.lproj/InfoPlist.strings +++ b/firefox-ios/Client/es-AR.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nueva pestaña"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa la cámara para escanear los códigos QR, tomar fotos y grabar video."; +"NSCameraUsageDescription" = "Ecosia usa la cámara para escanear los códigos QR, tomar fotos y grabar video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requiere Face ID para acceder a tus contraseñas guardadas y métodos de pago."; +"NSFaceIDUsageDescription" = "Ecosia requiere Face ID para acceder a tus contraseñas guardadas y métodos de pago."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Los sitios web que visités pueden solicitar tu ubicación."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox usa tu micrófono para grabar y cargar audio."; +"NSMicrophoneUsageDescription" = "Ecosia usa tu micrófono para grabar y cargar audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Esto te permite guardar las fotos."; diff --git a/firefox-ios/Client/es-CL.lproj/InfoPlist.strings b/firefox-ios/Client/es-CL.lproj/InfoPlist.strings index 5372122e5ed9c..ce003374a8ee6 100644 --- a/firefox-ios/Client/es-CL.lproj/InfoPlist.strings +++ b/firefox-ios/Client/es-CL.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nueva pestaña"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa tu cámara para escanear códigos QR y tomar fotos y videos."; +"NSCameraUsageDescription" = "Ecosia usa tu cámara para escanear códigos QR y tomar fotos y videos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requiere Face ID para acceder a sus contraseñas guardadas y métodos de pago."; +"NSFaceIDUsageDescription" = "Ecosia requiere Face ID para acceder a sus contraseñas guardadas y métodos de pago."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Las páginas que visites podrían solicitarte tu ubicación."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox usa tu micrófono para grabar y subir audio."; +"NSMicrophoneUsageDescription" = "Ecosia usa tu micrófono para grabar y subir audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Esto te permite guardar fotos."; diff --git a/firefox-ios/Client/es-MX.lproj/InfoPlist.strings b/firefox-ios/Client/es-MX.lproj/InfoPlist.strings index 4a1e1f435ef6e..ee94bb1c1add6 100644 --- a/firefox-ios/Client/es-MX.lproj/InfoPlist.strings +++ b/firefox-ios/Client/es-MX.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nueva pestaña"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa tu cámara para escanear códigos QR y para tomar fotos y vídeo."; +"NSCameraUsageDescription" = "Ecosia usa tu cámara para escanear códigos QR y para tomar fotos y vídeo."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requiere Face ID - reconocimiento facial-para acceder a tus contraseñas guardadas y métodos de pago."; +"NSFaceIDUsageDescription" = "Ecosia requiere Face ID - reconocimiento facial-para acceder a tus contraseñas guardadas y métodos de pago."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Los sitios web que visites pueden solicitar tu ubicación."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox usa tu micrófono para grabar y subir audio."; +"NSMicrophoneUsageDescription" = "Ecosia usa tu micrófono para grabar y subir audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Esto permite que guardes tus fotos."; diff --git a/firefox-ios/Client/es.lproj/InfoPlist.strings b/firefox-ios/Client/es.lproj/InfoPlist.strings index d99e1e56924e7..c08c3c169cdd7 100644 --- a/firefox-ios/Client/es.lproj/InfoPlist.strings +++ b/firefox-ios/Client/es.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nueva pestaña"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa tu cámara para escanear códigos QR y realizar fotos y videos."; +"NSCameraUsageDescription" = "Ecosia usa tu cámara para escanear códigos QR y realizar fotos y videos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox requiere Face ID para acceder a tus contraseñas y métodos de pago guardados."; +"NSFaceIDUsageDescription" = "Ecosia requiere Face ID para acceder a tus contraseñas y métodos de pago guardados."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Los sitios web que visitas pueden pedir tu ubicación."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox usa tu micrófono para grabar y subir audio."; +"NSMicrophoneUsageDescription" = "Ecosia usa tu micrófono para grabar y subir audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Esto te permite guardar fotos."; diff --git a/firefox-ios/Client/et.lproj/InfoPlist.strings b/firefox-ios/Client/et.lproj/InfoPlist.strings index 410d2ee0545e7..f8c2fcb1e9e2b 100644 --- a/firefox-ios/Client/et.lproj/InfoPlist.strings +++ b/firefox-ios/Client/et.lproj/InfoPlist.strings @@ -5,13 +5,13 @@ "New Tab" = "Uus kaart"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox kasutab sinu kaamerat QR-koodide skannimiseks ning fotode ja videote tegemiseks."; +"NSCameraUsageDescription" = "Ecosia kasutab sinu kaamerat QR-koodide skannimiseks ning fotode ja videote tegemiseks."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Külastatavad saidid võivad küsida sinu asukohta."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox kasutab mikrofoni heli salvestamiseks ja üleslaadimiseks."; +"NSMicrophoneUsageDescription" = "Ecosia kasutab mikrofoni heli salvestamiseks ja üleslaadimiseks."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "See võimaldab sul salvestada fotosid."; diff --git a/firefox-ios/Client/eu.lproj/InfoPlist.strings b/firefox-ios/Client/eu.lproj/InfoPlist.strings index 7725441d009a5..9d9df3d0a8347 100644 --- a/firefox-ios/Client/eu.lproj/InfoPlist.strings +++ b/firefox-ios/Client/eu.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Fitxa berria"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefoxek zure kamera erabiltzen du QR kodeak eskaneatu, argazkiak atera eta bideoak grabatzeko."; +"NSCameraUsageDescription" = "Ecosiaek zure kamera erabiltzen du QR kodeak eskaneatu, argazkiak atera eta bideoak grabatzeko."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefoxek Face ID behar du zure gordetako pasahitz eta ordainketa metodoetara sarbidea izateko."; +"NSFaceIDUsageDescription" = "Ecosiaek Face ID behar du zure gordetako pasahitz eta ordainketa metodoetara sarbidea izateko."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Bisitatutako webguneek zure kokapena eska lezakete."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefoxek zure mikrofonoa erabiltzen du audioa grabatu eta igotzeko."; +"NSMicrophoneUsageDescription" = "Ecosiaek zure mikrofonoa erabiltzen du audioa grabatu eta igotzeko."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Honekin argazkiak gorde ditzakezu."; diff --git a/firefox-ios/Client/fi.lproj/InfoPlist.strings b/firefox-ios/Client/fi.lproj/InfoPlist.strings index 398492c22865f..2482175bc2dc3 100644 --- a/firefox-ios/Client/fi.lproj/InfoPlist.strings +++ b/firefox-ios/Client/fi.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Uusi välilehti"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox käyttää kameraasi skannatakseen QR-koodeja ja ottaakseen valokuvia ja videoita."; +"NSCameraUsageDescription" = "Ecosia käyttää kameraasi skannatakseen QR-koodeja ja ottaakseen valokuvia ja videoita."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox vaatii Face ID:n käyttääkseen tallennettuja salasanoja ja maksutapoja."; +"NSFaceIDUsageDescription" = "Ecosia vaatii Face ID:n käyttääkseen tallennettuja salasanoja ja maksutapoja."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Vierailemasi sivustot voivat kysyä lupaa sijaintiisi."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox käyttää mikrofoniasi tallentamaan ja lähettämään ääntä."; +"NSMicrophoneUsageDescription" = "Ecosia käyttää mikrofoniasi tallentamaan ja lähettämään ääntä."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Näin voit tallentaa kuvia."; diff --git a/firefox-ios/Client/fil.lproj/InfoPlist.strings b/firefox-ios/Client/fil.lproj/InfoPlist.strings index b14a0e79bbcef..80635deefb092 100644 --- a/firefox-ios/Client/fil.lproj/InfoPlist.strings +++ b/firefox-ios/Client/fil.lproj/InfoPlist.strings @@ -5,7 +5,7 @@ "New Tab" = "Bagong Tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox uses your camera to scan QR codes and take photos and video."; +"NSCameraUsageDescription" = "Ecosia uses your camera to scan QR codes and take photos and video."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Maaaring hingiin ang iyong lokasyon ng binibisitang website"; diff --git a/firefox-ios/Client/fr.lproj/InfoPlist.strings b/firefox-ios/Client/fr.lproj/InfoPlist.strings index 554f33febe4c9..ed6079b16408e 100644 --- a/firefox-ios/Client/fr.lproj/InfoPlist.strings +++ b/firefox-ios/Client/fr.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nouvel onglet"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox utilise votre appareil photo pour scanner les codes QR et prendre des photos et vidéos."; +"NSCameraUsageDescription" = "Ecosia utilise votre appareil photo pour scanner les codes QR et prendre des photos et vidéos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox a besoin de Face ID pour accéder à vos mots de passe et moyens de paiement enregistrés."; +"NSFaceIDUsageDescription" = "Ecosia a besoin de Face ID pour accéder à vos mots de passe et moyens de paiement enregistrés."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Les sites web que vous visitez peuvent demander à connaître votre emplacement."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox utilise votre microphone pour enregistrer et charger des fichiers audio."; +"NSMicrophoneUsageDescription" = "Ecosia utilise votre microphone pour enregistrer et charger des fichiers audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Ceci vous permet d’enregistrer des photos."; diff --git a/firefox-ios/Client/gd.lproj/InfoPlist.strings b/firefox-ios/Client/gd.lproj/InfoPlist.strings index 004902438f2a8..0c93205ca0721 100644 --- a/firefox-ios/Client/gd.lproj/InfoPlist.strings +++ b/firefox-ios/Client/gd.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Bheir seo comas dhut dealbhan a thogail is a luchdadh suas."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Feumaidh Firefox Face ID mus fhaigh e cothrom air na faclan-faire is dòighean pàighidh a shàbhail thu."; +"NSFaceIDUsageDescription" = "Feumaidh Ecosia Face ID mus fhaigh e cothrom air na faclan-faire is dòighean pàighidh a shàbhail thu."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Dh’fhaoidte gun iarr làraichean-lìn air an tadhal thu d’ ionad."; diff --git a/firefox-ios/Client/gl.lproj/InfoPlist.strings b/firefox-ios/Client/gl.lproj/InfoPlist.strings index 9fee1558dad5b..598974d6dfea1 100644 --- a/firefox-ios/Client/gl.lproj/InfoPlist.strings +++ b/firefox-ios/Client/gl.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Isto permítelle facer fotos e subilas."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox require Face ID para acceder aos teus contrasinais e aos métodos de pago gardados."; +"NSFaceIDUsageDescription" = "Ecosia require Face ID para acceder aos teus contrasinais e aos métodos de pago gardados."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Os sitios web que visita poden solicitar a súa localización."; diff --git a/firefox-ios/Client/he.lproj/InfoPlist.strings b/firefox-ios/Client/he.lproj/InfoPlist.strings index 180ff6159aae6..497df5a9ce94b 100644 --- a/firefox-ios/Client/he.lproj/InfoPlist.strings +++ b/firefox-ios/Client/he.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "לשונית חדשה"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "‏Firefox משתמש במצלמה שלך כדי לסרוק קודי QR, לצלם תמונות ולהקליט סרטונים."; +"NSCameraUsageDescription" = "‏Ecosia משתמש במצלמה שלך כדי לסרוק קודי QR, לצלם תמונות ולהקליט סרטונים."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox‏ דורש שימוש ב־Face ID כדי לגשת לססמאות ולאמצעי התשלום השמורים שלך."; +"NSFaceIDUsageDescription" = "Ecosia‏ דורש שימוש ב־Face ID כדי לגשת לססמאות ולאמצעי התשלום השמורים שלך."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "אתרים אליהם נכנסים עשויים לבקש את המיקום של המשתמש."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "‏Firefox משתמש במיקרופון שלך כדי להקליט ולהעלות אודיו."; +"NSMicrophoneUsageDescription" = "‏Ecosia משתמש במיקרופון שלך כדי להקליט ולהעלות אודיו."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "זה מאפשר לך לשמור תמונות."; diff --git a/firefox-ios/Client/hr.lproj/InfoPlist.strings b/firefox-ios/Client/hr.lproj/InfoPlist.strings index fb1de40637ba0..3d0565f8690c3 100644 --- a/firefox-ios/Client/hr.lproj/InfoPlist.strings +++ b/firefox-ios/Client/hr.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nova kartica"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox koristi tvoju kameru za snimanje QR kodova, fotografija i videa."; +"NSCameraUsageDescription" = "Ecosia koristi tvoju kameru za snimanje QR kodova, fotografija i videa."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox zahtijeva Face ID za pristup tvojim spremljenim lozinkama i načinima plaćanja."; +"NSFaceIDUsageDescription" = "Ecosia zahtijeva Face ID za pristup tvojim spremljenim lozinkama i načinima plaćanja."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Web stranice koje posjećuješ mogu zatražiti tvoju lokaciju."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox koristi tvoj mikrofon za snimanje i prijenos audiosnimaka."; +"NSMicrophoneUsageDescription" = "Ecosia koristi tvoj mikrofon za snimanje i prijenos audiosnimaka."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Ovo omogućuje spremanje fotografija."; diff --git a/firefox-ios/Client/hsb.lproj/InfoPlist.strings b/firefox-ios/Client/hsb.lproj/InfoPlist.strings index 4cb6a185cb170..fd7deeb138847 100644 --- a/firefox-ios/Client/hsb.lproj/InfoPlist.strings +++ b/firefox-ios/Client/hsb.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nowy rajtark"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox wašu kameru wužiwa, zo by QR-kody skenował a fota činił a wideja natočił."; +"NSCameraUsageDescription" = "Ecosia wašu kameru wužiwa, zo by QR-kody skenował a fota činił a wideja natočił."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox Face ID trjeba, zo by přistup k wašim składowanym hesłam a płaćenskim metodam měł."; +"NSFaceIDUsageDescription" = "Ecosia Face ID trjeba, zo by přistup k wašim składowanym hesłam a płaćenskim metodam měł."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Websydła, kotrež wopytujeće, snano waše stejnišćo žadaja."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox waš mikrofon wužiwa, zo by awdio natočił a nahrał."; +"NSMicrophoneUsageDescription" = "Ecosia waš mikrofon wužiwa, zo by awdio natočił a nahrał."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Z tym móžeće fota składować."; diff --git a/firefox-ios/Client/hu.lproj/InfoPlist.strings b/firefox-ios/Client/hu.lproj/InfoPlist.strings index a765d3f44fb55..dccdcbb2ff3e1 100644 --- a/firefox-ios/Client/hu.lproj/InfoPlist.strings +++ b/firefox-ios/Client/hu.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Ez lehetővé teszi, hogy fényképeket készítsen és töltsön fel."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "A Firefox a Face ID-t igényli a mentett jelszavak és a fizetési módok eléréséhez."; +"NSFaceIDUsageDescription" = "A Ecosia a Face ID-t igényli a mentett jelszavak és a fizetési módok eléréséhez."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "A meglátogatott webhelyek kérhetik az Ön helyét."; diff --git a/firefox-ios/Client/hy-AM.lproj/InfoPlist.strings b/firefox-ios/Client/hy-AM.lproj/InfoPlist.strings index 745b216cfb613..eae6d7cb1f005 100644 --- a/firefox-ios/Client/hy-AM.lproj/InfoPlist.strings +++ b/firefox-ios/Client/hy-AM.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Նոր ներդիր"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox-ն օգտագործում է Ձեր տեսախցիկը՝ սկանավորելու QR կոդերը և լուսանկարելու ու տեսանկարելու համար:"; +"NSCameraUsageDescription" = "Ecosia-ն օգտագործում է Ձեր տեսախցիկը՝ սկանավորելու QR կոդերը և լուսանկարելու ու տեսանկարելու համար:"; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox-ը պահանջում է Face ID՝ Ձեր պահված գաղտնաբառերին և վճարման եղանակներին հասանելիություն ունենալու համար:"; +"NSFaceIDUsageDescription" = "Ecosia-ը պահանջում է Face ID՝ Ձեր պահված գաղտնաբառերին և վճարման եղանակներին հասանելիություն ունենալու համար:"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Ձեր այցելած կայքերը կարող են հարցնել ձեր տեղադրությունը։"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox-ն օգտագործում է Ձեր խոսափողը՝ ձայնագրման և ձայնանյութի վերբեռնման համար:"; +"NSMicrophoneUsageDescription" = "Ecosia-ն օգտագործում է Ձեր խոսափողը՝ ձայնագրման և ձայնանյութի վերբեռնման համար:"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Սա ձեզ թույլ է տալիս պահպանել լուսանկարներ։"; diff --git a/firefox-ios/Client/ia.lproj/InfoPlist.strings b/firefox-ios/Client/ia.lproj/InfoPlist.strings index ab94c2e5ce8b0..f034887f6835d 100644 --- a/firefox-ios/Client/ia.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ia.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nove scheda"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox usa tu camera pro scannar codices QR e pro prender photos e videos."; +"NSCameraUsageDescription" = "Ecosia usa tu camera pro scannar codices QR e pro prender photos e videos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox require Face ID pro acceder a tu contrasignos e methodos de pagamento salvate."; +"NSFaceIDUsageDescription" = "Ecosia require Face ID pro acceder a tu contrasignos e methodos de pagamento salvate."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Le sitos web que tu visita pote requestar tu position geographic."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox usa tu microphono pro registrar e incargar audio."; +"NSMicrophoneUsageDescription" = "Ecosia usa tu microphono pro registrar e incargar audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Isto te permitte salvar photos."; diff --git a/firefox-ios/Client/id.lproj/InfoPlist.strings b/firefox-ios/Client/id.lproj/InfoPlist.strings index 082cb822d8812..17d1c41c54c2b 100644 --- a/firefox-ios/Client/id.lproj/InfoPlist.strings +++ b/firefox-ios/Client/id.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Tab Baru"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox menggunakan kamera Anda untuk memindai kode QR dan mengambil foto dan video."; +"NSCameraUsageDescription" = "Ecosia menggunakan kamera Anda untuk memindai kode QR dan mengambil foto dan video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox memerlukan Face ID untuk mengakses kata sandi dan metode pembayaran Anda yang tersimpan."; +"NSFaceIDUsageDescription" = "Ecosia memerlukan Face ID untuk mengakses kata sandi dan metode pembayaran Anda yang tersimpan."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Situs web yang Anda kunjungi dapat meminta lokasi Anda."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox menggunakan mikrofon Anda untuk merekam dan mengunggah audio."; +"NSMicrophoneUsageDescription" = "Ecosia menggunakan mikrofon Anda untuk merekam dan mengunggah audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Ini memungkinkan Anda menyimpan foto."; diff --git a/firefox-ios/Client/is.lproj/InfoPlist.strings b/firefox-ios/Client/is.lproj/InfoPlist.strings index 6310437c657c4..48ecdb04a7c2b 100644 --- a/firefox-ios/Client/is.lproj/InfoPlist.strings +++ b/firefox-ios/Client/is.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nýr flipi"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox notar myndavélina þína til að skanna QR-kóða og taka myndir og myndskeið."; +"NSCameraUsageDescription" = "Ecosia notar myndavélina þína til að skanna QR-kóða og taka myndir og myndskeið."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox krefst notkunar Face ID til að fá aðgang að vistuðum lykilorðum þínum og greiðslumátum."; +"NSFaceIDUsageDescription" = "Ecosia krefst notkunar Face ID til að fá aðgang að vistuðum lykilorðum þínum og greiðslumátum."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Vefsvæði sem þú skoðar gætu beðið um staðsetninguna þína."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox notar hljóðnemann þinn til að taka upp og senda inn hljóð."; +"NSMicrophoneUsageDescription" = "Ecosia notar hljóðnemann þinn til að taka upp og senda inn hljóð."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Hér getur þú vistað myndir."; diff --git a/firefox-ios/Client/it.lproj/InfoPlist.strings b/firefox-ios/Client/it.lproj/InfoPlist.strings index b5119da2b5980..bbfab2f5562a8 100644 --- a/firefox-ios/Client/it.lproj/InfoPlist.strings +++ b/firefox-ios/Client/it.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nuova scheda"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox utilizza la fotocamera per eseguire la scansione di codici QR, scattare foto e registrare video."; +"NSCameraUsageDescription" = "Ecosia utilizza la fotocamera per eseguire la scansione di codici QR, scattare foto e registrare video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox richiede Face ID per accedere alle password e ai metodi di pagamento salvati."; +"NSFaceIDUsageDescription" = "Ecosia richiede Face ID per accedere alle password e ai metodi di pagamento salvati."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "I siti web visitati potrebbero richiedere la posizione corrente."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox utilizza il microfono per registrare e caricare audio."; +"NSMicrophoneUsageDescription" = "Ecosia utilizza il microfono per registrare e caricare audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Garantisci il permesso di salvare foto."; diff --git a/firefox-ios/Client/ja.lproj/InfoPlist.strings b/firefox-ios/Client/ja.lproj/InfoPlist.strings index 43bfd018dd88e..e23bfaaab3b99 100644 --- a/firefox-ios/Client/ja.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ja.lproj/InfoPlist.strings @@ -5,7 +5,7 @@ "New Tab" = "新規タブ"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox が QR コードのスキャンや写真、動画を撮影するためにカメラを使用します。"; +"NSCameraUsageDescription" = "Ecosia が QR コードのスキャンや写真、動画を撮影するためにカメラを使用します。"; /* Privacy - Face ID Usage Description */ "NSFaceIDUsageDescription" = "保存されたログイン情報と暗号化されたカード情報にアクセスするには Face ID が必要です。"; @@ -14,7 +14,7 @@ "NSLocationWhenInUseUsageDescription" = "訪れたウェブサイトがあなたの位置情報を要求しています。"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox が音声を録音してアップロードするためにマイクを使用します。"; +"NSMicrophoneUsageDescription" = "Ecosia が音声を録音してアップロードするためにマイクを使用します。"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "これにより写真を保存できます。"; diff --git a/firefox-ios/Client/kab.lproj/InfoPlist.strings b/firefox-ios/Client/kab.lproj/InfoPlist.strings index d2e6994220632..b05c3dfd5b788 100644 --- a/firefox-ios/Client/kab.lproj/InfoPlist.strings +++ b/firefox-ios/Client/kab.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Ayagi ad k-isireg tuṭṭfa d usali n tvidyutin."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox yesra Face ID i unekcum ɣer wawalen-ik·im uffiren d tarrayin n uxelleṣ yettwawgelhen."; +"NSFaceIDUsageDescription" = "Ecosia yesra Face ID i unekcum ɣer wawalen-ik·im uffiren d tarrayin n uxelleṣ yettwawgelhen."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Ismal web iɣer trezzuḍ zemren ad sutren adig-inek."; diff --git a/firefox-ios/Client/kk.lproj/InfoPlist.strings b/firefox-ios/Client/kk.lproj/InfoPlist.strings index 06ad595c2bf35..b1b0088a7c4c6 100644 --- a/firefox-ios/Client/kk.lproj/InfoPlist.strings +++ b/firefox-ios/Client/kk.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "Бұл арқылы фотоларды түсіріп, жүктеуге болады."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Сақталған парольдер мен төлем әдістеріне қол жеткізу үшін Firefox Face ID қажет етеді."; +"NSFaceIDUsageDescription" = "Сақталған парольдер мен төлем әдістеріне қол жеткізу үшін Ecosia Face ID қажет етеді."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Сіз ашатын веб-сайттар сізден орналасуыңызды сұрауы мүмкін."; diff --git a/firefox-ios/Client/ko.lproj/InfoPlist.strings b/firefox-ios/Client/ko.lproj/InfoPlist.strings index b3289ccbdaf5a..cb294cab329d2 100644 --- a/firefox-ios/Client/ko.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ko.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "새 탭"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox가 QR code 를 스캔하거나 사진, 영상을 찍기 위해 카메라를 사용합니다."; +"NSCameraUsageDescription" = "Ecosia가 QR code 를 스캔하거나 사진, 영상을 찍기 위해 카메라를 사용합니다."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox가 저장된 비밀번호와 결제 방법에 접근하려면 Face ID가 필요합니다."; +"NSFaceIDUsageDescription" = "Ecosia가 저장된 비밀번호와 결제 방법에 접근하려면 Face ID가 필요합니다."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "방문한 웹사이트에서 위치 정보를 요청할 수 있습니다."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox가 오디오 녹음 및 업로드를 하기 위해 마이크를 사용합니다."; +"NSMicrophoneUsageDescription" = "Ecosia가 오디오 녹음 및 업로드를 하기 위해 마이크를 사용합니다."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "사진을 저장할 수 있게 합니다."; diff --git a/firefox-ios/Client/lo.lproj/InfoPlist.strings b/firefox-ios/Client/lo.lproj/InfoPlist.strings index a7b2d54c2e769..d32d161053b2a 100644 --- a/firefox-ios/Client/lo.lproj/InfoPlist.strings +++ b/firefox-ios/Client/lo.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "ສິ່ງນີ້ຈະເຮັດໃຫ້ທ່ານຖ່າຍ ແລະ ອັບໂຫລດຮູບພາບໄດ້."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox ຕ້ອງການ Face ID ເພື່ອເຂົ້າເຖິງລະຫັດຜ່ານທີ່ບັນທຶກໄວ້ ແລະ ວິທີການຊໍາລະເງິນຂອງທ່ານ."; +"NSFaceIDUsageDescription" = "Ecosia ຕ້ອງການ Face ID ເພື່ອເຂົ້າເຖິງລະຫັດຜ່ານທີ່ບັນທຶກໄວ້ ແລະ ວິທີການຊໍາລະເງິນຂອງທ່ານ."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "ເວັບໄຊທ໌ທີ່ທ່ານເຂົ້າໄປອາດຈະຕ້ອງການຕຳແຫນ່ງທີຢູ່ຂອງທ່ານ"; diff --git a/firefox-ios/Client/lt.lproj/InfoPlist.strings b/firefox-ios/Client/lt.lproj/InfoPlist.strings index e9429455eae45..2b995b6ac473d 100644 --- a/firefox-ios/Client/lt.lproj/InfoPlist.strings +++ b/firefox-ios/Client/lt.lproj/InfoPlist.strings @@ -5,13 +5,13 @@ "New Tab" = "Nauja kortelė"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "„Firefox“ naudoja jūsų kamerą QR kodų nuskaitymui, nuotraukų darymui, ir vaizdo įrašams."; +"NSCameraUsageDescription" = "„Ecosia“ naudoja jūsų kamerą QR kodų nuskaitymui, nuotraukų darymui, ir vaizdo įrašams."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Svetainės gali teirautis jūsų buvimo vietos."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "„Firefox“ naudoja jūsų mikrofoną garso įrašymui ir įkėlimui."; +"NSMicrophoneUsageDescription" = "„Ecosia“ naudoja jūsų mikrofoną garso įrašymui ir įkėlimui."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Tai jums leis įrašyti nuotraukas."; diff --git a/firefox-ios/Client/metrics.yaml b/firefox-ios/Client/metrics.yaml index d783fbaa446bb..7f5591b3c12f5 100755 --- a/firefox-ios/Client/metrics.yaml +++ b/firefox-ios/Client/metrics.yaml @@ -25,7 +25,7 @@ app_cycle: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" background: type: event description: | @@ -37,7 +37,7 @@ app_cycle: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # App icon app_icon: new_private_tab_tapped: @@ -50,7 +50,7 @@ app_icon: - https://github.com/mozilla-mobile/firefox-ios/pull/15503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Accessibility accessibility: voice_over: @@ -69,7 +69,7 @@ accessibility: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" switch_control: type: event description: | @@ -86,7 +86,7 @@ accessibility: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" reduce_transparency: type: event description: | @@ -103,7 +103,7 @@ accessibility: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" reduce_motion: type: event description: | @@ -120,7 +120,7 @@ accessibility: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" invert_colors: type: event description: | @@ -137,7 +137,7 @@ accessibility: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" dynamic_text: type: event description: | @@ -158,9 +158,101 @@ accessibility: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # App menu app_menu: + main_menu_option_selected: + type: event + description: | + Record event when user has tapped on the main menu option. + extra_keys: + option: + type: string + description: | + The option type selected on the main menu. + is_homepage: + type: boolean + description: | + Indicate if is homepage when select an option. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/19329 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/22788 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-06-01" + save_menu_option_selected: + type: event + description: | + Record event when user has tapped on a save sub menu option. + extra_keys: + option: + type: string + description: | + The option type selected on the save sub menu. + is_homepage: + type: boolean + description: | + Indicate if is homepage when select an option. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23040 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23046 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-06-01" + tools_menu_option_selected: + type: event + description: | + Record event when user has tapped on a tools sub menu option. + extra_keys: + option: + type: string + description: | + The option type selected on the tools sub menu. + is_homepage: + type: boolean + description: | + Indicate if is homepage when select an option. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23040 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23046 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-06-01" + close_button: + type: event + description: | + Record event when user has tapped the close menu button. + extra_keys: + is_homepage: + type: boolean + description: | + Indicate if is homepage when select an option. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/19329 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/22788 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-06-01" + menu_dismissed: + type: event + description: | + Record event when user has dismissed the menu because of tapping outside or drag the menu. + extra_keys: + is_homepage: + type: boolean + description: | + Indicate if is homepage when select an option. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/19329 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/22788 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-06-01" homepage_menu: type: counter description: | @@ -173,7 +265,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" site_menu: type: counter description: | @@ -186,7 +278,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" home: type: counter description: | @@ -200,7 +292,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" block_images_enabled: type: counter description: | @@ -215,7 +307,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" block_images_disabled: type: counter description: | @@ -230,7 +322,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" night_mode_enabled: type: counter description: | @@ -245,7 +337,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" night_mode_disabled: type: counter description: | @@ -260,7 +352,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" whats_new: type: counter description: | @@ -274,7 +366,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" help: type: counter description: | @@ -286,7 +378,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" customize_homepage: type: counter description: | @@ -299,7 +391,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" settings: type: counter description: | @@ -313,7 +405,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" logins: type: counter description: | @@ -328,7 +420,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sign_into_sync: type: counter description: | @@ -344,7 +436,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/15181 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" passwords: type: event description: | @@ -355,7 +447,7 @@ app_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Bookmark metrics bookmarks: @@ -375,7 +467,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" add: type: labeled_counter description: | @@ -397,7 +489,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" delete: type: labeled_counter description: | @@ -419,7 +511,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" edit: type: labeled_counter description: | @@ -439,7 +531,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open: type: labeled_counter description: | @@ -461,7 +553,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/15833 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" has_mobile_bookmarks: type: boolean description: | @@ -473,7 +565,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/15833 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" mobile_bookmarks_count: type: quantity description: | @@ -485,7 +577,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/15833 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of mobile bookmarks folder_add: type: event @@ -497,7 +589,7 @@ bookmarks: - https://github.com/mozilla-mobile/firefox-ios/pull/15833 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Default browser card metrics default_browser_card: @@ -514,7 +606,7 @@ default_browser_card: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" go_to_settings_pressed: type: counter description: | @@ -529,7 +621,7 @@ default_browser_card: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" evergreen_impression: type: event description: | @@ -541,7 +633,7 @@ default_browser_card: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Default browser onboarding metrics default_browser_onboarding: @@ -558,7 +650,7 @@ default_browser_onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" go_to_settings_pressed: type: counter description: | @@ -573,9 +665,31 @@ default_browser_onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" app: + default_browser: + type: boolean + description: | + Is Firefox the default browser + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/XXX + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/XXX + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2026-01-01" + choice_screen_acquisition: + type: boolean + description: | + The user installed the app via the browser choice screen + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/XXX + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/XXX + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2026-01-01" opened_as_default_browser: type: counter description: | @@ -615,7 +729,7 @@ app: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Downloads downloads: @@ -630,7 +744,7 @@ downloads: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" downloads_panel_row_tapped: type: event description: | @@ -642,7 +756,7 @@ downloads: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" view_download_complete_toast: type: event description: | @@ -654,7 +768,32 @@ downloads: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" + +# Password Generator +password_generator: + shown: + type: counter + description: | + The password generator bottom sheet was shown and is visible + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/21248 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/issues/21248 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" + filled: + type: counter + description: | + The "use password button" of the password generator bottom sheet was clicked. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/21248 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/issues/21248 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" # Microsurvey microsurvey: @@ -675,7 +814,7 @@ microsurvey: - https://github.com/mozilla-mobile/firefox-ios/pull/20903 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" privacy_notice_tapped: type: event @@ -696,7 +835,7 @@ microsurvey: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" dismiss_button_tapped: type: event @@ -717,7 +856,7 @@ microsurvey: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" submit_button_tapped: type: event @@ -742,7 +881,7 @@ microsurvey: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" confirmation_shown: type: event @@ -761,7 +900,7 @@ microsurvey: - https://github.com/mozilla-mobile/firefox-ios/pull/20903 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Shopping Experience (Fakespot) shopping: @@ -775,7 +914,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16635 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" ads_exposure: type: event @@ -790,7 +929,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/17493 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - events @@ -807,7 +946,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/17663 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - events @@ -821,7 +960,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/17524 data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" notification_emails: - fx-ios-data-stewards@mozilla.com send_in_pings: @@ -837,7 +976,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/17861 data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" notification_emails: - fx-ios-data-stewards@mozilla.com send_in_pings: @@ -853,7 +992,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/15780 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" address_bar_icon_displayed: type: event @@ -865,7 +1004,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16282 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_closed: type: event @@ -883,7 +1022,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/15780 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_show_more_recent_reviews_clicked: type: event @@ -895,7 +1034,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16279 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_displayed: type: event @@ -913,7 +1052,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16272 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_settings_expand_clicked: type: event @@ -925,7 +1064,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16367 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_onboarding_displayed: type: event @@ -937,7 +1076,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16506 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_opt_in_accepted: type: event @@ -949,7 +1088,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16411 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_not_now_clicked: type: event @@ -961,7 +1100,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16411 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_show_terms_clicked: type: event @@ -973,7 +1112,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16411 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_show_privacy_policy_clicked: type: event @@ -985,7 +1124,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16411 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_learn_more_clicked: type: event @@ -997,7 +1136,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16411 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_show_quality_explainer_clicked: type: event @@ -1009,7 +1148,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" address_bar_feature_callout_displayed: type: event @@ -1021,7 +1160,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16593 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_powered_by_fakespot_link_clicked: type: event @@ -1034,7 +1173,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16655 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_analyze_reviews_none_available_clicked: type: event @@ -1047,7 +1186,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16721 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_reanalyze_clicked: type: event @@ -1060,7 +1199,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16721 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_no_review_reliability_available: type: event @@ -1072,7 +1211,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16750 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_stale_analysis_shown: type: event @@ -1084,7 +1223,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/16782 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_reactivated_button_clicked: type: event @@ -1096,7 +1235,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/17230 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" surface_ads_setting_toggled: type: event @@ -1113,7 +1252,7 @@ shopping: - https://github.com/mozilla-mobile/firefox-ios/pull/17681 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" shopping.settings: nimbus_disabled_shopping: @@ -1127,7 +1266,7 @@ shopping.settings: - https://github.com/mozilla-mobile/firefox-ios/pull/16780 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics @@ -1143,7 +1282,7 @@ shopping.settings: - https://github.com/mozilla-mobile/firefox-ios/pull/16780 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics @@ -1159,7 +1298,7 @@ shopping.settings: - https://github.com/mozilla-mobile/firefox-ios/pull/16780 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics @@ -1175,7 +1314,7 @@ shopping.settings: - https://github.com/mozilla-mobile/firefox-ios/pull/17930 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics @@ -1198,7 +1337,7 @@ key_commands: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Onboarding metrics onboarding: @@ -1230,7 +1369,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/10984 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" primary_button_tap: type: event description: | @@ -1264,7 +1403,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" secondary_button_tap: type: event description: | @@ -1298,7 +1437,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" multiple_choice_button_tap: type: event description: | @@ -1330,7 +1469,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/19678 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" close_tap: type: event description: | @@ -1360,7 +1499,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" wallpaper_selected: type: event description: | @@ -1382,7 +1521,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" wallpaper_selector_view: type: event description: | @@ -1395,7 +1534,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" wallpaper_selector_close: type: event description: | @@ -1409,7 +1548,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" wallpaper_selector_selected: type: event description: | @@ -1433,7 +1572,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" notification_permission_prompt: type: event description: | @@ -1450,7 +1589,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" engagement_notification_tapped: type: event description: | @@ -1462,7 +1601,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" engagement_notification_cancel: type: event description: | @@ -1474,7 +1613,7 @@ onboarding: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Top Site top_sites: @@ -1501,7 +1640,7 @@ top_sites: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" pressed_tile_origin: type: labeled_counter description: | @@ -1519,7 +1658,7 @@ top_sites: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" contextual_menu: type: event description: | @@ -1538,7 +1677,7 @@ top_sites: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" context_id: type: uuid description: | @@ -1558,7 +1697,7 @@ top_sites: - highly_sensitive notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" metadata: tags: - TopSites @@ -1580,7 +1719,7 @@ top_sites: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: integer metadata: tags: @@ -1603,7 +1742,7 @@ top_sites: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" metadata: tags: - TopSites @@ -1626,7 +1765,7 @@ top_sites: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" metadata: tags: - TopSites @@ -1654,7 +1793,7 @@ top_sites: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" metadata: tags: - TopSites @@ -1682,7 +1821,7 @@ top_sites: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" metadata: tags: - TopSites @@ -1696,7 +1835,7 @@ top_sites: - https://github.com/mozilla-mobile/firefox-ios/pull/15503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sponsored_shortcuts: type: boolean description: | @@ -1707,7 +1846,7 @@ top_sites: - https://github.com/mozilla-mobile/firefox-ios/pull/15768 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # HomePage - Normal firefox_home_page: open_from_menu_home_button: @@ -1725,7 +1864,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open_from_awesomebar: type: counter description: | @@ -1740,7 +1879,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_show_all: type: counter description: | @@ -1755,7 +1894,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_show_all_origin: type: labeled_counter description: | @@ -1774,7 +1913,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_bookmark_item: type: counter description: | @@ -1789,7 +1928,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_bookmark_origin: type: labeled_counter description: | @@ -1808,7 +1947,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_bookmark_view: type: event description: | @@ -1829,7 +1968,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" reading_list_view: type: event description: | @@ -1851,7 +1990,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_reading_item: type: counter description: | @@ -1866,7 +2005,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved_read_origin: type: labeled_counter description: | @@ -1885,7 +2024,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_show_all: type: counter description: | @@ -1900,7 +2039,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_show_all_origin: type: labeled_counter description: | @@ -1919,7 +2058,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_tile_view: type: counter description: | @@ -1933,7 +2072,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_tab_opened: type: counter description: | @@ -1948,7 +2087,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_tab_opened_origin: type: labeled_counter description: | @@ -1967,7 +2106,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_group_opened: type: counter description: | @@ -1982,7 +2121,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in_group_open_origin: type: labeled_counter description: | @@ -2001,7 +2140,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" synced_tab_tile_view: type: counter description: | @@ -2015,7 +2154,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" synced_tab_show_all: type: counter description: | @@ -2029,7 +2168,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" synced_tab_show_all_origin: type: labeled_counter description: | @@ -2047,7 +2186,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" synced_tab_opened: type: counter description: | @@ -2061,7 +2200,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" synced_tab_opened_origin: type: labeled_counter description: | @@ -2079,7 +2218,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" customize_homepage_button: type: counter @@ -2095,7 +2234,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" firefox_homepage_view: type: counter description: | @@ -2109,7 +2248,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" firefox_homepage_origin: type: labeled_counter description: | @@ -2127,7 +2266,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" history_highlights_show_all: type: counter description: | @@ -2142,7 +2281,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" history_highlights_item_opened: type: event description: | @@ -2158,7 +2297,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" history_impressions: type: event description: | @@ -2170,7 +2309,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/18068 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" history_highlights_group_open: type: event description: | @@ -2184,7 +2323,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" history_highlights_context: type: event description: | @@ -2203,7 +2342,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" pocket_stories_visible: type: boolean description: | @@ -2216,7 +2355,7 @@ firefox_home_page: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # HomePage - General homepage: @@ -2235,7 +2374,7 @@ homepage: - https://github.com/mozilla-mobile/firefox-ios/pull/18518 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Library library: @@ -2260,7 +2399,7 @@ library: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # History Panel history: @@ -2274,7 +2413,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/15696 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" group_list: type: counter description: | @@ -2290,7 +2429,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" selected_item: type: labeled_counter description: | @@ -2308,7 +2447,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" opened_item: type: event description: | @@ -2319,7 +2458,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/17462 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" search_tap: type: event description: | @@ -2332,7 +2471,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" delete_tap: type: event description: | @@ -2345,7 +2484,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" removed: type: event description: | @@ -2356,7 +2495,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/15696 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" removed_today: type: event description: | @@ -2367,7 +2506,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/15696 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" removed_today_and_yesterday: type: event description: | @@ -2379,7 +2518,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/15696 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" removed_all: type: event description: | @@ -2390,7 +2529,7 @@ history: - https://github.com/mozilla-mobile/firefox-ios/pull/15696 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Legacy IDs for continuity and for deletion-request legacy.ids: @@ -2445,7 +2584,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_to_device: type: counter description: | @@ -2460,7 +2599,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" copy_address: type: counter description: | @@ -2475,7 +2614,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" report_site_issue: type: counter description: | @@ -2490,7 +2629,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" find_in_page: type: counter description: | @@ -2505,7 +2644,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" request_desktop_site: type: counter description: | @@ -2520,7 +2659,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" request_mobile_site: type: counter description: | @@ -2535,7 +2674,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" pin_to_top_sites: type: counter description: | @@ -2550,7 +2689,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" remove_pinned_site: type: counter description: | @@ -2565,7 +2704,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" view_downloads_panel: type: counter description: | @@ -2579,7 +2718,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" view_history_panel: type: counter description: | @@ -2593,7 +2732,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" create_new_tab: type: counter description: | @@ -2607,7 +2746,7 @@ page_action_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Pocket pocket: @@ -2626,7 +2765,7 @@ pocket: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" section_impressions: type: counter description: | @@ -2641,7 +2780,7 @@ pocket: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open_story_origin: type: labeled_counter description: | @@ -2659,7 +2798,7 @@ pocket: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open_in_private_tab: type: event description: | @@ -2670,7 +2809,7 @@ pocket: - https://github.com/mozilla-mobile/firefox-ios/pull/15503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Preference metrics preferences: changed: @@ -2697,7 +2836,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" new_tab_experience: type: string description: | @@ -2715,7 +2854,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" home_page_setting: type: string description: | @@ -2730,7 +2869,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" save_logins: type: boolean description: | @@ -2744,7 +2883,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" show_clipboard_bar: type: boolean description: | @@ -2758,7 +2897,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" tips_and_features_notifs: type: boolean description: | @@ -2769,7 +2908,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15114 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sync_notifs: type: boolean description: | @@ -2780,7 +2919,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15114 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" fxa_logged_in: type: boolean description: | @@ -2791,7 +2930,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15181 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" close_private_tabs: type: boolean description: | @@ -2805,7 +2944,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" jump_back_in: type: boolean description: | @@ -2817,7 +2956,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15583 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_visited: type: boolean description: | @@ -2829,7 +2968,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15583 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" recently_saved: type: boolean description: | @@ -2841,7 +2980,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15583 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" pocket: type: boolean description: | @@ -2852,7 +2991,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15583 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" opening_screen: type: string description: | @@ -2865,7 +3004,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/15583 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" private_browsing_button_tapped: type: event description: | @@ -2882,7 +3021,7 @@ preferences: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Private Browsing private_browsing: @@ -2901,7 +3040,7 @@ private_browsing: - https://github.com/mozilla-mobile/firefox-ios/pull/18080 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Password Manager metrics logins: @@ -2915,7 +3054,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_failed: type: event description: | @@ -2926,7 +3065,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" management_add_tapped: type: event description: | @@ -2937,7 +3076,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" management_logins_tapped: type: event description: | @@ -2948,7 +3087,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" saved: type: counter description: | @@ -2959,7 +3098,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" saved_all: type: quantity description: | @@ -2970,7 +3109,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of logins.saved_all deleted: type: counter @@ -2982,7 +3121,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" modified: type: counter description: | @@ -2993,7 +3132,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sync_enabled: type: event description: | @@ -3011,7 +3150,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_shown: type: event description: | @@ -3024,7 +3163,7 @@ logins: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_expanded: type: event description: | @@ -3035,7 +3174,7 @@ logins: - https://github.com/mozilla-mobile/firefox-ios/pull/19791 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_dismissed: type: event description: | @@ -3048,7 +3187,7 @@ logins: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # QR Code metrics qr_code: scanned: @@ -3064,7 +3203,7 @@ qr_code: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Reading List metrics reading_list: @@ -3089,7 +3228,7 @@ reading_list: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open: type: counter description: | @@ -3104,7 +3243,7 @@ reading_list: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" delete: type: labeled_counter description: | @@ -3125,7 +3264,7 @@ reading_list: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Reader Mode metrics reader_mode: @@ -3142,7 +3281,7 @@ reader_mode: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" close: type: counter description: | @@ -3156,7 +3295,7 @@ reader_mode: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Default Browser Settings Menu Option settings_menu: @@ -3190,7 +3329,7 @@ settings_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/13827 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" passwords: type: event description: | @@ -3201,7 +3340,7 @@ settings_menu: - https://github.com/mozilla-mobile/firefox-ios/pull/16685 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Search and search related metrics search: counts: @@ -3229,7 +3368,7 @@ search: - https://bugzilla.mozilla.org/show_bug.cgi?id=1923843 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics - baseline @@ -3254,7 +3393,7 @@ search: - https://bugzilla.mozilla.org/show_bug.cgi?id=1923843 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics - baseline @@ -3275,7 +3414,7 @@ search: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" in_content: type: labeled_counter description: | @@ -3291,7 +3430,7 @@ search: - https://bugzilla.mozilla.org/show_bug.cgi?id=1923843 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" send_in_pings: - metrics - baseline @@ -3312,7 +3451,7 @@ search: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Sync sync: @@ -3330,7 +3469,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sign_in_sync_pressed: type: counter description: | @@ -3346,7 +3485,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" create_account_pressed: type: counter description: | @@ -3361,7 +3500,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" registration_view: type: event description: | @@ -3374,7 +3513,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" registration_completed_view: type: event description: | @@ -3387,7 +3526,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" login_view: type: event description: | @@ -3400,7 +3539,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" login_completed_view: type: event description: | @@ -3413,7 +3552,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" login_token_view: type: event description: | @@ -3426,7 +3565,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" registration_code_view: type: event description: | @@ -3439,7 +3578,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" use_email: type: event description: | @@ -3451,7 +3590,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/15181 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" paired: type: event description: | @@ -3462,7 +3601,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/15181 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" disconnect: type: event description: | @@ -3473,7 +3612,7 @@ sync: - https://github.com/mozilla-mobile/firefox-ios/pull/15181 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Tab metrics @@ -3491,7 +3630,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of normal tabs private_tabs_quantity: type: quantity @@ -3506,7 +3645,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of private tabs inactive_tabs_count: type: quantity @@ -3520,7 +3659,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/issues/15511 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of inactive tabs cumulative_count: type: counter @@ -3543,7 +3682,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open: type: labeled_counter description: | @@ -3562,7 +3701,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" close: type: labeled_counter description: | @@ -3581,7 +3720,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" close_all: type: labeled_counter description: | @@ -3600,7 +3739,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" new_tab_pressed: type: counter description: | @@ -3615,7 +3754,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" click_tab: type: event description: | @@ -3629,7 +3768,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open_tab_tray: type: event description: | @@ -3643,7 +3782,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" close_tab_tray: type: event description: | @@ -3657,7 +3796,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" pull_to_refresh: type: counter description: | @@ -3670,9 +3809,21 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/9673 - https://github.com/mozilla-mobile/firefox-ios/pull/12334 - https://github.com/mozilla-mobile/firefox-ios/pull/14102 + - https://github.com/mozilla-mobile/firefox-ios/pull/23252 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" + pull_to_refresh_easter_egg: + type: event + description: | + Record event when user has discovered the Easter egg. + bugs: + - https://github.com/mozilla-mobile/firefox-ios/issues/23225 + data_reviews: + - https://github.com/mozilla-mobile/firefox-ios/pull/23252 + notification_emails: + - fx-ios-data-stewards@mozilla.com + expires: "2025-07-01" normal_and_private_uri_count: type: counter description: | @@ -3724,7 +3875,7 @@ tabs: description: Total number of tabs in all groups type: quantity - expires: "2025-01-01" + expires: "2025-07-01" grouped_tab_closed: type: counter description: | @@ -3738,7 +3889,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" grouped_tab_search: type: counter description: | @@ -3753,7 +3904,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" navigate_tab_history_back: type: counter description: | @@ -3767,7 +3918,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" navigate_tab_back_swipe: type: counter description: | @@ -3781,7 +3932,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" navigate_tab_history_forward: type: counter description: | @@ -3795,7 +3946,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" reload_from_url_bar: type: counter description: | @@ -3809,7 +3960,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" press_tab_toolbar: type: event description: | @@ -3821,7 +3972,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" press_top_tab: type: event description: | @@ -3833,7 +3984,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" restore_tabs_alert: type: event description: | @@ -3851,7 +4002,7 @@ tabs: - https://github.com/mozilla-mobile/firefox-ios/pull/16515 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" tab_switch: type: timing_distribution time_unit: millisecond @@ -3881,7 +4032,7 @@ tracking_protection: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" strength: type: string description: | @@ -3898,7 +4049,7 @@ tracking_protection: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" etp_setting_changed: type: event description: | @@ -3921,7 +4072,7 @@ tracking_protection: - https://github.com/mozilla-mobile/firefox-ios/pull/15503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # iOS 14 widget metrics # m: medium, s: small, l: large widget: @@ -3938,7 +4089,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" l_tabs_open_url: type: counter description: | @@ -3952,7 +4103,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" s_quick_action_search: type: counter description: | @@ -3967,7 +4118,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" m_quick_action_search: type: counter description: | @@ -3982,7 +4133,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" m_quick_action_private_search: type: counter description: | @@ -3997,7 +4148,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" m_quick_action_copied_link: type: counter description: | @@ -4012,7 +4163,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" m_quick_action_close_private: type: counter description: | @@ -4027,7 +4178,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" m_top_sites_widget: type: counter description: | @@ -4041,7 +4192,7 @@ widget: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Wallpaper metrics wallpaper_analytics: @@ -4069,7 +4220,7 @@ wallpaper_analytics: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" themed_wallpaper: type: labeled_counter description: | @@ -4083,7 +4234,7 @@ wallpaper_analytics: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # CFR Metrics cfr_analytics: @@ -4107,7 +4258,7 @@ cfr_analytics: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" dismiss_cfr_from_outside_tap: type: event description: | @@ -4127,7 +4278,7 @@ cfr_analytics: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" press_cfr_action_button: type: event description: | @@ -4147,7 +4298,7 @@ cfr_analytics: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Tabs Tray metrics tabs_tray: @@ -4167,7 +4318,7 @@ tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/15503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" new_private_tab_tapped: type: event description: | @@ -4178,7 +4329,7 @@ tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/15503 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Inactive Tabs metrics inactive_tabs_tray: toggle_inactive_tab_tray: @@ -4201,7 +4352,7 @@ inactive_tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" open_inactive_tab: type: counter description: | @@ -4214,7 +4365,7 @@ inactive_tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" inactive_tabs_close_all_btn: type: counter description: | @@ -4227,7 +4378,7 @@ inactive_tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" inactive_tab_swipe_close: type: counter description: | @@ -4240,7 +4391,7 @@ inactive_tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" inactive_tab_shown: type: counter description: | @@ -4253,7 +4404,7 @@ inactive_tabs_tray: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" browser_search: with_ads: @@ -4277,7 +4428,7 @@ browser_search: - https://bugzilla.mozilla.org/show_bug.cgi?id=1923843 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" ad_clicks: type: labeled_counter description: | @@ -4300,7 +4451,7 @@ browser_search: - https://bugzilla.mozilla.org/show_bug.cgi?id=1923843 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" installed_mozilla_products: klar: @@ -4319,7 +4470,7 @@ installed_mozilla_products: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" focus: type: boolean lifetime: application @@ -4336,7 +4487,7 @@ installed_mozilla_products: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # URLbar related telemetry urlbar: @@ -4398,7 +4549,7 @@ urlbar: - https://github.com/mozilla-mobile/firefox-ios/pull/18507 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" engagement: type: event @@ -4470,7 +4621,7 @@ urlbar: - https://github.com/mozilla-mobile/firefox-ios/pull/18654 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" abandonment: type: event description: | @@ -4523,7 +4674,7 @@ urlbar: - https://github.com/mozilla-mobile/firefox-ios/pull/18507 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Awesomebar related telemetry @@ -4546,7 +4697,7 @@ awesomebar: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" drag_location_bar: type: event description: | @@ -4558,7 +4709,7 @@ awesomebar: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" search_result_impression: type: event description: | @@ -4577,7 +4728,7 @@ awesomebar: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" search_result_tap: type: event description: | @@ -4600,7 +4751,7 @@ awesomebar: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" query_time: type: timing_distribution time_unit: millisecond @@ -4633,8 +4784,8 @@ awesomebar: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" - + expires: "2025-07-01" + # Toolbar related metrics toolbar: qr_scan_button_tapped: @@ -4944,7 +5095,7 @@ share_sheet: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" copy_link_tapped: type: event description: | @@ -4957,7 +5108,7 @@ share_sheet: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" pocket_action_tapped: type: event description: | @@ -4970,7 +5121,7 @@ share_sheet: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" save_to_pocket_tapped: type: event description: | @@ -4983,7 +5134,7 @@ share_sheet: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Device specific metrics @@ -5004,7 +5155,7 @@ device: - technical notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" messaging: shown: @@ -5027,7 +5178,7 @@ messaging: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" dismissed: type: event description: | @@ -5048,7 +5199,7 @@ messaging: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" clicked: type: event description: | @@ -5072,7 +5223,7 @@ messaging: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" expired: type: event description: | @@ -5093,7 +5244,7 @@ messaging: - fx-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2025-01-01" + expires: "2025-07-01" malformed: type: event description: | @@ -5114,7 +5265,7 @@ messaging: - fx-ios-data-stewards@mozilla.com data_sensitivity: - technical - expires: "2025-01-01" + expires: "2025-07-01" adjust: deeplink_received: type: event @@ -5138,7 +5289,7 @@ adjust: - fx-ios-data-stewards@mozilla.com data_sensitivity: - technical - expires: "2025-01-01" + expires: "2025-07-01" campaign: type: string lifetime: ping @@ -5158,7 +5309,7 @@ adjust: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" ad_group: type: string lifetime: ping @@ -5178,7 +5329,7 @@ adjust: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" creative: type: string lifetime: ping @@ -5198,7 +5349,7 @@ adjust: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" network: type: string lifetime: ping @@ -5218,7 +5369,7 @@ adjust: - interaction notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" migration: image_sd_cache_cleanup: type: counter @@ -5233,7 +5384,7 @@ migration: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" places_history_migration: duration: @@ -5251,7 +5402,7 @@ places_history_migration: notification_emails: - mhammond@mozilla.com - sync-team@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" num_to_migrate: type: quantity unit: visits @@ -5267,7 +5418,7 @@ places_history_migration: notification_emails: - mhammond@mozilla.com - sync-team@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" num_migrated: type: quantity unit: visits @@ -5283,7 +5434,7 @@ places_history_migration: notification_emails: - mhammond@mozilla.com - sync-team@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" migration_ended_rate: type: rate description: | @@ -5298,7 +5449,7 @@ places_history_migration: notification_emails: - mhammond@mozilla.com - sync-team@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" migration_error_rate: type: rate description: | @@ -5313,7 +5464,7 @@ places_history_migration: notification_emails: - mhammond@mozilla.com - sync-team@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" # Address autofill addresses: form_detected: @@ -5326,7 +5477,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_shown: type: event description: | @@ -5337,7 +5488,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_expanded: type: event description: | @@ -5348,7 +5499,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_dismissed: type: event description: | @@ -5359,7 +5510,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofilled: type: event description: | @@ -5370,7 +5521,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" modified: type: event description: | @@ -5381,7 +5532,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" settings_autofill: type: event description: | @@ -5392,7 +5543,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" saved_all: type: quantity description: | @@ -5403,7 +5554,7 @@ addresses: - https://github.com/mozilla-mobile/firefox-ios/pull/19228 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of addresses.saved_all # Credit card autofill credit_card: @@ -5419,7 +5570,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/14102 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" form_detected: type: event description: | @@ -5431,7 +5582,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofilled: type: event description: | @@ -5444,7 +5595,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_failed: type: event description: | @@ -5457,7 +5608,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" save_prompt_create: type: event description: | @@ -5469,7 +5620,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_toggle: type: event description: | @@ -5488,7 +5639,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sync_toggle: type: event description: | @@ -5507,7 +5658,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_enabled: type: boolean description: | @@ -5520,7 +5671,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" sync_enabled: type: boolean description: | @@ -5533,7 +5684,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/15251 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_shown: type: event @@ -5545,7 +5696,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_expanded: type: event description: | @@ -5556,7 +5707,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" autofill_prompt_dismissed: type: event description: | @@ -5567,7 +5718,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" save_prompt_shown: type: event description: | @@ -5578,7 +5729,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" save_prompt_update: type: event description: | @@ -5589,7 +5740,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" management_add_tapped: type: event description: | @@ -5600,7 +5751,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" management_card_tapped: type: event description: | @@ -5611,7 +5762,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" saved: type: counter description: | @@ -5622,7 +5773,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" saved_all: type: quantity description: | @@ -5633,7 +5784,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of credit_card.saved_all deleted: type: counter @@ -5645,7 +5796,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" modified: type: counter description: | @@ -5656,7 +5807,7 @@ credit_card: - https://github.com/mozilla-mobile/firefox-ios/pull/17331 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" app_errors: large_file_write: @@ -5947,7 +6098,7 @@ windows: - https://github.com/mozilla-mobile/firefox-ios/pull/20133 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" unit: quantity of iPad windows # Server Knobs validation @@ -5966,4 +6117,4 @@ server_knobs: notification_emails: - fx-ios-data-stewards@mozilla.com - brosa@mozilla.com - expires: "2025-01-01" + expires: "2025-07-01" diff --git a/firefox-ios/Client/nb.lproj/InfoPlist.strings b/firefox-ios/Client/nb.lproj/InfoPlist.strings index 3c848f05eb676..9ba39879b6afd 100644 --- a/firefox-ios/Client/nb.lproj/InfoPlist.strings +++ b/firefox-ios/Client/nb.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Ny fane"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox bruker kameraet ditt til å skanne QR-koder og ta bilder og video."; +"NSCameraUsageDescription" = "Ecosia bruker kameraet ditt til å skanne QR-koder og ta bilder og video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox krever Face ID for å få tilgang til dine lagrede passord og betalingsmåter."; +"NSFaceIDUsageDescription" = "Ecosia krever Face ID for å få tilgang til dine lagrede passord og betalingsmåter."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Nettsider du besøker kan be om din plassering."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox bruker mikrofonen din til å ta opp og laste opp lyd."; +"NSMicrophoneUsageDescription" = "Ecosia bruker mikrofonen din til å ta opp og laste opp lyd."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Dette gjør det mulig å lagre bilder."; diff --git a/firefox-ios/Client/ne-NP.lproj/InfoPlist.strings b/firefox-ios/Client/ne-NP.lproj/InfoPlist.strings index 9b1f93f88fdc7..e9103b29d2a5d 100644 --- a/firefox-ios/Client/ne-NP.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ne-NP.lproj/InfoPlist.strings @@ -5,13 +5,13 @@ "New Tab" = "नयाँ ट्याब"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox ले QR कोडहरू स्क्यान गर्न र फोटो र भिडियो खिच्न तपाईंको क्यामरा प्रयोग गर्दछ।"; +"NSCameraUsageDescription" = "Ecosia ले QR कोडहरू स्क्यान गर्न र फोटो र भिडियो खिच्न तपाईंको क्यामरा प्रयोग गर्दछ।"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "तपाईंले भ्रमण गर्ने वेबसाइटहरूले तपाईंको स्थान अनुरोध गर्न सक्छन् ।"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox ले अडियो रेकर्ड गर्न र अडियो अपलोड गर्न तपाईंको माइक्रोफोन प्रयोग गर्छ।"; +"NSMicrophoneUsageDescription" = "Ecosia ले अडियो रेकर्ड गर्न र अडियो अपलोड गर्न तपाईंको माइक्रोफोन प्रयोग गर्छ।"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "यसले तपाईंलाई फोटोहरू सेभ गर्न दिन्छ ।"; diff --git a/firefox-ios/Client/nl.lproj/InfoPlist.strings b/firefox-ios/Client/nl.lproj/InfoPlist.strings index 06722df17f9ea..66d27fbad9718 100644 --- a/firefox-ios/Client/nl.lproj/InfoPlist.strings +++ b/firefox-ios/Client/nl.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nieuw tabblad"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox gebruikt uw camera om QR-codes te scannen en foto’s en video te maken."; +"NSCameraUsageDescription" = "Ecosia gebruikt uw camera om QR-codes te scannen en foto’s en video te maken."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox vereist Face ID om uw opgeslagen aanmeldingen en betalingsmethoden te benaderen."; +"NSFaceIDUsageDescription" = "Ecosia vereist Face ID om uw opgeslagen aanmeldingen en betalingsmethoden te benaderen."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Websites die u bezoekt kunnen om uw locatie vragen."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox gebruikt uw microfoon om audio op te nemen en te uploaden."; +"NSMicrophoneUsageDescription" = "Ecosia gebruikt uw microfoon om audio op te nemen en te uploaden."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Hiermee kunt u foto’s opslaan."; diff --git a/firefox-ios/Client/nn.lproj/InfoPlist.strings b/firefox-ios/Client/nn.lproj/InfoPlist.strings index 5d397357b2ba5..021087e4ab119 100644 --- a/firefox-ios/Client/nn.lproj/InfoPlist.strings +++ b/firefox-ios/Client/nn.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Ny fane"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox brukar kameraet ditt til å skanne QR-kodar og ta bilde og video."; +"NSCameraUsageDescription" = "Ecosia brukar kameraet ditt til å skanne QR-kodar og ta bilde og video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox krev Face ID for å få tilgang til dei lagra passorda dine og betalingsmetodar."; +"NSFaceIDUsageDescription" = "Ecosia krev Face ID for å få tilgang til dei lagra passorda dine og betalingsmetodar."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Nettsider du besøkjer kan be om plasseringa di."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox brukar mikrofonen din til å ta opp og laste opp lyd."; +"NSMicrophoneUsageDescription" = "Ecosia brukar mikrofonen din til å ta opp og laste opp lyd."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Dette gjer det mogleg å lagre bilde."; diff --git a/firefox-ios/Client/pa-IN.lproj/InfoPlist.strings b/firefox-ios/Client/pa-IN.lproj/InfoPlist.strings index 279508eb3c1d7..0b5ebe4e3b656 100644 --- a/firefox-ios/Client/pa-IN.lproj/InfoPlist.strings +++ b/firefox-ios/Client/pa-IN.lproj/InfoPlist.strings @@ -1,20 +1,20 @@ /* (No Comment) */ -"New Private Tab" = "ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ"; +"New Private Tab" = "ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਟੈਬ"; /* (No Comment) */ "New Tab" = "ਨਵੀਂ ਟੈਬ"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox ਤੁਹਾਡੇ ਕੈਮਰੇ ਨੂੰ QR ਕੋਡ ਸਕੈਨ ਕਰਨ ਅਤੇ ਫੋਟੋ ਖਿੱਚ ਤੇ ਵੀਡੀਓ ਬਣਾਉਣ ਲਈ ਵਰਤਦਾ ਹੈ।"; +"NSCameraUsageDescription" = "Ecosia ਤੁਹਾਡੇ ਕੈਮਰੇ ਨੂੰ QR ਕੋਡ ਸਕੈਨ ਕਰਨ ਅਤੇ ਫੋਟੋ ਖਿੱਚ ਤੇ ਵੀਡੀਓ ਬਣਾਉਣ ਲਈ ਵਰਤਦਾ ਹੈ।"; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox ਨੂੰ ਤੁਹਾਡੇ ਸੰਭਾਲੇ ਹੋਏ ਪਾਸਵਰਡਾਂ ਅਤੇ ਭੁਗਤਾਨ ਢੰਗਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ Face ID ਚਾਹੀਦਾ ਹੈ।"; +"NSFaceIDUsageDescription" = "Ecosia ਨੂੰ ਤੁਹਾਡੇ ਸੰਭਾਲੇ ਹੋਏ ਪਾਸਵਰਡਾਂ ਅਤੇ ਭੁਗਤਾਨ ਢੰਗਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ Face ID ਚਾਹੀਦਾ ਹੈ।"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "ਤੁਸੀਂ ਜਿਹਨਾਂ ਵੈੱਬਸਾਈਟਾਂ ਉੱਤੇ ਜਾਉਗੇ, ਉਹ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਦੀ ਬੇਨਤੀ ਕਰ ਸਕਦੀਆਂ ਹਨ।"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox ਤੁਹਾਡੇ ਮਾਈਕਰੋਫ਼ੋਨ ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਤੇ ਆਡੀਓ ਅੱਪਲੋਡ ਕਰਨ ਲਈ ਵਰਤਦਾ ਹੈ।"; +"NSMicrophoneUsageDescription" = "Ecosia ਤੁਹਾਡੇ ਮਾਈਕਰੋਫ਼ੋਨ ਨੂੰ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਤੇ ਆਡੀਓ ਅੱਪਲੋਡ ਕਰਨ ਲਈ ਵਰਤਦਾ ਹੈ।"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "ਇਹ ਤੁਹਾਨੂੰ ਫ਼ੋਟੋ ਸੰਭਾਲਣ ਦਿੰਦਾ ਹੈ।"; diff --git a/firefox-ios/Client/pl.lproj/InfoPlist.strings b/firefox-ios/Client/pl.lproj/InfoPlist.strings index f6c62a0c68723..9b6b11680c410 100644 --- a/firefox-ios/Client/pl.lproj/InfoPlist.strings +++ b/firefox-ios/Client/pl.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nowa karta"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox używa aparatu do skanowania kodów QR, robienia zdjęć i nagrywania filmów."; +"NSCameraUsageDescription" = "Ecosia używa aparatu do skanowania kodów QR, robienia zdjęć i nagrywania filmów."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox wymaga Face ID, by uzyskać dostęp do zachowanych haseł i metod płatności."; +"NSFaceIDUsageDescription" = "Ecosia wymaga Face ID, by uzyskać dostęp do zachowanych haseł i metod płatności."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Pozwoli to odwiedzanym stronom żądać informacji o położeniu."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox używa mikrofonu do nagrywania i przesyłania dźwięku."; +"NSMicrophoneUsageDescription" = "Ecosia używa mikrofonu do nagrywania i przesyłania dźwięku."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Pozwoli to zachowywać zdjęcia."; diff --git a/firefox-ios/Client/pt-BR.lproj/InfoPlist.strings b/firefox-ios/Client/pt-BR.lproj/InfoPlist.strings index 96b0749197719..e1ef99505d882 100644 --- a/firefox-ios/Client/pt-BR.lproj/InfoPlist.strings +++ b/firefox-ios/Client/pt-BR.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nova aba"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "O Firefox usa sua câmera para capturar códigos QR, tirar fotos e gravar vídeos."; +"NSCameraUsageDescription" = "O Ecosia usa sua câmera para capturar códigos QR, tirar fotos e gravar vídeos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "O Firefox requer Face ID para acessar suas senhas e métodos de pagamento salvos."; +"NSFaceIDUsageDescription" = "O Ecosia requer Face ID para acessar suas senhas e métodos de pagamento salvos."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Sites que você visita podem solicitar sua localização."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "O Firefox usa seu microfone para gravar e enviar áudio."; +"NSMicrophoneUsageDescription" = "O Ecosia usa seu microfone para gravar e enviar áudio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Permite salvar fotos."; diff --git a/firefox-ios/Client/pt-PT.lproj/InfoPlist.strings b/firefox-ios/Client/pt-PT.lproj/InfoPlist.strings index 62af79af1aab9..3d5d27eec1350 100644 --- a/firefox-ios/Client/pt-PT.lproj/InfoPlist.strings +++ b/firefox-ios/Client/pt-PT.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Novo separador"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "O Firefox utiliza a sua câmara para digitalizar códigos QR e tirar fotos e vídeos."; +"NSCameraUsageDescription" = "O Ecosia utiliza a sua câmara para digitalizar códigos QR e tirar fotos e vídeos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "O Firefox requer o Face ID para aceder às suas palavras-passe e métodos de pagamento guardados."; +"NSFaceIDUsageDescription" = "O Ecosia requer o Face ID para aceder às suas palavras-passe e métodos de pagamento guardados."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Os websites que visita poderão solicitar a sua localização."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "O Firefox permite-lhe usar o seu microfone para gravar e carregar áudio."; +"NSMicrophoneUsageDescription" = "O Ecosia permite-lhe usar o seu microfone para gravar e carregar áudio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Isto deixa-lhe guardar fotos."; diff --git a/firefox-ios/Client/rm.lproj/InfoPlist.strings b/firefox-ios/Client/rm.lproj/InfoPlist.strings index bfddaf3870d7d..1b5f76781c554 100644 --- a/firefox-ios/Client/rm.lproj/InfoPlist.strings +++ b/firefox-ios/Client/rm.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nov tab"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox utilisescha tia camera per scannar codes QR e far fotos e videos."; +"NSCameraUsageDescription" = "Ecosia utilisescha tia camera per scannar codes QR e far fotos e videos."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox pretenda Face ID per acceder a tes pleds-clav memorisads e tias metodas da pajament."; +"NSFaceIDUsageDescription" = "Ecosia pretenda Face ID per acceder a tes pleds-clav memorisads e tias metodas da pajament."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Paginas d'internet che ti visitas dumondan eventualmain tia posiziun geografica."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox utilisescha tes microfon per registrar e transferir audio."; +"NSMicrophoneUsageDescription" = "Ecosia utilisescha tes microfon per registrar e transferir audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Uschia pos ti memorisar fotografias."; diff --git a/firefox-ios/Client/ru.lproj/InfoPlist.strings b/firefox-ios/Client/ru.lproj/InfoPlist.strings index 32b8b79d59330..45acafedb443f 100644 --- a/firefox-ios/Client/ru.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ru.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Новая вкладка"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox использует вашу камеру для сканирования QR-кодов и съемки фотографий и видео."; +"NSCameraUsageDescription" = "Ecosia использует вашу камеру для сканирования QR-кодов и съемки фотографий и видео."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox требует Face ID для доступа к вашим сохранённым логинам и зашифрованным картам."; +"NSFaceIDUsageDescription" = "Ecosia требует Face ID для доступа к вашим сохранённым логинам и зашифрованным картам."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Посещаемые вами веб-сайты могут запрашивать ваше местоположение."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox использует ваш микрофон для записи и выгрузки аудио."; +"NSMicrophoneUsageDescription" = "Ecosia использует ваш микрофон для записи и выгрузки аудио."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Это позволит вам сохранять фотографии."; diff --git a/firefox-ios/Client/sat-Olck.lproj/InfoPlist.strings b/firefox-ios/Client/sat-Olck.lproj/InfoPlist.strings index c2c18ef4d1166..b316f77aecf55 100644 --- a/firefox-ios/Client/sat-Olck.lproj/InfoPlist.strings +++ b/firefox-ios/Client/sat-Olck.lproj/InfoPlist.strings @@ -8,7 +8,7 @@ "NSCameraUsageDescription" = "ᱱᱚᱶᱟ ᱛᱮ ᱟᱢ ᱪᱤᱛᱟᱹᱨ ᱨᱟᱠᱟᱵ ᱟᱨ ᱞᱟᱫᱮ ᱫᱟᱰᱮᱭᱟᱜᱼᱟᱢ ᱾"; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "ᱟᱢᱟᱜ ᱥᱟᱧᱪᱟᱣ ᱨᱩᱠᱷᱭᱟᱹ ᱞᱳᱜᱤᱱ ᱨᱮ ᱵᱚᱞᱚᱱ ᱞᱟᱹᱜᱤᱱ Firefox ᱟᱢᱟᱜ ᱢᱮᱫᱦᱟᱸ ID ᱞᱟᱹᱠᱛᱤᱜ ᱠᱟᱱᱟᱭ ᱾"; +"NSFaceIDUsageDescription" = "ᱟᱢᱟᱜ ᱥᱟᱧᱪᱟᱣ ᱨᱩᱠᱷᱭᱟᱹ ᱞᱳᱜᱤᱱ ᱨᱮ ᱵᱚᱞᱚᱱ ᱞᱟᱹᱜᱤᱱ Ecosia ᱟᱢᱟᱜ ᱢᱮᱫᱦᱟᱸ ID ᱞᱟᱹᱠᱛᱤᱜ ᱠᱟᱱᱟᱭ ᱾"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "ᱦᱤᱨᱤᱟᱠᱟᱱ ᱣᱮᱵᱥᱟᱭᱤᱴ ᱟᱢᱟᱜ ᱡᱟᱭᱜᱟ ᱠᱚ ᱠᱷᱚᱡ ᱫᱟᱲᱮᱟᱜᱼᱟ᱾"; diff --git a/firefox-ios/Client/sk.lproj/InfoPlist.strings b/firefox-ios/Client/sk.lproj/InfoPlist.strings index 2c800ebe80426..0ab09fc71f513 100644 --- a/firefox-ios/Client/sk.lproj/InfoPlist.strings +++ b/firefox-ios/Client/sk.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nová karta"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox používa váš fotoaparát na skenovanie QR kódov a snímanie fotografií a videí."; +"NSCameraUsageDescription" = "Ecosia používa váš fotoaparát na skenovanie QR kódov a snímanie fotografií a videí."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox vyžaduje Face ID na prístup k vašim uloženým heslám a spôsobom platby."; +"NSFaceIDUsageDescription" = "Ecosia vyžaduje Face ID na prístup k vašim uloženým heslám a spôsobom platby."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Stránky, ktoré navštevujete, môžu žiadať prezradiť vašu polohu."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox používa váš mikrofón na nahrávanie zvuku."; +"NSMicrophoneUsageDescription" = "Ecosia používa váš mikrofón na nahrávanie zvuku."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Toto vám umožňuje ukladať fotky."; diff --git a/firefox-ios/Client/sl.lproj/InfoPlist.strings b/firefox-ios/Client/sl.lproj/InfoPlist.strings index 93c0bfd61bb9f..4736c2b11b630 100644 --- a/firefox-ios/Client/sl.lproj/InfoPlist.strings +++ b/firefox-ios/Client/sl.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Nov zavihek"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox uporablja vašo kamero za skeniranje QR-kod, fotografiranje in snemanje."; +"NSCameraUsageDescription" = "Ecosia uporablja vašo kamero za skeniranje QR-kod, fotografiranje in snemanje."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox za dostop do shranjenih gesel in plačilnih sredstev zahteva Face ID."; +"NSFaceIDUsageDescription" = "Ecosia za dostop do shranjenih gesel in plačilnih sredstev zahteva Face ID."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Spletne strani lahko zahtevajo vašo lokacijo."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox uporablja vaš mikrofon za snemanje in nalaganje zvoka."; +"NSMicrophoneUsageDescription" = "Ecosia uporablja vaš mikrofon za snemanje in nalaganje zvoka."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "To vam omogoča shranjevati fotografije."; diff --git a/firefox-ios/Client/sq.lproj/InfoPlist.strings b/firefox-ios/Client/sq.lproj/InfoPlist.strings index 919699f7dad03..31cc9a0210afb 100644 --- a/firefox-ios/Client/sq.lproj/InfoPlist.strings +++ b/firefox-ios/Client/sq.lproj/InfoPlist.strings @@ -5,10 +5,10 @@ "New Tab" = "Skedë e Re"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox-i përdor kamerën tuaj për të skanuar kode QR dhe për të bërë foto dhe video."; +"NSCameraUsageDescription" = "Ecosia-i përdor kamerën tuaj për të skanuar kode QR dhe për të bërë foto dhe video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox-i lyp Face ID që të hyjë në fjalëkalimet dhe metoda pagesash tuajat të ruajtura."; +"NSFaceIDUsageDescription" = "Ecosia-i lyp Face ID që të hyjë në fjalëkalimet dhe metoda pagesash tuajat të ruajtura."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Sajte që vizitoni mund të kërkojnë të njohin vendndodhjen tuaj."; diff --git a/firefox-ios/Client/su.lproj/InfoPlist.strings b/firefox-ios/Client/su.lproj/InfoPlist.strings index 3f630aab11840..90297a08c466d 100644 --- a/firefox-ios/Client/su.lproj/InfoPlist.strings +++ b/firefox-ios/Client/su.lproj/InfoPlist.strings @@ -5,10 +5,10 @@ "New Tab" = "Tab Anyar"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox maké kaméra anjeun pikeun nyekén kode QR jeung moto sarta vidéo."; +"NSCameraUsageDescription" = "Ecosia maké kaméra anjeun pikeun nyekén kode QR jeung moto sarta vidéo."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox butuh Face ID pikeun ngaksés sandi kedap nu disimpen jeung cara mayar."; +"NSFaceIDUsageDescription" = "Ecosia butuh Face ID pikeun ngaksés sandi kedap nu disimpen jeung cara mayar."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Raramatloka nu disorang ku anjeun sigana ménta lokasi anjeun."; diff --git a/firefox-ios/Client/sv.lproj/InfoPlist.strings b/firefox-ios/Client/sv.lproj/InfoPlist.strings index edd6aec968b64..84e6998f77915 100644 --- a/firefox-ios/Client/sv.lproj/InfoPlist.strings +++ b/firefox-ios/Client/sv.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Ny flik"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox använder din kamera för att skanna QR-koder och ta foton och video."; +"NSCameraUsageDescription" = "Ecosia använder din kamera för att skanna QR-koder och ta foton och video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox kräver Face ID för att komma åt dina sparade lösenord och betalningsmetoder."; +"NSFaceIDUsageDescription" = "Ecosia kräver Face ID för att komma åt dina sparade lösenord och betalningsmetoder."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Webbsidor du besöker kan be om din plats."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox använder din mikrofon för att spela in och ladda upp ljud."; +"NSMicrophoneUsageDescription" = "Ecosia använder din mikrofon för att spela in och ladda upp ljud."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Detta gör det möjligt att spara bilder."; diff --git a/firefox-ios/Client/th.lproj/InfoPlist.strings b/firefox-ios/Client/th.lproj/InfoPlist.strings index ade5f6507d1c0..026e2385b5e2b 100644 --- a/firefox-ios/Client/th.lproj/InfoPlist.strings +++ b/firefox-ios/Client/th.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "แท็บใหม่"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox ใช้กล้องของคุณเพื่อสแกนคิวอาร์โค้ด รวมทั้งถ่ายรูปและวิดีโอ"; +"NSCameraUsageDescription" = "Ecosia ใช้กล้องของคุณเพื่อสแกนคิวอาร์โค้ด รวมทั้งถ่ายรูปและวิดีโอ"; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox ต้องใช้ Face ID เพื่อเข้าถึงการเข้าสู่ระบบและวิธีการชำระเงินที่คุณบันทึกไว้"; +"NSFaceIDUsageDescription" = "Ecosia ต้องใช้ Face ID เพื่อเข้าถึงการเข้าสู่ระบบและวิธีการชำระเงินที่คุณบันทึกไว้"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "เว็บไซต์ที่คุณเยี่ยมชมอาจขอตำแหน่งที่ตั้งของคุณ"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox ใช้ไมโครโฟนของคุณเพื่อบันทึกและอัปโหลดเสียง"; +"NSMicrophoneUsageDescription" = "Ecosia ใช้ไมโครโฟนของคุณเพื่อบันทึกและอัปโหลดเสียง"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "สิ่งนี้ช่วยให้คุณบันทึกรูปถ่าย"; diff --git a/firefox-ios/Client/tr.lproj/InfoPlist.strings b/firefox-ios/Client/tr.lproj/InfoPlist.strings index 5809a48893b4c..54415fd5b4116 100644 --- a/firefox-ios/Client/tr.lproj/InfoPlist.strings +++ b/firefox-ios/Client/tr.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Yeni sekme"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox, QR kodlarını taramak, fotoğraf ve video çekmek için kameranızı kullanır."; +"NSCameraUsageDescription" = "Ecosia, QR kodlarını taramak, fotoğraf ve video çekmek için kameranızı kullanır."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox, kayıtlı parolalarınıza ve ödeme yöntemlerinize erişmek için Face ID’ye ihtiyaç duyar."; +"NSFaceIDUsageDescription" = "Ecosia, kayıtlı parolalarınıza ve ödeme yöntemlerinize erişmek için Face ID’ye ihtiyaç duyar."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Ziyaret ettiğiniz web siteleri konumunuzu isteyebilir."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox, ses kaydetmek ve yüklemek için mikrofonunuzu kullanır."; +"NSMicrophoneUsageDescription" = "Ecosia, ses kaydetmek ve yüklemek için mikrofonunuzu kullanır."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Fotoğraf kaydetmenize olanak sağlar."; diff --git a/firefox-ios/Client/tt.lproj/InfoPlist.strings b/firefox-ios/Client/tt.lproj/InfoPlist.strings index 3614052239b49..edf1460cca59b 100644 --- a/firefox-ios/Client/tt.lproj/InfoPlist.strings +++ b/firefox-ios/Client/tt.lproj/InfoPlist.strings @@ -5,13 +5,13 @@ "New Tab" = "Яңа таб"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox uses your camera to scan QR codes and take photos and video."; +"NSCameraUsageDescription" = "Ecosia uses your camera to scan QR codes and take photos and video."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Зиярат ителгән вебсайтлар урнашуыгызны сорый ала."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox uses your microphone to record and upload audio."; +"NSMicrophoneUsageDescription" = "Ecosia uses your microphone to record and upload audio."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Фотоларны саклау өчен сез моны куллана аласыз."; diff --git a/firefox-ios/Client/ug.lproj/InfoPlist.strings b/firefox-ios/Client/ug.lproj/InfoPlist.strings index db20abe6bb943..366108b226abc 100644 --- a/firefox-ios/Client/ug.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ug.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "يېڭى بەتكۈچ"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox كامېرا ئارقىلىق QR كودىنى تارايدۇ ھەمدە سۈرەت تارتىپ ۋە سىنغا ئالىدۇ."; +"NSCameraUsageDescription" = "Ecosia كامېرا ئارقىلىق QR كودىنى تارايدۇ ھەمدە سۈرەت تارتىپ ۋە سىنغا ئالىدۇ."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox ساقلانغان ئىم ۋە پۇل تۆلەش ئۇسۇلىڭىزنى زىيارەت قىلىش ئۈچۈن چىراي كىملىكىنى تەلەپ قىلىدۇ."; +"NSFaceIDUsageDescription" = "Ecosia ساقلانغان ئىم ۋە پۇل تۆلەش ئۇسۇلىڭىزنى زىيارەت قىلىش ئۈچۈن چىراي كىملىكىنى تەلەپ قىلىدۇ."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "سىز زىيارەت قىلغان تور بېكەتلەر ئورنىڭىزنى تەلەپ قىلىشى مۇمكىن."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox مىكروفونىڭىزنى ئىشلىتىپ ئۈنگە ئالىدۇ ۋە يۈكلەيدۇ."; +"NSMicrophoneUsageDescription" = "Ecosia مىكروفونىڭىزنى ئىشلىتىپ ئۈنگە ئالىدۇ ۋە يۈكلەيدۇ."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "بۇ ئارقىلىق سۈرەتلەرنى ساقلىغىلى بولىدۇ."; diff --git a/firefox-ios/Client/uk.lproj/InfoPlist.strings b/firefox-ios/Client/uk.lproj/InfoPlist.strings index 274d50ec67bc5..8d7bb8c5a3e6f 100644 --- a/firefox-ios/Client/uk.lproj/InfoPlist.strings +++ b/firefox-ios/Client/uk.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Нова вкладка"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox використовує вашу камеру для сканування QR-кодів і зйомки фотографій і відео."; +"NSCameraUsageDescription" = "Ecosia використовує вашу камеру для сканування QR-кодів і зйомки фотографій і відео."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox потребує Face ID для доступу до ваших збережених паролів та способів оплати."; +"NSFaceIDUsageDescription" = "Ecosia потребує Face ID для доступу до ваших збережених паролів та способів оплати."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Відвідувані вами сайти можуть запитувати ваше розташування."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox використовує ваш мікрофон для запису та вивантаження аудіо."; +"NSMicrophoneUsageDescription" = "Ecosia використовує ваш мікрофон для запису та вивантаження аудіо."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Це дозволяє вам зберігати фотографії."; diff --git a/firefox-ios/Client/ur.lproj/InfoPlist.strings b/firefox-ios/Client/ur.lproj/InfoPlist.strings index 8029379764e31..6908504707d7f 100644 --- a/firefox-ios/Client/ur.lproj/InfoPlist.strings +++ b/firefox-ios/Client/ur.lproj/InfoPlist.strings @@ -5,7 +5,7 @@ "New Tab" = "نیا ٹیب"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox QR کوڈز کو اسکین کرنے اور تصاویر اور ویڈیو لینے کیلئے آپ کے کیمرہ کا استعمال کرتا ہے۔"; +"NSCameraUsageDescription" = "Ecosia QR کوڈز کو اسکین کرنے اور تصاویر اور ویڈیو لینے کیلئے آپ کے کیمرہ کا استعمال کرتا ہے۔"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "جن ویب سائٹ کا آُپ دورہ کریں شاید وہ آُپ سے محل وقوع کی درخواست کر سکتی ہیں۔"; diff --git a/firefox-ios/Client/vi.lproj/InfoPlist.strings b/firefox-ios/Client/vi.lproj/InfoPlist.strings index a0b3a31b644d5..07f0a638d055d 100644 --- a/firefox-ios/Client/vi.lproj/InfoPlist.strings +++ b/firefox-ios/Client/vi.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "Thẻ mới"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox sử dụng máy ảnh của bạn để quét mã QR,chụp ảnh và quay video."; +"NSCameraUsageDescription" = "Ecosia sử dụng máy ảnh của bạn để quét mã QR,chụp ảnh và quay video."; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox yêu cầu Face ID để truy cập vào mật khẩu và phương thức thanh toán đã lưu của bạn."; +"NSFaceIDUsageDescription" = "Ecosia yêu cầu Face ID để truy cập vào mật khẩu và phương thức thanh toán đã lưu của bạn."; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Trang web bạn truy cập có thể yêu cầu vị trí của bạn."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox sử dụng micrô của bạn để ghi và tải lên âm thanh."; +"NSMicrophoneUsageDescription" = "Ecosia sử dụng micrô của bạn để ghi và tải lên âm thanh."; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "Điều này cho phép bạn lưu ảnh."; diff --git a/firefox-ios/Client/zh-CN.lproj/InfoPlist.strings b/firefox-ios/Client/zh-CN.lproj/InfoPlist.strings index 403f2b3595c53..3252da762a7c6 100644 --- a/firefox-ios/Client/zh-CN.lproj/InfoPlist.strings +++ b/firefox-ios/Client/zh-CN.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "新建标签页"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox 需要使用您的相机扫码、拍摄照片或视频。"; +"NSCameraUsageDescription" = "Ecosia 需要使用您的相机扫码、拍摄照片或视频。"; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox 需进行面容 ID 验证才能访问保存的密码和付款方式。"; +"NSFaceIDUsageDescription" = "Ecosia 需进行面容 ID 验证才能访问保存的密码和付款方式。"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "您访问的网站可能会请求您的地理位置。"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox 需要使用您的麦克风来录制并上传音频。"; +"NSMicrophoneUsageDescription" = "Ecosia 需要使用您的麦克风来录制并上传音频。"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "让您可以保存照片。"; diff --git a/firefox-ios/Client/zh-TW.lproj/InfoPlist.strings b/firefox-ios/Client/zh-TW.lproj/InfoPlist.strings index ea001a8622e5f..e7fc0c74598d2 100644 --- a/firefox-ios/Client/zh-TW.lproj/InfoPlist.strings +++ b/firefox-ios/Client/zh-TW.lproj/InfoPlist.strings @@ -5,16 +5,16 @@ "New Tab" = "新分頁"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "Firefox 可使用您的攝影機掃描 QR Code 或為網站拍照、錄影上傳。"; +"NSCameraUsageDescription" = "Ecosia 可使用您的攝影機掃描 QR Code 或為網站拍照、錄影上傳。"; /* Privacy - Face ID Usage Description */ -"NSFaceIDUsageDescription" = "Firefox 需要進行 Face ID 才能存取您儲存的密碼與付款方式資訊。"; +"NSFaceIDUsageDescription" = "Ecosia 需要進行 Face ID 才能存取您儲存的密碼與付款方式資訊。"; /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "您造訪的網站可能會詢問您的所在位置。"; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "Firefox 可使用您的麥克風為網站錄音上傳。"; +"NSMicrophoneUsageDescription" = "Ecosia 可使用您的麥克風為網站錄音上傳。"; /* Privacy - Photo Library Additions Usage Description */ "NSPhotoLibraryAddUsageDescription" = "讓您可儲存相片。"; diff --git a/firefox-ios/CredentialProvider/CredentialProviderEcosia.entitlements b/firefox-ios/CredentialProvider/CredentialProviderEcosia.entitlements new file mode 100644 index 0000000000000..e2a833b8bdbc4 --- /dev/null +++ b/firefox-ios/CredentialProvider/CredentialProviderEcosia.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.com.ecosia.ecosiaapp + + keychain-access-groups + + $(AppIdentifierPrefix)com.ecosia.ecosiaapp + + + diff --git a/firefox-ios/CredentialProvider/CredentialProviderEcosiaBeta.entitlements b/firefox-ios/CredentialProvider/CredentialProviderEcosiaBeta.entitlements new file mode 100644 index 0000000000000..f989c1c3ce357 --- /dev/null +++ b/firefox-ios/CredentialProvider/CredentialProviderEcosiaBeta.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.com.ecosia.ecosiaapp.firefox + + keychain-access-groups + + $(AppIdentifierPrefix)com.ecosia.ecosiaapp.firefox + + + diff --git a/firefox-ios/CredentialProvider/Info.plist b/firefox-ios/CredentialProvider/Info.plist index 919d92d5b9e1a..fdc0f285afc30 100644 --- a/firefox-ios/CredentialProvider/Info.plist +++ b/firefox-ios/CredentialProvider/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 133.0 + $(MARKETING_VERSION) CFBundleVersion 1 MozDevelopmentTeam diff --git a/firefox-ios/Ecosia/Account/Auth/Auth0ProviderProtocol.swift b/firefox-ios/Ecosia/Account/Auth/Auth0ProviderProtocol.swift new file mode 100644 index 0000000000000..6285410f0be39 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/Auth0ProviderProtocol.swift @@ -0,0 +1,111 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Auth0 + +/// Protocol defining authentication operations for Auth0. +public protocol Auth0ProviderProtocol { + + /// The Auth0 configuration settings provider + var settings: Auth0SettingsProviderProtocol { get } + + /// The `CredentialsManager` final concrete type conforming to the protocol `CredentialsManaging` to use for storing and retrieving credentials. + var credentialsManager: CredentialsManagerProtocol { get } + + /// The `WebAuth` instance to use for authentication + var webAuth: WebAuth { get } + + /// Starts the authentication process asynchronously and returns credentials. + /// + /// - Returns: A `Credentials` object upon successful authentication. + /// - Throws: An error if the authentication fails. + func startAuth() async throws -> Credentials + + /// Clears the current authentication session asynchronously. + /// + /// - Throws: An error if the session clearing fails. + func clearSession() async throws + + /// Stores the provided credentials securely. + /// + /// - Parameter credentials: The credentials to store. + /// - Returns: A boolean indicating whether the credentials were successfully stored. + /// - Throws: An error if storing credentials fails. + @discardableResult + func storeCredentials(_ credentials: Credentials) throws -> Bool + + /// Retrieves stored credentials asynchronously. + /// + /// - Returns: The stored `Credentials` object if available. + /// - Throws: An error if retrieving credentials fails. + func retrieveCredentials() async throws -> Credentials + + /// Clears stored credentials. + /// + /// - Returns: A boolean indicating whether the credentials were successfully cleared. + @discardableResult + func clearCredentials() -> Bool + + /// Checks if stored credentials can be renewed. + /// + /// - Returns: A boolean indicating if credentials are renewable. + func canRenewCredentials() -> Bool + + /// Renews credentials asynchronously if possible. + /// + /// - Returns: A `Credentials` object upon successful renewal. + /// - Throws: An error if the credential renewal fails. + @discardableResult + func renewCredentials() async throws -> Credentials +} + +extension Auth0ProviderProtocol { + + /// - Returns: An HTTPS `WebAuth` + func makeHttpsWebAuth() -> WebAuth { + let configuration: URLSessionConfiguration = .default + let ecosiaAuth0Session = URLSession(configuration: configuration.withCloudFlareAuthParameters()) + return Auth0 + .webAuth(clientId: settings.id, + domain: settings.domain, + session: ecosiaAuth0Session) + .useHTTPS() + } +} + +/// Default Protocol Implementation +extension Auth0ProviderProtocol { + + public var settings: Auth0SettingsProviderProtocol { DefaultAuth0SettingsProvider() } + + public var credentialsManager: CredentialsManagerProtocol { EcosiaAuthenticationService.defaultCredentialsManager } + + public func startAuth() async throws -> Credentials { + return try await webAuth.start() + } + + public func clearSession() async throws { + try await webAuth.clearSession() + } + + public func storeCredentials(_ credentials: Credentials) throws -> Bool { + return credentialsManager.store(credentials: credentials) + } + + public func retrieveCredentials() async throws -> Credentials { + return try await credentialsManager.credentials() + } + + public func clearCredentials() -> Bool { + return credentialsManager.clear() + } + + public func canRenewCredentials() -> Bool { + return credentialsManager.canRenew() + } + + public func renewCredentials() async throws -> Credentials { + return try await credentialsManager.renew() + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/Auth0SettingsProvider.swift b/firefox-ios/Ecosia/Account/Auth/Auth0SettingsProvider.swift new file mode 100644 index 0000000000000..1bd1d46dc359b --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/Auth0SettingsProvider.swift @@ -0,0 +1,29 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +public protocol Auth0SettingsProviderProtocol { + var id: String { get } + var domain: String { get } + var cookieDomain: String { get } +} + +public struct DefaultAuth0SettingsProvider: Auth0SettingsProviderProtocol { + + public var id: String { + guard let clientId = EnvironmentFetcher.valueFromMainBundleOrProcessInfo(forKey: "AUTH0_CLIENT_ID") else { + fatalError("AUTH0_CLIENT_ID not found") + } + return clientId + } + + public var domain: String { + return EcosiaEnvironment.current.urlProvider.auth0Domain + } + + public var cookieDomain: String { + return EcosiaEnvironment.current.urlProvider.auth0CookieDomain + } + + public init() {} +} diff --git a/firefox-ios/Ecosia/Account/Auth/AuthError.swift b/firefox-ios/Ecosia/Account/Auth/AuthError.swift new file mode 100644 index 0000000000000..89a7e01ba52e3 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/AuthError.swift @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +/// Errors that can occur during login and logout operations. +public enum AuthError: Error, LocalizedError { + case authenticationFailed(Error) + case credentialsStorageFailed + case credentialsStorageError(Error) + case credentialsClearingFailed + case credentialsRenewalFailed(Error) + case credentialsRetrievalFailed(Error) + case sessionClearingFailed(Error) + case userCancelled + case notLoggedIn + case authFlowConfigurationError(String) + case authFlowSessionManagementFailed(String) + case authFlowInvisibleTabCreationFailed + + public var errorDescription: String? { + switch self { + case .authenticationFailed(let error): + return "Authentication failed: \(error.localizedDescription)" + case .credentialsStorageFailed: + return "Authentication succeeded but failed to store credentials locally" + case .credentialsStorageError(let error): + return "Authentication succeeded but credential storage failed: \(error.localizedDescription)" + case .credentialsClearingFailed: + return "Failed to clear stored credentials" + case .credentialsRenewalFailed(let error): + return "Failed to renew credentials: \(error.localizedDescription)" + case .credentialsRetrievalFailed(let error): + return "Failed to retrieve credentials: \(error.localizedDescription)" + case .sessionClearingFailed(let error): + return "Failed to clear web session and credentials: \(error.localizedDescription)" + case .userCancelled: + return "User cancelled the authentication operation" + case .notLoggedIn: + return "User is not logged in" + case .authFlowConfigurationError(let message): + return "Authentication flow configuration error: \(message)" + case .authFlowSessionManagementFailed(let message): + return "Authentication flow session management failed: \(message)" + case .authFlowInvisibleTabCreationFailed: + return "Authentication flow failed to create required background tabs" + } + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/AuthNotifications.swift b/firefox-ios/Ecosia/Account/Auth/AuthNotifications.swift new file mode 100644 index 0000000000000..4682a805446af --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/AuthNotifications.swift @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Notification names for authentication state changes +extension Notification.Name { + /// Posted when authentication state changes for any window + /// UserInfo contains: windowUUID, authState, actionType + public static let EcosiaAuthStateChanged = Notification.Name("EcosiaAuthStateChanged") + + /// Posted when user profile information is updated from Auth0 + /// This includes user name, email, and profile picture URL + public static let EcosiaUserProfileUpdated = Notification.Name("EcosiaUserProfileUpdated") +} diff --git a/firefox-ios/Ecosia/Account/Auth/AuthStateAction.swift b/firefox-ios/Ecosia/Account/Auth/AuthStateAction.swift new file mode 100644 index 0000000000000..a3a01288582d9 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/AuthStateAction.swift @@ -0,0 +1,55 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +/// Action types for authentication state changes +public enum EcosiaAuthActionType: String, CaseIterable { + case authStateLoaded + case userLoggedIn + case userLoggedOut +} + +/// Action structure for authentication state changes +public struct AuthStateAction { + public let type: EcosiaAuthActionType + public let windowUUID: WindowUUID + public let isLoggedIn: Bool? + public let accountOrigin: AccountOrigin? + public let timestamp: Date + + public init(type: EcosiaAuthActionType, + windowUUID: WindowUUID, + isLoggedIn: Bool? = nil, + accountOrigin: AccountOrigin? = nil, + timestamp: Date = Date()) { + self.type = type + self.windowUUID = windowUUID + self.isLoggedIn = isLoggedIn + self.accountOrigin = accountOrigin + self.timestamp = timestamp + } +} + +/// Window-specific authentication state +public struct AuthWindowState: Equatable { + public let windowUUID: WindowUUID + public let isLoggedIn: Bool + public let authStateLoaded: Bool + public let accountOrigin: AccountOrigin? + public let lastUpdated: Date + + public init(windowUUID: WindowUUID, + isLoggedIn: Bool, + authStateLoaded: Bool, + accountOrigin: AccountOrigin? = nil, + lastUpdated: Date = Date()) { + self.windowUUID = windowUUID + self.isLoggedIn = isLoggedIn + self.authStateLoaded = authStateLoaded + self.accountOrigin = accountOrigin + self.lastUpdated = lastUpdated + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/Credentials+AccountOrigin.swift b/firefox-ios/Ecosia/Account/Auth/Credentials+AccountOrigin.swift new file mode 100644 index 0000000000000..30ce7256e1b40 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/Credentials+AccountOrigin.swift @@ -0,0 +1,59 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Auth0 +import JWTDecode + +/// Indicates whether the user logged in to an existing account or created a new one. +public enum AccountOrigin: Equatable, Sendable { + /// The user created a new account during this authentication session. + case newAccount + /// The user signed in to a pre-existing account. + case existingAccount +} + +extension Credentials { + + /// The namespace prefix used to scope custom claims in Auth0 ID tokens. + /// This is **not** environment dependant – Auth0 requires a namespace that resembles a URI + /// to avoid collisions with registered JWT claims. + /// Configured in the Auth0 Post-Login Action as `CUSTOM_CLAIM_NAMESPACE`. + private static let customClaimNamespace = "https://ecosia.org" + + /// The custom claim key for the account creation timestamp. + /// Set via an Auth0 Post-Login Action: ``api.idToken.setCustomClaim(`${CUSTOM_CLAIM_NAMESPACE}/created_at`, event.user.created_at);`` + private static let createdAtClaim = "\(customClaimNamespace)/created_at" + + /// A UTC calendar used for same-day comparisons. + private static let utcCalendar: Calendar = { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(identifier: "UTC")! + return calendar + }() + + /// Determines whether this login represents a new account or an existing sign-in. + /// + /// Compares the `https://ecosia.org/created_at` custom claim (account creation time) with + /// the current date. If the account was created on the same UTC calendar day, + /// the account is considered new. This accommodates delays caused by e-mail verification + /// while still distinguishing new from returning users. + var accountOrigin: AccountOrigin { + guard let jwt = try? decode(jwt: idToken), + let createdAtString = jwt[Self.createdAtClaim].string, + let createdAt = Self.iso8601Formatter.date(from: createdAtString) else { + return .existingAccount + } + + let sameDay = Self.utcCalendar.isDate(createdAt, inSameDayAs: Date()) + return sameDay ? .newAccount : .existingAccount + } + + /// ISO 8601 formatter configured to handle fractional seconds (e.g. `2026-03-04T10:44:47.942Z`). + private static let iso8601Formatter: ISO8601DateFormatter = { + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + return formatter + }() +} diff --git a/firefox-ios/Ecosia/Account/Auth/CredentialsManager/CredentialsManagerProtocol.swift b/firefox-ios/Ecosia/Account/Auth/CredentialsManager/CredentialsManagerProtocol.swift new file mode 100644 index 0000000000000..2c6e48ddd65f5 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/CredentialsManager/CredentialsManagerProtocol.swift @@ -0,0 +1,49 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Auth0 + +/// A protocol for managing authentication credentials. +public protocol CredentialsManagerProtocol { + + /// Stores the provided credentials securely. + /// + /// - Parameter credentials: The credentials to store. + /// - Returns: A boolean indicating whether the credentials were successfully stored. + @discardableResult + func store(credentials: Credentials) -> Bool + + /// Retrieves stored credentials asynchronously. + /// + /// - Returns: The stored `Credentials` object if available. + /// - Throws: An error if retrieving credentials fails. + func credentials() async throws -> Credentials + + /// Retrieves stored credentials asynchronously with a minimum time-to-live. + /// + /// - Parameters: + /// - scope: The scope to request. If nil, uses the stored credentials' scope. + /// - minTTL: The minimum time in seconds the access token should remain valid. + /// If 0, returns credentials without checking expiry or refreshing. + /// - Returns: The stored `Credentials` object if available. + /// - Throws: An error if retrieving credentials fails. + func credentials(withScope scope: String?, minTTL: Int) async throws -> Credentials + + /// Clears stored credentials. + /// + /// - Returns: A boolean indicating whether the credentials were successfully cleared. + @discardableResult + func clear() -> Bool + + /// Checks if stored credentials can be renewed. + /// + /// - Returns: A boolean indicating if credentials are renewable. + func canRenew() -> Bool + + /// Renews credentials asynchronously if possible. + /// + /// - Returns: A `Credentials` object upon successful renewal. + /// - Throws: An error if the credential renewal fails. + func renew() async throws -> Credentials +} diff --git a/firefox-ios/Ecosia/Account/Auth/CredentialsManager/DefaultCredentialsManager.swift b/firefox-ios/Ecosia/Account/Auth/CredentialsManager/DefaultCredentialsManager.swift new file mode 100644 index 0000000000000..f41d29ff9604e --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/CredentialsManager/DefaultCredentialsManager.swift @@ -0,0 +1,39 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Auth0 + +struct DefaultCredentialsManager: CredentialsManagerProtocol { + + private let credentialManager: CredentialsManager + + init(auth0SettingsProvider: Auth0SettingsProviderProtocol = DefaultAuth0SettingsProvider()) { + self.credentialManager = CredentialsManager(authentication: Auth0.authentication(clientId: auth0SettingsProvider.id, + domain: auth0SettingsProvider.domain)) + } + + func store(credentials: Auth0.Credentials) -> Bool { + credentialManager.store(credentials: credentials) + } + + func credentials() async throws -> Auth0.Credentials { + try await credentialManager.credentials() + } + + func credentials(withScope scope: String?, minTTL: Int) async throws -> Auth0.Credentials { + try await credentialManager.credentials(withScope: scope, minTTL: minTTL) + } + + func clear() -> Bool { + credentialManager.clear() + } + + func canRenew() -> Bool { + credentialManager.canRenew() + } + + func renew() async throws -> Auth0.Credentials { + try await credentialManager.renew() + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/EcosiaAuthWindowRegistry.swift b/firefox-ios/Ecosia/Account/Auth/EcosiaAuthWindowRegistry.swift new file mode 100644 index 0000000000000..8b8fc5381eed2 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/EcosiaAuthWindowRegistry.swift @@ -0,0 +1,71 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +/// Registry for tracking active browser windows that should receive auth state updates +/// This provides minimal external integration point for the authentication system +public final class EcosiaAuthWindowRegistry { + + /// Shared instance for global access + public static let shared = EcosiaAuthWindowRegistry() + + /// Thread-safe storage for window UUIDs + private let queue = DispatchQueue(label: "ecosia.auth.window.registry", attributes: .concurrent) + private var _windows: Set = [] + + private init() {} + + /// Register a window to receive auth state updates + /// - Parameter windowUUID: The window UUID to register + public func registerWindow(_ windowUUID: WindowUUID) { + queue.async(flags: .barrier) { [weak self] in + self?._windows.insert(windowUUID) + } + EcosiaLogger.auth.info("Registered window: \(windowUUID)") + } + + /// Unregister a window from receiving auth state updates + /// - Parameter windowUUID: The window UUID to unregister + public func unregisterWindow(_ windowUUID: WindowUUID) { + queue.async(flags: .barrier) { [weak self] in + self?._windows.remove(windowUUID) + } + EcosiaLogger.auth.info("Unregistered window: \(windowUUID)") + } + + /// Get all registered windows + /// - Returns: Array of registered window UUIDs + public var registeredWindows: [WindowUUID] { + return queue.sync { + Array(_windows) + } + } + + /// Get count of registered windows + /// - Returns: Number of registered windows + public var windowCount: Int { + return queue.sync { + _windows.count + } + } + + /// Check if a specific window is registered + /// - Parameter windowUUID: The window UUID to check + /// - Returns: True if the window is registered + public func isWindowRegistered(_ windowUUID: WindowUUID) -> Bool { + return queue.sync { + _windows.contains(windowUUID) + } + } + + /// Clear all registered windows (for testing/cleanup) + public func clearAllWindows() { + queue.async(flags: .barrier) { [weak self] in + self?._windows.removeAll() + } + EcosiaLogger.auth.info("Cleared all windows") + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/EcosiaAuthenticationService.swift b/firefox-ios/Ecosia/Account/Auth/EcosiaAuthenticationService.swift new file mode 100644 index 0000000000000..39d85a0e68472 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/EcosiaAuthenticationService.swift @@ -0,0 +1,464 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Auth0 +import WebKit +import Common + +/** + The `EcosiaAuthenticationService` class manages user authentication, credential storage, and session management using Auth0. + + This class provides a centralized interface for all authentication operations in the Ecosia app, + including login, logout, credential renewal, and session token management for web-to-native SSO.. + */ +public final class EcosiaAuthenticationService { + + // MARK: - Public Properties + + /// The shared singleton instance of the EcosiaAuthenticationService class. + public static let shared = EcosiaAuthenticationService() + + /// The default credentials manager used across the application. + /// This is a static property to ensure consistent credential storage. + public static let defaultCredentialsManager: CredentialsManagerProtocol = DefaultCredentialsManager() + + /// The Auth0 provider responsible for authentication operations. + /// This can be customized to use different authentication flows (e.g., web auth, native-to-web SSO). + public let auth0Provider: Auth0ProviderProtocol + + // MARK: - Private Properties + + /// The current ID token for the authenticated user. + /// This token contains user identity information and is used for authentication. + public private(set) var idToken: String? + + /// The current access token for the authenticated user. + /// This token is used to access protected resources. + public private(set) var accessToken: String? + + /// The current refresh token for the authenticated user. + /// This token is used to obtain new access tokens when they expire. + private(set) var refreshToken: String? + + /// The current SSO credentials for session transfer between web and native contexts. + private(set) var ssoCredentials: SSOCredentials? + + /// Indicates whether the user is currently logged in. + /// This property is automatically updated when login/logout operations complete successfully. + public private(set) var isLoggedIn: Bool = false + + /// The current user's profile information from Auth0. + /// This includes name, email, profile picture URL, etc. + public private(set) var userProfile: UserProfile? { + didSet { + NotificationCenter.default.post(name: .EcosiaUserProfileUpdated, object: nil) + } + } + + /// For testing: Skip fetching user info from Auth0 to avoid HTTP calls + var skipUserInfoFetch: Bool = false + + // MARK: - Initialization + + /** + Initializes a new instance of the `Auth` class with a specified authentication provider. + + - Parameter auth0Provider: An object conforming to `Auth0ProviderProtocol` that handles + the actual authentication operations. Defaults to `WebAuth0Provider()` for standard web authentication. + + - Note: The initializer automatically attempts to retrieve any stored credentials from the previous session. + */ + public init(auth0Provider: Auth0ProviderProtocol = NativeToWebSSOAuth0Provider()) { + self.auth0Provider = auth0Provider + Task { + await self.retrieveStoredCredentials() + } + } + + /// Logs in the user asynchronously and stores credentials if successful. + /// - Returns: An `AccountOrigin` indicating whether the user created a new account or signed in to an existing one. + /// - Throws: `AuthError.userCancelled` if user cancels the authentication, + /// `AuthError.authenticationFailed` if Auth0 authentication fails, + /// `AuthError.credentialStorageError` if credential storage throws an error, + /// `AuthError.credentialStorageFailed` if credential storage returns false. + @discardableResult + public func login() async throws -> AccountOrigin { + // First, attempt authentication + let credentials: Credentials + do { + credentials = try await auth0Provider.startAuth() + EcosiaLogger.auth.info("Authentication successful") + } catch { + EcosiaLogger.auth.error("Authentication failed: \(error)") + + // Check if user cancelled the login operation + if let webAuthError = error as? WebAuthError, + case .userCancelled = webAuthError { + EcosiaLogger.auth.info("User cancelled login operation") + throw AuthError.userCancelled + } + + throw AuthError.authenticationFailed(error) + } + + // Determine account origin from ID token claims + let accountOrigin = credentials.accountOrigin + EcosiaLogger.auth.info("Account origin: \(accountOrigin == .newAccount ? "new account" : "existing account")") + + // Then, attempt to store credentials + do { + let didStore = try auth0Provider.storeCredentials(credentials) + if didStore { + setupTokensWithCredentials(credentials, settingLoggedInStateTo: true, accountOrigin: accountOrigin) + if !skipUserInfoFetch { + await fetchUserInfoFromAuth0(accessToken: credentials.accessToken) + } + EcosiaLogger.auth.info("Login completed successfully") + } else { + EcosiaLogger.auth.error("Credential storage failed (returned false)") + throw AuthError.credentialsStorageFailed + } + } catch { + EcosiaLogger.auth.error("Credential storage error: \(error)") + throw AuthError.credentialsStorageError(error) + } + + return accountOrigin + } + + /// Logs out the user with option to skip web logout (for web-initiated logout) + /// - Parameter triggerWebLogout: Whether to clear the web session. Defaults to true. + /// - Throws: `AuthError.userCancelled` if user cancels the logout web session, + /// `AuthError.sessionClearingFailed` if both web session and credential clearing fail, + /// `AuthError.credentialsClearingFailed` if only credential clearing fails. + public func logout(triggerWebLogout: Bool = true) async throws { + var sessionClearingError: Error? + + // First, try to clear the web session if requested + if triggerWebLogout { + do { + try await auth0Provider.clearSession() + EcosiaLogger.auth.info("Web session cleared successfully") + } catch { + sessionClearingError = error + EcosiaLogger.auth.error("Failed to clear web session: \(error)") + + // Check if user cancelled the logout operation + if let webAuthError = error as? WebAuthError, + case .userCancelled = webAuthError { + EcosiaLogger.auth.info("User cancelled logout operation") + throw AuthError.userCancelled + } + } + } + + // Then, try to clear stored credentials + let credentialsCleared = auth0Provider.clearCredentials() + + if credentialsCleared { + setupTokensWithCredentials(nil) + // Clear user profile on logout + try await ImageCacheLoader.clearCache(for: userProfile?.pictureURL) + userProfile = nil + EcosiaLogger.auth.info("Credentials cleared successfully") + + // If we had a session clearing error but credentials cleared successfully, + // we still consider the logout successful since the user is logged out locally + if let sessionError = sessionClearingError { + EcosiaLogger.auth.notice("Logout completed with web session clearing warning: \(sessionError)") + } + } else { + // If credentials clearing failed, throw appropriate error + if let sessionError = sessionClearingError { + // Both session and credentials clearing failed + throw AuthError.sessionClearingFailed(sessionError) + } else if auth0Provider.canRenewCredentials() { + // Only credentials clearing failed, as we can still renew them + throw AuthError.credentialsClearingFailed + } + } + } + + /** + Retrieves stored credentials asynchronously from secure storage. + + This method attempts to retrieve previously stored credentials from the device's secure storage. + If successful, it updates the authentication state and credential properties. + + - Note: This method is automatically called during initialization to restore the user's + authentication state from a previous session. + + ## Error Handling + + If credential retrieval fails (e.g., no stored credentials, corrupted data, or keychain access issues), + the method will log the error and leave the user in an unauthenticated state. + */ + public func retrieveStoredCredentials() async { + do { + let credentials = try await auth0Provider.retrieveCredentials() + setupTokensWithCredentials(credentials, settingLoggedInStateTo: true) + if !skipUserInfoFetch { + await fetchUserInfoFromAuth0(accessToken: credentials.accessToken) + } + EcosiaLogger.auth.info("Retrieved stored credentials successfully") + + // Dispatch state loaded with current authentication status + await dispatchAuthStateChange(isLoggedIn: self.isLoggedIn, fromCredentialRetrieval: true) + } catch { + EcosiaLogger.auth.error("Failed to retrieve credentials: \(error)") + // Even if retrieval fails, dispatch state loaded as false + await dispatchAuthStateChange(isLoggedIn: false, fromCredentialRetrieval: true) + } + } + + /** + Renews credentials if they are renewable and close to expiration. + + This method checks if the current credentials can be renewed (i.e., a valid refresh token exists) + and attempts to obtain new credentials using the refresh token. This is useful for maintaining + long-lived sessions without requiring user re-authentication. + + - Note: This method will update the credential properties with the new tokens upon successful renewal. + + ## When to Use + + Call this method when: + - You detect that access tokens are expired or about to expire + - You want to proactively refresh tokens to maintain session continuity + - You encounter authentication errors that might be resolved by token renewal + */ + public func renewCredentialsIfNeeded() async throws { + guard auth0Provider.canRenewCredentials() else { + EcosiaLogger.auth.info("No renewable credentials available") + return + } + + do { + let credentials = try await auth0Provider.renewCredentials() + setupTokensWithCredentials(credentials, settingLoggedInStateTo: true) + EcosiaLogger.auth.info("Renewed credentials successfully") + } catch { + EcosiaLogger.auth.error("Failed to renew credentials: \(error)") + throw AuthError.credentialsRenewalFailed(error) + } + } + + /** + Retrieves a fresh access token, leveraging Auth0's `CredentialsManager.credentials(withScope:minTTL:parameters:headers:)` + to automatically handle token expiry checking and renewal. + + - Returns: A fresh, valid access token + - Throws: `AuthError.notLoggedIn` if stored credentials do not contain a valid access token, + `AuthError.credentialsRetrievalFailed` if credentials cannot be retrieved or refreshed + */ + public func getFreshAccessToken() async throws -> String { + do { + let credentials = try await auth0Provider.retrieveCredentials() + + let accessToken = credentials.accessToken + guard !accessToken.isEmpty else { + EcosiaLogger.auth.error("Retrieved credentials do not contain a valid access token") + throw AuthError.notLoggedIn + } + + // Update cached tokens only after validating the access token + setupTokensWithCredentials(credentials, settingLoggedInStateTo: true) + + return accessToken + } catch let error as AuthError { + // Re-throw AuthError as-is + EcosiaLogger.auth.error("Failed to get fresh access token: \(error)") + throw error + } catch { + // Wrap other errors in credentialsRetrievalFailed + EcosiaLogger.auth.error("Failed to get fresh access token: \(error)") + throw AuthError.credentialsRetrievalFailed(error) + } + } + + /// Helper method to setup tokens and login flag + private func setupTokensWithCredentials(_ credentials: Credentials?, + settingLoggedInStateTo isLoggedIn: Bool = false, + accountOrigin: AccountOrigin? = nil) { + self.idToken = credentials?.idToken + self.accessToken = credentials?.accessToken + self.refreshToken = credentials?.refreshToken + + let wasLoggedIn = self.isLoggedIn + self.isLoggedIn = isLoggedIn + + // Only dispatch the auth state change when the login state actually transitions, + // to avoid triggering observers (e.g. EcosiaAuthUIStateProvider.registerVisitIfNeeded) + // on every token refresh when the user is already logged in. + guard wasLoggedIn != isLoggedIn else { return } + Task { + await dispatchAuthStateChange(isLoggedIn: isLoggedIn, fromCredentialRetrieval: false, accountOrigin: accountOrigin) + } + } + + /// Fetches detailed user information from Auth0's userInfo endpoint + private func fetchUserInfoFromAuth0(accessToken: String) async { + do { + let userInfo = try await Auth0 + .authentication(clientId: auth0Provider.settings.id, + domain: auth0Provider.settings.domain) + .userInfo(withAccessToken: accessToken) + .start() + + // Update user profile with actual data from Auth0 + self.userProfile = UserProfile( + name: userInfo.name ?? userInfo.nickname, + email: userInfo.email, + picture: userInfo.picture?.absoluteString, + sub: userInfo.sub + ) + + EcosiaLogger.auth.info("Updated user profile with Auth0 data: name=\(userInfo.name ?? "nil"), email=\(userInfo.email ?? "nil"), picture=\(userInfo.picture?.absoluteString ?? "nil")") + } catch { + EcosiaLogger.auth.error("Failed to fetch user info from Auth0: \(error)") + } + } +} + +extension EcosiaAuthenticationService { + + // MARK: - SSO Methods + + /** + Retrieves the session transfer token for native-to-web SSO. + + This method obtains a session transfer token that can be used to authenticate the user + in web contexts without requiring them to log in again. This enables seamless transitions + between native and web experiences. + + - Note: This method requires the user to be logged in and only works with providers that + support SSO credentials (e.g., `NativeToWebSSOAuth0Provider`). + + ## Prerequisites + + - User must be logged in (`isLoggedIn` must be `true`) + - The auth provider must support SSO credential retrieval + */ + public func getSessionTransferToken() async { + guard isLoggedIn else { + EcosiaLogger.auth.notice("Cannot get session transfer token - user not logged in") + return + } + ssoCredentials = await retrieveSSOCredentials() + if ssoCredentials != nil { + EcosiaLogger.session.info("Retrieved session transfer token for SSO") + } + } + + /** + Returns a session token cookie for web authentication. + + This method creates an HTTP cookie containing the session transfer token that can be used + to authenticate the user in web views or web contexts. + + - Returns: An `HTTPCookie` object containing the session transfer token, or `nil` if the user + is not logged in or no session token is available. + + ## Usage + + ```swift + if let cookie = auth.getSessionTokenCookie() { + webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie) + } + ``` + */ + public func getSessionTokenCookie() -> HTTPCookie? { + guard isLoggedIn else { + EcosiaLogger.auth.notice("Cannot create session cookie - user not logged in") + return nil + } + return makeSessionTokenCookieWithSSOCredentials(ssoCredentials) + } + + /** + Retrieves SSO credentials from the authentication provider if supported. + + This method checks if the current authentication provider supports SSO credential retrieval + (specifically `NativeToWebSSOAuth0Provider`) and requests the session transfer token. + + - Returns: `SSOCredentials` containing the session transfer token and expiration information, + or `nil` if the provider doesn't support SSO or if the request fails. + + - Note: This method performs a type check to ensure the provider supports SSO operations + before attempting to retrieve credentials. + */ + private func retrieveSSOCredentials() async -> SSOCredentials? { + if let authProvider = auth0Provider as? NativeToWebSSOAuth0Provider { + do { + return try await authProvider.getSSOCredentials() + } catch { + EcosiaLogger.auth.error("Failed to retrieve SSO credentials: \(error)") + } + } + return nil + } + + /** + Creates an HTTP cookie containing the session transfer token. + + This method constructs a properly formatted HTTP cookie that can be used to authenticate + the user in web contexts. The cookie includes security attributes and expiration information. + + - Parameter ssoCredentials: The SSO credentials containing the session transfer token. + - Returns: An `HTTPCookie` object configured for the Auth0 domain, or `nil` if credentials are unavailable. + + ## Cookie Properties + + The created cookie includes: + - Domain: Set to the Auth0 domain from the provider settings + - Path: Set to "/" for site-wide access + - Name: "auth0_session_transfer_token" + - Value: The session transfer token + - Expires: Set to the token's expiration time + - Secure: Set to `true` for HTTPS-only transmission + */ + private func makeSessionTokenCookieWithSSOCredentials(_ ssoCredentials: SSOCredentials?) -> HTTPCookie? { + guard let ssoCredentials else { + EcosiaLogger.auth.notice("No SSO credentials available to create session cookie") + return nil + } + return HTTPCookie(properties: [ + .domain: auth0Provider.settings.cookieDomain, + .path: "/", + .name: "auth0_session_transfer_token", + .value: ssoCredentials.sessionTransferToken, + .expires: ssoCredentials.expiresIn, + .secure: true + ]) + } +} + +extension EcosiaAuthenticationService { + + // MARK: - State Management Integration + + /** + Dispatches authentication state changes to the new state management system + and posts legacy notifications for backward compatibility. + + - Parameters: + - isLoggedIn: Current authentication status + - fromCredentialRetrieval: Whether this is from credential retrieval (for state loaded) + */ + private func dispatchAuthStateChange(isLoggedIn: Bool, fromCredentialRetrieval: Bool, accountOrigin: AccountOrigin? = nil) async { + // Determine the correct action type + let actionType: EcosiaAuthActionType + if fromCredentialRetrieval { + actionType = .authStateLoaded + } else if isLoggedIn { + actionType = .userLoggedIn + } else { + actionType = .userLoggedOut + } + + // Dispatch to the new state management system + EcosiaBrowserWindowAuthManager.shared.dispatchAuthState(isLoggedIn: isLoggedIn, actionType: actionType, accountOrigin: accountOrigin) + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/EcosiaBrowserWindowAuthManager.swift b/firefox-ios/Ecosia/Account/Auth/EcosiaBrowserWindowAuthManager.swift new file mode 100644 index 0000000000000..67b852c11893c --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/EcosiaBrowserWindowAuthManager.swift @@ -0,0 +1,174 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +/// Main authentication state manager that provides Redux-like state management +/// This system manages authentication state for multiple browser windows independently +public final class EcosiaBrowserWindowAuthManager { + + /// Shared instance for global access + public static let shared = EcosiaBrowserWindowAuthManager() + + /// Thread-safe storage for window-specific authentication states + private let queue = DispatchQueue(label: "ecosia.auth.state.manager", attributes: .concurrent) + private var _windowStates: [WindowUUID: AuthWindowState] = [:] + + /// Notification center for broadcasting state changes + private let notificationCenter = NotificationCenter.default + + private init() { + EcosiaLogger.auth.info("EcosiaBrowserWindowAuthManager initialized") + } + + // MARK: - State Management + + /// Get current authentication state for a specific window + /// - Parameter windowUUID: The window UUID to get state for + /// - Returns: AuthWindowState if available, nil otherwise + public func getAuthState(for windowUUID: WindowUUID) -> AuthWindowState? { + return queue.sync { + _windowStates[windowUUID] + } + } + + /// Get authentication states for all windows + /// - Returns: Dictionary of window UUIDs to their auth states + public func getAllAuthStates() -> [WindowUUID: AuthWindowState] { + return queue.sync { + _windowStates + } + } + + /// Dispatch an authentication action for a specific window + /// - Parameters: + /// - action: The authentication action to dispatch + /// - windowUUID: The window UUID to dispatch the action for + public func dispatch(action: AuthStateAction, for windowUUID: WindowUUID) { + let newState = reduce(currentState: getAuthState(for: windowUUID), action: action) + + queue.async(flags: .barrier) { [weak self] in + self?._windowStates[windowUUID] = newState + } + + // Broadcast state change + notificationCenter.post( + name: .EcosiaAuthStateChanged, + object: self, + userInfo: [ + "windowUUID": windowUUID, + "authState": newState, + "actionType": action.type + ] + ) + + EcosiaLogger.auth.debug("Dispatched \(action.type.rawValue) for window \(windowUUID)") + } + + /// Dispatch authentication state changes to all registered windows + /// - Parameters: + /// - isLoggedIn: Current login status + /// - actionType: Type of authentication action + /// - accountOrigin: Whether the user created a new account or signed in to an existing one (login only) + public func dispatchAuthState(isLoggedIn: Bool, actionType: EcosiaAuthActionType, accountOrigin: AccountOrigin? = nil) { + let windowUUIDs = EcosiaAuthWindowRegistry.shared.registeredWindows + + for windowUUID in windowUUIDs { + let action = AuthStateAction( + type: actionType, + windowUUID: windowUUID, + isLoggedIn: isLoggedIn, + accountOrigin: accountOrigin + ) + dispatch(action: action, for: windowUUID) + } + + EcosiaLogger.auth.debug("Dispatched \(actionType.rawValue) to \(windowUUIDs.count) windows") + } + + // MARK: - State Reduction + + /// Reduce current state with an action to produce new state + /// - Parameters: + /// - currentState: Current authentication state (can be nil) + /// - action: Action to apply to the state + /// - Returns: New authentication state + private func reduce(currentState: AuthWindowState?, action: AuthStateAction) -> AuthWindowState { + let existingState = currentState ?? AuthWindowState( + windowUUID: action.windowUUID, + isLoggedIn: false, + authStateLoaded: false + ) + + switch action.type { + case .authStateLoaded: + return AuthWindowState( + windowUUID: action.windowUUID, + isLoggedIn: action.isLoggedIn ?? false, + authStateLoaded: true, + accountOrigin: action.accountOrigin ?? existingState.accountOrigin, + lastUpdated: action.timestamp + ) + + case .userLoggedIn: + return AuthWindowState( + windowUUID: action.windowUUID, + isLoggedIn: true, + authStateLoaded: existingState.authStateLoaded, + accountOrigin: action.accountOrigin, + lastUpdated: action.timestamp + ) + + case .userLoggedOut: + return AuthWindowState( + windowUUID: action.windowUUID, + isLoggedIn: false, + authStateLoaded: existingState.authStateLoaded, + accountOrigin: nil, + lastUpdated: action.timestamp + ) + } + } + + // MARK: - State Subscription + + /// Subscribe to authentication state changes + /// - Parameters: + /// - observer: Object that will observe the changes + /// - selector: Selector method to call on state change + public func subscribe(observer: AnyObject, selector: Selector) { + notificationCenter.addObserver( + observer, + selector: selector, + name: .EcosiaAuthStateChanged, + object: self + ) + } + + /// Unsubscribe from authentication state changes + /// - Parameter observer: Object to remove from observations + public func unsubscribe(observer: AnyObject) { + notificationCenter.removeObserver(observer, name: .EcosiaAuthStateChanged, object: self) + } + + // MARK: - Cleanup + + /// Remove state for a specific window + /// - Parameter windowUUID: Window UUID to remove state for + public func removeWindowState(for windowUUID: WindowUUID) { + queue.async(flags: .barrier) { [weak self] in + self?._windowStates.removeValue(forKey: windowUUID) + } + EcosiaLogger.auth.debug("Removed state for window \(windowUUID)") + } + + /// Clear all window states (for testing/cleanup) + public func clearAllStates() { + queue.async(flags: .barrier) { [weak self] in + self?._windowStates.removeAll() + } + EcosiaLogger.auth.debug("Cleared all states") + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/NativeToWebSSOAuth0Provider.swift b/firefox-ios/Ecosia/Account/Auth/NativeToWebSSOAuth0Provider.swift new file mode 100644 index 0000000000000..ed66ab5596bb3 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/NativeToWebSSOAuth0Provider.swift @@ -0,0 +1,101 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Auth0 +import WebKit + +/// Native to Web SSO implementation of `Auth0ProviderProtocol` using Auth0's SDK and performing Native to Web SSO via REST API to perform the session token exchange. +public struct NativeToWebSSOAuth0Provider: Auth0ProviderProtocol { + + public let settings: Auth0SettingsProviderProtocol + public let credentialsManager: CredentialsManagerProtocol + public typealias SessionToken = String + private let environment: Environment + + enum NativeToWebSSOError: Error, Equatable { + case invalidResponse + case missingRefreshToken(String) + case missingConfiguration(String) + } + + public init(settings: Auth0SettingsProviderProtocol = DefaultAuth0SettingsProvider(), + credentialsManager: CredentialsManagerProtocol? = nil, + environment: Environment = .current) { + self.settings = settings + self.credentialsManager = credentialsManager ?? DefaultCredentialsManager(auth0SettingsProvider: settings) + self.environment = environment + } + + public var webAuth: WebAuth { + makeHttpsWebAuth() + .useEphemeralSession() + .audience(environment.urlProvider.authApiAudience.absoluteString) + .scope("openid profile email offline_access read:impact write:impact") + } + + public func startAuth() async throws -> Credentials { + return try await webAuth.start() + } + + /// Custom clearSession implementation that bypasses Auth0's default logout alert + /// We provide immediate logout without any confirmation popups for better UX + public func clearSession() async throws { + // Skip calling webAuth.clearSession() to avoid Auth0's native logout alert + // Logout happens immediately without any confirmation dialogs by clearing the auth session cookie + await clearWebSessionCookies() + EcosiaLogger.auth.info("\(Cookie.authSession.name) cookie cleared successfully") + } + + /// Clears EASC (Ecosia Auth Session Cookie) cookies from the default web data store + private func clearWebSessionCookies() async { + let cookieStore = await WKWebsiteDataStore.default().httpCookieStore + guard let sessionCookie = await cookieStore.allCookies().first(where: { $0.name == Cookie.authSession.name }) else { return } + await cookieStore.deleteCookie(sessionCookie) + } +} + +extension NativeToWebSSOAuth0Provider { + + /// Requests the `session_token` with the `refresh_token`. + /// + /// - Returns: A `session_token` as `SessionToken` (a `String` type). + /// - Throws: An error if the retrieval fails. + /// - Note: This method retrieves credentials without minTTL to avoid unnecessary token refresh + /// that could invalidate the SSO session transfer token before it's used. + public func getSSOCredentials() async throws -> SSOCredentials { + // Retrieve credentials without triggering refresh by setting minTTL to 0 + // This prevents the refresh token from being rotated before we use it for SSO + let credentials = try await credentialsManager.credentials(withScope: nil, minTTL: 0) + guard let refreshToken = credentials.refreshToken else { + throw NativeToWebSSOError.missingRefreshToken("Refresh token is missing. Please check your credentials.") + } + + let configuration: URLSessionConfiguration = .default + let ecosiaAuth0Session = URLSession(configuration: configuration.withCloudFlareAuthParameters()) + return try await Auth0 + .authentication(clientId: settings.id, + domain: settings.domain, + session: ecosiaAuth0Session) + .ssoExchange(withRefreshToken: refreshToken) + .start() + } + + /// Retrieves configuration values from the Auth0.plist file. + /// + /// - Parameter bundle: The bundle containing the Auth0.plist file. Defaults to `.ecosia`. + /// - Returns: A tuple containing the `clientId` and `domain` if available, otherwise `nil`. + func configurationValues(bundle: Bundle = .ecosia) -> (clientId: String, domain: String)? { + guard let path = bundle.path(forResource: "Auth0", ofType: "plist"), + let values = NSDictionary(contentsOfFile: path) as? [String: Any] else { + EcosiaLogger.auth.error("Missing Auth0.plist file with 'ClientId' and 'Domain' entries in main bundle!") + return nil + } + + guard let clientId = values["ClientId"] as? String, let domain = values["Domain"] as? String else { + EcosiaLogger.auth.error("Auth0.plist file at \(path) is missing 'ClientId' and/or 'Domain' entries!") + return nil + } + return (clientId: clientId, domain: domain) + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/UserProfile.swift b/firefox-ios/Ecosia/Account/Auth/UserProfile.swift new file mode 100644 index 0000000000000..eaa57a3f1bfbf --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/UserProfile.swift @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// User profile information retrieved from Auth0 +public struct UserProfile: Codable, Equatable { + /// User's display name (falls back to nickname if name is nil) + public let name: String? + + /// User's email address + public let email: String? + + /// URL string for user's profile picture + private let picture: String? + + /// User's unique identifier from Auth0 + public let sub: String + + public init(name: String?, email: String?, picture: String?, sub: String) { + self.name = name + self.email = email + self.picture = picture + self.sub = sub + } + + /// Display name with fallback logic + public var displayName: String? { + return name ?? email?.components(separatedBy: "@").first + } + + /// Profile picture URL + public var pictureURL: URL? { + guard let picture else { return nil } + return URL(string: picture) + } +} diff --git a/firefox-ios/Ecosia/Account/Auth/WebAuth0Provider.swift b/firefox-ios/Ecosia/Account/Auth/WebAuth0Provider.swift new file mode 100644 index 0000000000000..01e2ae3687dd4 --- /dev/null +++ b/firefox-ios/Ecosia/Account/Auth/WebAuth0Provider.swift @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Auth0 + +/// Default implementation of `Auth0ProviderProtocol` using Auth0's SDK, utilizing Web Auth Login solely. +public struct WebAuth0Provider: Auth0ProviderProtocol { + + public var webAuth: WebAuth { makeHttpsWebAuth() } + + public init() {} +} diff --git a/firefox-ios/Ecosia/Analytics/Analytics+Configuration.swift b/firefox-ios/Ecosia/Analytics/Analytics+Configuration.swift new file mode 100644 index 0000000000000..900b74f5948b0 --- /dev/null +++ b/firefox-ios/Ecosia/Analytics/Analytics+Configuration.swift @@ -0,0 +1,112 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +internal import SnowplowTracker + +extension Analytics { + + /// Configuration for the Snowplow tracker. + /// - Includes settings such as application ID, session context, application context, platform context, and geolocation context. + /// - This configuration also enables tracking of minimal device properties like Apple ID for vendors (IDFV). + static let trackerConfiguration = TrackerConfiguration() + .appId(Bundle.version) + .sessionContext(true) + .applicationContext(true) + .platformContext(true) + .platformContextProperties([.appleIdfv]) // track minimal device properties + .geoLocationContext(true) + .deepLinkContext(false) + .screenContext(false) + + /// Configuration for the Snowplow subject. + /// - Sets the user ID using the unique analytics ID associated with the user. + static let subjectConfiguration = SubjectConfiguration() + .userId(User.shared.analyticsId.uuidString) + + /// Configuration for the daily tracking plugin. + /// - This plugin filters events based on whether a day has passed since the last check for a specific identifier. + /// - It specifically filters structured events related to in-app navigation and resume actions. + /// - Returns: A configured `PluginConfiguration` that determines whether the event should be tracked. + static var appResumeDailyTrackingPluginConfiguration: PluginConfiguration { + let identifier = "appResumeDailyTrackingPluginConfiguration" + let plugin = PluginConfiguration(identifier: identifier) + return plugin.filter(schemas: [ + "se" // Structured Events + ]) { event in + let isInAppLabel = event.payload["se_la"] as? String == Analytics.Label.Navigation.inapp.rawValue + let isResumeEvent = event.payload["se_ac"] as? String == Analytics.Action.Activity.resume.rawValue + let isInAppResumeEvent = isInAppLabel && isResumeEvent + + guard isInAppResumeEvent else { + return true + } + + return Self.hasDayPassedSinceLastCheck(for: identifier) + } + } + + /// Configuration for the install tracking plugin. + /// - This plugin filters install events, allowing them to be tracked only if it's the first installation. + /// - Returns: A configured `PluginConfiguration` that determines whether the event should be tracked. + static var appInstallTrackingPluginConfiguration: PluginConfiguration { + let identifier = "appInstallTrackingPluginConfiguration" + let plugin = PluginConfiguration(identifier: identifier) + return plugin.filter(schemas: [ + Self.installSchema + ]) { _ in + return Self.isFirstInstall(for: identifier) + } + } +} + +extension Analytics { + + /// Checks if the current installation is the first time the app has been installed. + /// - Parameter identifier: A unique identifier used to store and retrieve the first install check status from `UserDefaults`. + /// - Returns: A Boolean value indicating whether the app is being installed for the first time. + static func isFirstInstall(for identifier: String) -> Bool { + let defaults = UserDefaults.standard + let isFirstTime = defaults.object(forKey: identifier) as? Bool ?? { + defaults.set(false, forKey: identifier) + return true + }() + return isFirstTime && EcosiaInstallType.get() != .upgrade + } +} + +extension Analytics { + + /// Checks if a day has passed since the last check for a specific event. + /// - Parameter identifier: A unique identifier used to store and retrieve the last check date from `UserDefaults`. + /// - Returns: A Boolean value indicating whether a day has passed since the last check. If no previous check exists, returns `true` and records the current date. + public static func hasDayPassedSinceLastCheck(for identifier: String) -> Bool { + let now = Date() + let defaults = UserDefaults.standard + + // Retrieve the last check date from UserDefaults + if let lastCheck = defaults.object(forKey: identifier) as? Date { + // Calculate the number of days between the last check and now + let calendar = Calendar.current + let components = calendar.dateComponents([.day], from: lastCheck, to: now) + + if let day = components.day { + // if a day or more has passed + if day >= 1 { + defaults.set(now, forKey: identifier) // update the last check date + return true + } else { + // less than a day has passed + return false + } + } + } else { + // If no last check date exists, set the current date and return true + defaults.set(now, forKey: identifier) + return true + } + + return false + } +} diff --git a/firefox-ios/Ecosia/Analytics/Analytics.Values.swift b/firefox-ios/Ecosia/Analytics/Analytics.Values.swift new file mode 100644 index 0000000000000..4813389e55c06 --- /dev/null +++ b/firefox-ios/Ecosia/Analytics/Analytics.Values.swift @@ -0,0 +1,305 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Analytics { + public enum Category: String { + case + account, + activity, + autocomplete = "autocomplete_suggestion", + bookmarks, + brazeIAM = "braze_iam", + browser, + external, + intro, + invitations, + menu, + menuStatus = "menu_status", + migration, + navigation, + ntp, + pushNotificationConsent = "push_notification_consent", + settings + } + + public enum Label: String { + case + analytics, + accountNudgeCard = "account_nudge_card", + clear, + close, + market, + profile, + signIn = "sign_in", + signUp = "sign_up", + toolbar + + public enum Bookmarks: String { + case + importFunctionality = "import_functionality", + learnMore = "learn_more", + `import` + } + + public enum DefaultBrowser: String { + case + deeplink = "default_browser_deeplink", + promo = "default_browser_promo", + settings = "default_browser_settings", + settingsNudgeCard = "default_browser_settings_nudge_card" + } + + public enum Menu: String { + case + bookmarks, + copyLink = "copy_link", + customizeHomepage = "customize_homepage", + downloads, + findInPage = "find_in_page", + help, + history, + home, + newTab = "new_tab", + openInSafari = "open_in_safari", + readingList = "reading_list", + requestDesktopSite = "request_desktop_site", + settings, + share, + zoom + } + + public enum MenuStatus: String { + case + bookmark, + darkMode = "dark_mode", + readingList = "reading_list", + shortcut + } + + public enum Migration: String { + case + tabs + } + + public enum Navigation: String { + case + help, + inapp, + market, + news, + privacy, + projects, + sendFeedback = "send_feedback", + terms + } + + public enum NTP: String { + case + climateCounter = "climate_counter", + customize, + impact, + news, + quickActions = "quick_actions", + topSites = "top_sites" + } + + public enum Onboarding: String { + case + welcome = "welcome_screen", + firstSearchCard = "first_search_card", + serpTour = "serp_tour", + privacyTour = "privacy_tour" + } + + public enum Referral: String { + case + invite, + inviteScreen = "invite_screen", + learnMore = "learn_more", + linkCopying = "link_copying", + promo + } + + public enum Settings: String { + case + aiOverviews = "ai_overviews", + analytics, + clear, + toolbar + } + + public enum AISearch: String { + case + cta = "ai_search_cta" + } + } + + public enum Action: String { + case + change, + click, + disable, + dismiss, + display, + enable, + error, + open, + receive, + success, + view + + public enum Activity: String { + case + launch, + resume + } + + public enum APNConsent: String { + case + allow, + deny, + error, + view + } + + public enum Bookmarks: String { + case + `import` + } + + public enum BrazeIAM: String { + case + click, + dismiss, + view + } + + public enum NewsletterCardExperiment: String { + case + click, + dismiss, + view + } + + public enum NTPCustomization: String { + case + click, + disable, + enable + } + + public enum Promo: String { + case + click, + close, + view + } + + public enum Referral: String { + case + claim, + click, + open, + send, + view + } + + public enum SeedCounter: String { + case + level, + collect, + click + } + + public enum TopSite: String { + case + click, + openNewTab = "open_new_tab", + openPrivateTab = "open_private_tab", + pin, + remove, + unpin + } + + public enum Welcome: String { + case + click, + display + } + } + + public enum Property: String { + case + account, + cancel, + enable, + disable, + header, + home, + detail, + menu, + nativeSettings = "native_settings", + signOut = "sign_out" + + public enum APNConsent: String { + case + home, + onLaunchPrompt = "on_launch_prompt" + } + + public enum Bookmarks: String { + case + `import`, + export, + emptyState = "empty_state", + success, + error + } + + public enum Library: String { + case + bookmarks, + downloads, + history, + readingList = "reading_list" + } + + public enum ShareContent: String { + case + ntp, + web, + file + } + + public enum SettingsPrivateDataSection: String { + case + websites = "websites_data", + main = "all_private_data" + } + + public enum SpotlightTour: String { + case + skip, + next, + back, + complete, + readMore = "read_more" + } + + public enum TopSite: String { + case + `default`, + mostVisited = "most_visited", + pinned + } + + public enum Welcome: String { + case + signIn = "sign_in", + maybeLater = "maybe_later" + } + } +} diff --git a/firefox-ios/Ecosia/Analytics/Analytics.swift b/firefox-ios/Ecosia/Analytics/Analytics.swift new file mode 100644 index 0000000000000..ff55c986e6f49 --- /dev/null +++ b/firefox-ios/Ecosia/Analytics/Analytics.swift @@ -0,0 +1,563 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UIKit +internal import SnowplowTracker + +open class Analytics { + private static let abTestSchema = "iglu:org.ecosia/abtest_context/jsonschema/1-0-1" + private static let consentSchema = "iglu:org.ecosia/eccc_context/jsonschema/1-0-2" + private static let feedbackSchema = "iglu:org.ecosia/ios_feedback_event/jsonschema/1-0-0" + static let impactBalanceSchema = "iglu:org.ecosia/impact_balance/jsonschema/1-0-0" + private static let abTestRoot = "ab_tests" + private static let namespace = "ios_sp" + static let installSchema = "iglu:org.ecosia/ios_install_event/jsonschema/1-0-0" + static let userSchema = "iglu:org.ecosia/app_user_state_context/jsonschema/1-0-0" + static let inappSearchSchema = "iglu:org.ecosia/inapp_search_event/jsonschema/1-0-1" + private static let shouldUseMicroInstanceKey = "shouldUseMicroInstance" + public static var shouldUseMicroInstance: Bool { + get { + UserDefaults.standard.bool(forKey: shouldUseMicroInstanceKey) + } + set { + UserDefaults.standard.set(newValue, forKey: shouldUseMicroInstanceKey) + Analytics.updateTrackerController() + } + } + + public static var shared = Analytics() + private var tracker: TrackerController + private let notificationCenter: AnalyticsUserNotificationCenterProtocol + + internal init(notificationCenter: AnalyticsUserNotificationCenterProtocol = AnalyticsUserNotificationCenterWrapper()) { + tracker = Self.makeTracker() + tracker.installAutotracking = true + tracker.screenViewAutotracking = false + tracker.lifecycleAutotracking = false + tracker.screenEngagementAutotracking = false + tracker.exceptionAutotracking = false + tracker.diagnosticAutotracking = false + self.notificationCenter = notificationCenter + } + + internal func track(_ event: SnowplowTracker.Event) { + guard User.shared.sendAnonymousUsageData else { return } + if let structuredEvent = event as? Structured { + appendContextIfNeeded(to: structuredEvent) + } +#if !TESTING + _ = tracker.track(event) +#endif + } + + private static func updateTrackerController() { + Analytics.shared.tracker = makeTracker() + } + + private static func getTestContext(from toggle: Unleash.Toggle.Name) -> SelfDescribingJson? { + let variant = Unleash.getVariant(toggle).name + guard variant != "disabled" else { return nil } + + let variantContext: [String: String] = [toggle.rawValue: variant] + let abTestContext: [String: AnyHashable] = [abTestRoot: variantContext] + return SelfDescribingJson(schema: abTestSchema, andDictionary: abTestContext) + } + + public func reset() { + User.shared.analyticsId = .init() + tracker = Self.makeTracker() + } + + // MARK: App events + public func install() { + track(SelfDescribing(schema: Self.installSchema, + payload: ["app_v": Bundle.version as NSObject])) + } + + public func activity(_ action: Action.Activity) { + let event = Structured(category: Category.activity.rawValue, + action: action.rawValue) + .label(Analytics.Label.Navigation.inapp.rawValue) + + appendActivityContextIfNeeded(action, event) { [weak self] in + self?.track(event) + } + } + + // MARK: Bookmarks + public func bookmarksPerformImportExport(_ property: Property.Bookmarks) { + let event = Structured(category: Category.bookmarks.rawValue, + action: Action.click.rawValue) + .label(Label.Bookmarks.importFunctionality.rawValue) + .property(property.rawValue) + track(event) + } + + public func bookmarksEmptyLearnMoreClicked() { + let event = Structured(category: Category.bookmarks.rawValue, + action: Action.click.rawValue) + .label(Label.Bookmarks.learnMore.rawValue) + .property(Property.Bookmarks.emptyState.rawValue) + track(event) + } + + public func bookmarksImportEnded(_ property: Property.Bookmarks) { + let event = Structured(category: Category.bookmarks.rawValue, + action: Action.Bookmarks.import.rawValue) + .label(Label.Bookmarks.import.rawValue) + .property(property.rawValue) + track(event) + } + + // MARK: Braze IAM + public func brazeIAM(action: Action.BrazeIAM, messageOrButtonId: String?) { + track(Structured(category: Category.brazeIAM.rawValue, + action: action.rawValue) + .property(messageOrButtonId)) + } + + // MARK: Default Browser + public func appOpenAsDefaultBrowser() { + let event = Structured(category: Category.external.rawValue, + action: Action.receive.rawValue) + .label(Label.DefaultBrowser.deeplink.rawValue) + + track(event) + } + + public func defaultBrowser(_ action: Action.Promo) { + track(Structured(category: Category.browser.rawValue, + action: action.rawValue) + .label(Label.DefaultBrowser.promo.rawValue) + .property(Property.home.rawValue)) + } + + public func defaultBrowserSettingsShowsDetailViewVia(_ label: Label.DefaultBrowser) { + track(Structured(category: Category.browser.rawValue, + action: Action.open.rawValue) + .label(label.rawValue)) + } + + public func defaultBrowserSettingsViaNudgeCardDismiss() { + track(Structured(category: Category.browser.rawValue, + action: Action.dismiss.rawValue) + .label(Label.DefaultBrowser.settingsNudgeCard.rawValue)) + } + + public func defaultBrowserSettingsOpenNativeSettingsVia(_ label: Label.DefaultBrowser) { + track(Structured(category: Category.browser.rawValue, + action: Action.click.rawValue) + .label(label.rawValue) + .property(Property.nativeSettings.rawValue)) + } + + public func defaultBrowserSettingsDismissDetailViewVia(_ label: Label.DefaultBrowser) { + track(Structured(category: Category.browser.rawValue, + action: Action.dismiss.rawValue) + .label(label.rawValue) + .property(Property.detail.rawValue)) + } + + // MARK: Menu + public func menuClick(_ item: Analytics.Label.Menu) { + let event = Structured(category: Category.menu.rawValue, + action: Action.click.rawValue) + .label(item.rawValue) + track(event) + } + + public func menuShare(_ content: Property.ShareContent) { + let event = Structured(category: Category.menu.rawValue, + action: Action.click.rawValue) + .label(Label.Menu.share.rawValue) + .property(content.rawValue) + track(event) + } + + public func menuStatus(changed item: Analytics.Label.MenuStatus, to: Bool) { + let event = Structured(category: Category.menuStatus.rawValue, + action: Action.click.rawValue) + .label(item.rawValue) + .value(.init(value: to)) + track(event) + } + + // MARK: Migration + public func migration(_ success: Bool) { + track(Structured(category: Category.migration.rawValue, + action: success ? Action.success.rawValue : Action.error.rawValue)) + } + + public func migrationError(in migration: Label.Migration, message: String) { + track(Structured(category: Category.migration.rawValue, + action: Action.error.rawValue) + .label(migration.rawValue) + .property(message)) + } + + // MARK: Navigation + public func navigation(_ action: Action, label: Label.Navigation) { + track(Structured(category: Category.navigation.rawValue, + action: action.rawValue) + .label(label.rawValue)) + } + + public func navigationOpenNews(_ id: String) { + track(Structured(category: Category.navigation.rawValue, + action: Action.open.rawValue) + .label(Label.Navigation.news.rawValue) + .property(id)) + } + + public func navigationChangeMarket(_ new: String) { + track(Structured(category: Category.navigation.rawValue, + action: Action.change.rawValue) + .label(Label.Navigation.market.rawValue) + .property(new)) + } + + // MARK: NTP + public func ntpCustomisation(_ action: Action.NTPCustomization, label: Label.NTP) { + track(Structured(category: Category.ntp.rawValue, + action: action.rawValue) + .label(label.rawValue)) + } + + public func ntpTopSite(_ action: Action.TopSite, property: Property.TopSite, position: NSNumber? = nil) { + track(Structured(category: Category.ntp.rawValue, + action: action.rawValue) + .label(Label.NTP.topSites.rawValue) + .property(property.rawValue) + .value(position)) + } + + public func ntpLibraryItem(_ action: Action, property: Property.Library) { + track(Structured(category: Category.ntp.rawValue, + action: action.rawValue) + .label(Label.NTP.quickActions.rawValue) + .property(property.rawValue)) + } + + public func ntpSeedCounterExperiment(_ action: Action.SeedCounter, value: NSNumber) { + track(Structured(category: Category.ntp.rawValue, + action: action.rawValue) + .label(Label.NTP.climateCounter.rawValue) + .value(value) + ) + } + + // MARK: Product Tour + public func introWelcome(action: Action.Welcome, property: Property.Welcome? = nil) { + let event = Structured(category: Category.intro.rawValue, + action: action.rawValue) + .label(Label.Onboarding.welcome.rawValue) + .property(property?.rawValue) + track(event) + } + + public func firstSearchCardDismiss() { + let event = Structured(category: Category.intro.rawValue, + action: Action.dismiss.rawValue) + .label(Label.Onboarding.firstSearchCard.rawValue) + track(event) + } + + public func firstSearchCardSuggestionClick(pillNumber: Int, languageRegionIdentifier: String) { + let event = Structured(category: Category.intro.rawValue, + action: Action.click.rawValue) + .label(Label.Onboarding.firstSearchCard.rawValue) + .property(languageRegionIdentifier) + .value(NSNumber(value: pillNumber)) + track(event) + } + + public func spotlightTourDisplay(label: Label.Onboarding, step: Int) { + let event = Structured(category: Category.intro.rawValue, + action: Action.display.rawValue) + .label(label.rawValue) + .value(NSNumber(value: step)) + track(event) + } + + public func spotlightTourClick(label: Label.Onboarding, action: Property.SpotlightTour, step: Int) { + let event = Structured(category: Category.intro.rawValue, + action: Action.click.rawValue) + .label(label.rawValue) + .property(action.rawValue) + .value(NSNumber(value: step)) + track(event) + } + + // MARK: Push Notifications Consent + func apnConsent(_ action: Action.APNConsent) { + let event = Structured(category: Category.pushNotificationConsent.rawValue, + action: action.rawValue) + .property(Property.APNConsent.onLaunchPrompt.rawValue) + track(event) + } + + // MARK: Referrals + public func referral(action: Action.Referral, label: Label.Referral? = nil) { + track(Structured(category: Category.invitations.rawValue, + action: action.rawValue) + .label(label?.rawValue)) + } + + // MARK: In-App Search + public func inappSearch(url: URL) { + // Note: This functionality was previously guarded/throttled by the mob_ios_native_srpv_analytics feature flag + // and has been permanently enabled/unthrottled as part of MOB-4040 + guard let query = url.getEcosiaSearchQuery() else { + return + } + let payload: [String: Any?] = [ + "query": query, + "page_num": url.getEcosiaSearchPage(), + "plt_name": "ios", + "plt_v": Bundle.version as NSObject, + "search_type": url.getEcosiaSearchVerticalPath() + ] + track(SelfDescribing(schema: Self.inappSearchSchema, + payload: payload.compactMapValues({ $0 }))) + } + + // MARK: Settings + public func searchbarChanged(to position: String) { + track(Structured(category: Category.settings.rawValue, + action: Action.change.rawValue) + .label(Label.Settings.toolbar.rawValue) + .property(position)) + } + + public func toggleAISearchOverviewsSetting(enabled: Bool) { + track(Structured(category: Category.settings.rawValue, + action: Action.change.rawValue) + .label(Label.Settings.aiOverviews.rawValue) + .property(enabled ? Property.enable.rawValue : Property.disable.rawValue)) + } + + public func sendAnonymousUsageDataSetting(enabled: Bool) { + // This is the only place where the tracker should be directly + // used since we want to send this just as the user opts out + _ = tracker.track(Structured(category: Category.settings.rawValue, + action: Action.change.rawValue) + .label(Label.Settings.analytics.rawValue) + .property(enabled ? Property.enable.rawValue : Property.disable.rawValue)) + } + + public func clearsDataFromSection(_ section: Analytics.Property.SettingsPrivateDataSection) { + track(Structured(category: Category.settings.rawValue, + action: Action.click.rawValue) + .label(Analytics.Label.Settings.clear.rawValue) + .property(section.rawValue)) + } + + // MARK: Feedback + + public func sendFeedback(_ feedback: String, withType feedbackType: FeedbackType) { + let deviceType = UIDevice.current.model + let operatingSystem = "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)" + let idiom = UIDevice.current.userInterfaceIdiom == .pad ? "iPadOS" : "iOS" + let browserVersion = "Ecosia \(idiom) \(Bundle.version)" + + let payload: [String: Any] = [ + "feedback_type": feedbackType.analyticsIdentfier, + "device_type": deviceType, + "os": operatingSystem, + "browser_version": browserVersion, + "feedback_text": feedback + ] + + track(SelfDescribing(schema: Self.feedbackSchema, + payload: payload)) + } + + // MARK: AI Search MVP + + public func aiSearchNTPButtonTapped() { + track(Structured(category: Category.ntp.rawValue, + action: Action.click.rawValue) + .label(Analytics.Label.AISearch.cta.rawValue) + .property(Analytics.Property.header.rawValue)) + } + + public func aiSearchAutocompleteForQuery(_ text: String) { + track(Structured(category: Category.autocomplete.rawValue, + action: Action.click.rawValue) + .label(Analytics.Label.AISearch.cta.rawValue) + .property(text)) + } + + // MARK: Account Authentication + + public func accountHeaderClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.signIn.rawValue) + .property(Property.header.rawValue) + track(event) + } + + public func accountSignInCancelled() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.signIn.rawValue) + .property(Property.cancel.rawValue) + track(event) + } + + public func accountImpactSignUpClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.signUp.rawValue) + .property(Property.menu.rawValue) + track(event) + } + + public func accountImpactCloseClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.close.rawValue) + .property(Property.menu.rawValue) + track(event) + } + + public func accountImpactCardCtaClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.accountNudgeCard.rawValue) + .property(Property.menu.rawValue) + track(event) + } + + public func accountImpactCardDismissClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.dismiss.rawValue) + .label(Label.accountNudgeCard.rawValue) + .property(Property.menu.rawValue) + track(event) + } + + public func accountProfileClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.profile.rawValue) + .property(Property.menu.rawValue) + track(event) + } + + public func accountSignOutClicked() { + let event = Structured(category: Category.account.rawValue, + action: Action.click.rawValue) + .label(Label.profile.rawValue) + .property(Property.signOut.rawValue) + track(event) + } + + public func accountProfileViewed() { + let event = Structured(category: Category.menu.rawValue, + action: Action.view.rawValue) + .label(Label.profile.rawValue) + .property(Property.account.rawValue) + track(event) + } + + public func accountProfileDismissed() { + let event = Structured(category: Category.menu.rawValue, + action: Action.dismiss.rawValue) + .label(Label.profile.rawValue) + .property(Property.account.rawValue) + track(event) + } +} + +extension Analytics { + + /// Appends common context to all structured events + func appendContextIfNeeded(to event: Structured) { + addUserSeedCountContext(to: event) + } + + /// Appends activity-specific context for launch/resume events + func appendActivityContextIfNeeded(_ action: Analytics.Action.Activity, _ event: Structured, completion: @escaping () -> Void) { + switch action { + case .resume, .launch: + addABTestContexts(to: event, toggles: [.brazeIntegration, .onboardingProductTour]) + addCookieConsentContext(to: event) + addUserStateContext(to: event, completion: completion) + } + } + + private func addABTestContexts(to event: Structured, toggles: [Unleash.Toggle.Name]) { + toggles.forEach { toggle in + if let context = Self.getTestContext(from: toggle) { + event.entities.append(context) + } + } + } + + private func addCookieConsentContext(to event: Structured) { + if let consentValue = User.shared.cookieConsentValue { + let consentContext = SelfDescribingJson(schema: Self.consentSchema, + andDictionary: ["cookie_consent": consentValue]) + event.entities.append(consentContext) + } + } + + private func addUserStateContext(to event: Structured, completion: @escaping () -> Void) { + notificationCenter.getNotificationSettingsProtocol { settings in + User.shared.updatePushNotificationUserStateWithAnalytics(from: settings.authorizationStatus) + let userContext = SelfDescribingJson(schema: Self.userSchema, + andDictionary: User.shared.analyticsUserState.dictionary) + event.entities.append(userContext) + completion() + } + } + + private func addUserSeedCountContext(to event: Structured) { + let consentContext = SelfDescribingJson(schema: Self.impactBalanceSchema, + andDictionary: ["amount": User.shared.seedCount]) + event.entities.append(consentContext) + } +} + +extension Analytics { + + /// Creates and configures a new instance of `TrackerController` using Snowplow. + /// + /// - Returns: A configured `TrackerController` instance, which in non-release builds can either point to mini or micro Snowplow instance. + private static func makeTracker() -> TrackerController { + return Snowplow.createTracker(namespace: namespace, + network: makeNetworkConfig(), + configurations: [ + Self.trackerConfiguration, + Self.subjectConfiguration, + Self.appInstallTrackingPluginConfiguration, + Self.appResumeDailyTrackingPluginConfiguration]) + } + + /// Factory that builds the `NetworkConfiguration` for the Snowplow tracker, optionally + /// including authentication headers when the environment provides Cloudflare credentials. + /// + /// - Parameters: + /// - environment: The environment to use for resolving the endpoint and authentication. + /// Defaults to `EcosiaEnvironment.current`. Pass a specific value in tests. + /// - Returns: A configured `NetworkConfiguration` object. + static func makeNetworkConfig(environment: EcosiaEnvironment = .current) -> NetworkConfiguration { + let urlProvider = environment.urlProvider + let endpoint = shouldUseMicroInstance ? urlProvider.snowplowMicro : urlProvider.snowplow + var networkConfig = NetworkConfiguration(endpoint: endpoint!) + + if let auth = environment.cloudFlareAuth { + networkConfig = networkConfig + .requestHeaders([ + CloudflareKeyProvider.clientId: auth.id, + CloudflareKeyProvider.clientSecret: auth.secret + ]) + } + + return networkConfig + } +} diff --git a/firefox-ios/Ecosia/Analytics/AnalyticsNotificationSettings.swift b/firefox-ios/Ecosia/Analytics/AnalyticsNotificationSettings.swift new file mode 100644 index 0000000000000..44169d2739453 --- /dev/null +++ b/firefox-ios/Ecosia/Analytics/AnalyticsNotificationSettings.swift @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UserNotifications + +/// A protocol that abstracts the `UNUserNotificationCenter` to allow for dependency injection and mocking. +/// Utilized for integration tests. Specifically `AnalyticsSpyTests` +/// +/// By conforming to this protocol, you can replace the notification center with a mock implementation during testing. +protocol AnalyticsUserNotificationCenterProtocol { + /// Retrieves the notification settings asynchronously. + /// + /// - Parameter completionHandler: A closure that is executed with the retrieved `AnalyticsUNNotificationSettingsProtocol` object. + func getNotificationSettingsProtocol(completionHandler: @escaping (AnalyticsUNNotificationSettingsProtocol) -> Void) +} + +/// A protocol that abstracts `UNNotificationSettings`, focusing on the authorization status. +/// +/// This protocol allows for mocking the notification settings during testing by providing a custom implementation. +protocol AnalyticsUNNotificationSettingsProtocol { + /// The authorization status that indicates whether the app is authorized to schedule or receive notifications. + var authorizationStatus: UNAuthorizationStatus { get } +} + +/// Extends `UNNotificationSettings` to conform to `AnalyticsUNNotificationSettingsProtocol`. +/// +/// This allows `UNNotificationSettings` instances to be used wherever `AnalyticsUNNotificationSettingsProtocol` is expected. +extension UNNotificationSettings: AnalyticsUNNotificationSettingsProtocol {} + +/// A wrapper class for `UNUserNotificationCenter` that conforms to `AnalyticsUserNotificationCenterProtocol`. +/// +/// This class provides a concrete implementation of the protocol by forwarding calls to an instance of `UNUserNotificationCenter`. +class AnalyticsUserNotificationCenterWrapper: AnalyticsUserNotificationCenterProtocol { + /// The underlying `UNUserNotificationCenter` instance. + private let center: UNUserNotificationCenter + + /// Initializes the wrapper with a specific `UNUserNotificationCenter` instance. + /// + /// - Parameter center: The `UNUserNotificationCenter` instance to wrap. Defaults to the current notification center. + init(center: UNUserNotificationCenter = .current()) { + self.center = center + } + + /// Retrieves the notification settings asynchronously. + /// + /// - Parameter completionHandler: A closure that is executed with the retrieved `AnalyticsUNNotificationSettingsProtocol` object. + func getNotificationSettingsProtocol(completionHandler: @escaping (AnalyticsUNNotificationSettingsProtocol) -> Void) { + center.getNotificationSettings { settings in + completionHandler(settings) + } + } +} diff --git a/firefox-ios/Ecosia/Braze/APNConsent.swift b/firefox-ios/Ecosia/Braze/APNConsent.swift new file mode 100644 index 0000000000000..64e596d18480d --- /dev/null +++ b/firefox-ios/Ecosia/Braze/APNConsent.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct APNConsent { + private init() {} + + public static func requestIfNeeded() async { + // Do not ask for push consent during Onboarding experiment + guard !OnboardingProductTourExperiment.isEnabled else { + return + } + guard BrazeService.shared.notificationAuthorizationStatus == .notDetermined else { + return + } + Analytics.shared.apnConsent(.view) + do { + let granted = try await BrazeService.shared.requestAPNConsent() + Analytics.shared.apnConsent(granted ? .allow : .deny) + } catch { + Analytics.shared.apnConsent(.error) + } + } +} diff --git a/firefox-ios/Ecosia/Braze/BrazeService.swift b/firefox-ios/Ecosia/Braze/BrazeService.swift new file mode 100644 index 0000000000000..6defa89f7d5de --- /dev/null +++ b/firefox-ios/Ecosia/Braze/BrazeService.swift @@ -0,0 +1,223 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UIKit +import UserNotifications +import BrazeKit +import BrazeUI + +public protocol BrazeBrowserDelegate: AnyObject { + func openBrazeURLInNewTab(_ url: URL?) +} + +public final class BrazeService: NSObject { + override private init() {} + + private var braze: Braze? + private var userId: String { + User.shared.analyticsId.uuidString + } + private(set) var notificationAuthorizationStatus: UNAuthorizationStatus? + private static var apiKey = EnvironmentFetcher.valueFromMainBundleOrProcessInfo(forKey: "BRAZE_API_KEY") ?? "" + public static let shared = BrazeService() + public weak var browserDelegate: BrazeBrowserDelegate? + + enum Error: Swift.Error { + case invalidConfiguration + case generic(description: String) + } + + public enum CustomEvent: String { + case empty = "" + } + + public func initialize() { + guard BrazeIntegrationExperiment.isEnabled else { return } + + do { + try initBraze(userId: userId) + Task { + await refreshAPNRegistrationIfNeeded() + await APNConsent.requestIfNeeded() + } + } catch { + debugPrint(error) + } + } + + public func registerDeviceToken(_ deviceToken: Data) { + braze?.notifications.register(deviceToken: deviceToken) + updateID(userId) + } + + public func logCustomEvent(_ event: CustomEvent) { + self.braze?.logCustomEvent(name: event.rawValue) + } + + // MARK: - APN Consent + + func requestAPNConsent() async throws -> Bool { + await UIApplication.shared.registerForRemoteNotifications() + let notificationCenter = makeNotificationCenter() + let granted = try await notificationCenter.requestAuthorization(options: [.badge, .sound, .alert]) + await retrieveUserCurrentNotificationAuthStatus() // Make sure status is always updated + return granted + } + + func refreshAPNRegistrationIfNeeded() async { + await retrieveUserCurrentNotificationAuthStatus() + switch notificationAuthorizationStatus { + case .authorized, .ephemeral, .provisional: + _ = try? await requestAPNConsent() + default: + break + } + } +} + +extension BrazeService { + // MARK: - Init Braze + + private func initBraze(userId: String) throws { + braze = Braze(configuration: try getBrazeConfiguration()) + braze?.delegate = self + Task { @MainActor in + let inAppMessageUI = BrazeInAppMessageUI() + inAppMessageUI.delegate = self + braze?.inAppMessagePresenter = inAppMessageUI + } + updateID(userId) + } +} + +extension BrazeService { + // MARK: - Braze proxy function + + public static func prepareForDelayedInitialization() { + Braze.prepareForDelayedInitialization() + } +} + +extension BrazeService { + // MARK: - Notification Center + + private func makeNotificationCenter() -> UNUserNotificationCenter { + let center = UNUserNotificationCenter.current() + center.setNotificationCategories(BrazeKit.Braze.Notifications.categories) + center.delegate = self + return center + } + + private func retrieveUserCurrentNotificationAuthStatus() async { + let notificationCenter = UNUserNotificationCenter.current() + let currentStatus = await notificationCenter.notificationSettings().authorizationStatus + notificationAuthorizationStatus = currentStatus + } +} + +extension BrazeService { + // MARK: - ID Update + + private func updateID(_ id: String?) { + guard let id else { return } + #if MOZ_CHANNEL_FENNEC + print("📣🆔 Braze Identifier Updating To: \(id)") + #endif + let brazeID = braze?.user.id + guard id != brazeID else { return } + braze?.changeUser(userId: id) + } +} + +extension BrazeService { + // MARK: - Environment Configuration + + /// Retrieves the Braze configuration based on the provided parameters. + /// + /// - Parameters: + /// - apiKey: The Braze API key to be used for configuration. + /// - environment: The target environment for which the Braze configuration is requested. + /// Defaults to the current environment. + /// - Returns: A Braze configuration if the required parameters are present. + /// - Throws: An `Error.invalidConfiguration` if the API key is empty. + /// + /// - Note: The `environment` parameter allows customization of the target environment. + /// If not provided, it defaults to the current environment. + /// + /// - Warning: Ensure that the provided API key is not empty to avoid invalid configurations. + func getBrazeConfiguration(apiKey: String = BrazeService.apiKey, + environment: Environment = EcosiaEnvironment.current) throws -> BrazeKit.Braze.Configuration { + guard !apiKey.isEmpty else { throw Error.invalidConfiguration } + + let brazeConfiguration = BrazeKit.Braze.Configuration(apiKey: apiKey, endpoint: environment.urlProvider.brazeEndpoint) + #if MOZ_CHANNEL_FENNEC + brazeConfiguration.logger.level = .debug + #endif + return brazeConfiguration + } +} + +extension BrazeService: BrazeInAppMessageUIDelegate { + + public func inAppMessage(_ ui: BrazeInAppMessageUI, didPresent message: Braze.InAppMessage, view: any InAppMessageView) { + Analytics.shared.brazeIAM(action: .view, messageOrButtonId: message.id) + } + + public func inAppMessage(_ ui: BrazeInAppMessageUI, didDismiss message: Braze.InAppMessage, view: any InAppMessageView) { + Analytics.shared.brazeIAM(action: .dismiss, messageOrButtonId: message.id) + } + + public func inAppMessage(_ ui: BrazeInAppMessageUI, shouldProcess clickAction: Braze.InAppMessage.ClickAction, buttonId: String?, message: Braze.InAppMessage, view: any InAppMessageView) -> Bool { + Analytics.shared.brazeIAM(action: .click, messageOrButtonId: buttonId) + return true + } +} + +extension BrazeService: BrazeDelegate { + public func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool { + let isInternal: Bool = { + let contextUrl = context.url + let rootUrl = EcosiaEnvironment.current.urlProvider.root + if #available(iOS 16.0, *) { + return contextUrl.host() == rootUrl.host() + } else { + return contextUrl.host == rootUrl.host + } + }() + + // Braze should handle external URLs (or as a fallback with no delegate) + guard isInternal, let browserDelegate = self.browserDelegate else { return true } + + browserDelegate.openBrazeURLInNewTab(context.url) + return false + } +} + +extension BrazeService: UNUserNotificationCenterDelegate { + public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + if let braze, braze.notifications.handleUserNotification( + response: response, + withCompletionHandler: completionHandler + ) { + return + } + completionHandler() + } + + public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + if let braze { + braze.notifications.handleForegroundNotification(notification: notification) + } + completionHandler([.list, .banner, .sound, .badge]) + } +} + +// Exposing Braze logic to be used inside `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` +extension BrazeService { + public func handleBackgroundNotification(userInfo: [AnyHashable: Any], completionHandler: @escaping (UIBackgroundFetchResult) -> Void) -> Bool { + guard let braze else { return false } + return braze.notifications.handleBackgroundNotification(userInfo: userInfo, fetchCompletionHandler: completionHandler) + } +} diff --git a/firefox-ios/Ecosia/Core/Accounts/AccountsProvider.swift b/firefox-ios/Ecosia/Core/Accounts/AccountsProvider.swift new file mode 100644 index 0000000000000..5402fdd1f0f4f --- /dev/null +++ b/firefox-ios/Ecosia/Core/Accounts/AccountsProvider.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol AccountsProviderProtocol { + func registerVisit(accessToken: String) async throws -> AccountVisitResponse +} + +public struct AccountsProvider: AccountsProviderProtocol { + + public let accountsService: AccountsServiceProtocol + + public init(accountsService: AccountsServiceProtocol = AccountsService()) { + self.accountsService = accountsService + } + + /// Registers a user visit and returns current balance. + /// This endpoint automatically handles visit-based use cases and returns the current balance, + /// including indicating if it has changed as a result of this call. + /// - Parameter accessToken: Valid Auth0-issued JWT access token + public func registerVisit(accessToken: String) async throws -> AccountVisitResponse { + return try await accountsService.registerVisit(accessToken: accessToken) + } +} diff --git a/firefox-ios/Ecosia/Core/Accounts/Service/AccountBalanceResponse.swift b/firefox-ios/Ecosia/Core/Accounts/Service/AccountBalanceResponse.swift new file mode 100644 index 0000000000000..878bee1759dbe --- /dev/null +++ b/firefox-ios/Ecosia/Core/Accounts/Service/AccountBalanceResponse.swift @@ -0,0 +1,40 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct AccountBalanceResponse: Codable { + public let balance: Balance + public let previousBalance: PreviousBalance? + + public init(balance: Balance, previousBalance: PreviousBalance?) { + self.balance = balance + self.previousBalance = previousBalance + } + + public struct Balance: Codable { + public let amount: Int + public let isModified: Bool + let updatedAt: String + + public init(amount: Int, updatedAt: String, isModified: Bool) { + self.amount = amount + self.updatedAt = updatedAt + self.isModified = isModified + } + } + + public struct PreviousBalance: Codable { + public let amount: Int + + public init(amount: Int) { + self.amount = amount + } + } + + public var balanceIncrement: Int? { + guard let previousBalance, balance.isModified else { return nil } + return balance.amount - previousBalance.amount + } +} diff --git a/firefox-ios/Ecosia/Core/Accounts/Service/AccountVisitRequest.swift b/firefox-ios/Ecosia/Core/Accounts/Service/AccountVisitRequest.swift new file mode 100644 index 0000000000000..3795f7813c016 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Accounts/Service/AccountVisitRequest.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct AccountVisitRequest: BaseRequest { + + var method: HTTPMethod { + .post + } + + var path: String { + "/api/v2/accounts/impact/visits" + } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? + + var baseURL: URL { + environment.urlProvider.root + } + + init(accessToken: String) { + self.additionalHeaders = [ + "Authorization": "Bearer \(accessToken)", + "E-Device-Type-Id": "2" // iOS Device Type ID + ] + } +} diff --git a/firefox-ios/Ecosia/Core/Accounts/Service/AccountVisitResponse.swift b/firefox-ios/Ecosia/Core/Accounts/Service/AccountVisitResponse.swift new file mode 100644 index 0000000000000..ed2cbd095993d --- /dev/null +++ b/firefox-ios/Ecosia/Core/Accounts/Service/AccountVisitResponse.swift @@ -0,0 +1,98 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct AccountVisitResponse: Codable { + public let seeds: Seeds + public let growthPoints: GrowthPoints + + public init(seeds: Seeds, growthPoints: GrowthPoints) { + self.seeds = seeds + self.growthPoints = growthPoints + } + + public struct Seeds: Codable { + public let balanceAmount: Int + public let totalAmount: Int + public let previousTotalAmount: Int + public let isModified: Bool + public let lastVisitAt: String + public let updatedAt: String + + public init(balanceAmount: Int, totalAmount: Int, previousTotalAmount: Int, isModified: Bool, lastVisitAt: String, updatedAt: String) { + self.balanceAmount = balanceAmount + self.totalAmount = totalAmount + self.previousTotalAmount = previousTotalAmount + self.isModified = isModified + self.lastVisitAt = lastVisitAt + self.updatedAt = updatedAt + } + } + + public struct GrowthPoints: Codable { + public let balanceAmount: Int + public let totalAmount: Int + public let previousTotalAmount: Int + public let level: Level + public let previousLevel: Level + public let isModified: Bool + public let lastVisitAt: String + public let updatedAt: String + + public init(balanceAmount: Int, totalAmount: Int, previousTotalAmount: Int, level: Level, previousLevel: Level, isModified: Bool, lastVisitAt: String, updatedAt: String) { + self.balanceAmount = balanceAmount + self.totalAmount = totalAmount + self.previousTotalAmount = previousTotalAmount + self.level = level + self.previousLevel = previousLevel + self.isModified = isModified + self.lastVisitAt = lastVisitAt + self.updatedAt = updatedAt + } + } + + public struct Level: Codable { + public let number: Int + public let totalGrowthPointsRequired: Int + public let seedsRewardedForLevelUp: Int + public let growthPointsToUnlockNextLevel: Int + public let growthPointsEarnedTowardsNextLevel: Int + + public init(number: Int, totalGrowthPointsRequired: Int, seedsRewardedForLevelUp: Int, growthPointsToUnlockNextLevel: Int, growthPointsEarnedTowardsNextLevel: Int) { + self.number = number + self.totalGrowthPointsRequired = totalGrowthPointsRequired + self.seedsRewardedForLevelUp = seedsRewardedForLevelUp + self.growthPointsToUnlockNextLevel = growthPointsToUnlockNextLevel + self.growthPointsEarnedTowardsNextLevel = growthPointsEarnedTowardsNextLevel + } + } + + // MARK: - Convenience Properties + + /// Seeds increment, returns value only if seeds were modified + public var seedsIncrement: Int? { + guard seeds.isModified else { return nil } + return seeds.totalAmount - seeds.previousTotalAmount + } + + /// Growth points increment, returns value only if growth points were modified + public var growthPointsIncrement: Int? { + guard growthPoints.isModified else { return nil } + return growthPoints.totalAmount - growthPoints.previousTotalAmount + } + + /// Returns true if user leveled up + public var didLevelUp: Bool { + return growthPoints.level.number > growthPoints.previousLevel.number + } + + /// Progress towards next level (0.0 to 1.0) + public var progressToNextLevel: Double { + let earned = growthPoints.level.growthPointsEarnedTowardsNextLevel + let required = growthPoints.level.growthPointsToUnlockNextLevel + guard required > 0 else { return 0.0 } + return Double(earned) / Double(required) + } +} diff --git a/firefox-ios/Ecosia/Core/Accounts/Service/AccountsService.swift b/firefox-ios/Ecosia/Core/Accounts/Service/AccountsService.swift new file mode 100644 index 0000000000000..711372744693a --- /dev/null +++ b/firefox-ios/Ecosia/Core/Accounts/Service/AccountsService.swift @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol AccountsServiceProtocol { + func registerVisit(accessToken: String) async throws -> AccountVisitResponse +} + +public final class AccountsService: AccountsServiceProtocol { + + enum Error: Swift.Error { + case network + case invalidResponse + case decodingError(String) + case authenticationRequired + case unauthorized + } + + private let client: HTTPClient + + public init(client: HTTPClient = URLSessionHTTPClient()) { + self.client = client + } + + public func registerVisit(accessToken: String) async throws -> AccountVisitResponse { + let request = AccountVisitRequest(accessToken: accessToken) + + EcosiaLogger.network.info("Making accounts visit request to: \(request.baseURL.absoluteString)\(request.path)") + + let (data, response) = try await client.perform(request) + + guard let response else { + EcosiaLogger.network.error("Accounts visit request failed: No response received") + throw Error.network + } + + EcosiaLogger.network.info("Accounts visit response: status=\(response.statusCode), dataSize=\(data.count) bytes") + + switch response.statusCode { + case 200: + break + case 401: + EcosiaLogger.network.error("Accounts visit request unauthorized (401): Invalid or expired access token") + throw Error.unauthorized + case 403: + EcosiaLogger.network.error("Accounts visit request forbidden (403): Valid token but insufficient permissions - check scopes") + throw Error.unauthorized // We can treat 403 same as 401 for now + default: + EcosiaLogger.network.error("Accounts visit request failed with status: \(response.statusCode)") + throw Error.network + } + + do { + let decodedResponse = try JSONDecoder().decode(AccountVisitResponse.self, from: data) + EcosiaLogger.network.info("Accounts visit successful: seeds=\(decodedResponse.seeds.balanceAmount), seedsModified=\(decodedResponse.seeds.isModified), level=\(decodedResponse.growthPoints.level.number), levelUp=\(decodedResponse.didLevelUp)") + return decodedResponse + } catch { + EcosiaLogger.network.error("Accounts visit response decoding failed: \(error.localizedDescription)") + if let responseString = String(data: data, encoding: .utf8) { + EcosiaLogger.network.debug("Raw response data: \(responseString)") + } + throw Error.decodingError(error.localizedDescription) + } + } +} diff --git a/firefox-ios/Ecosia/Core/AdultFilter.swift b/firefox-ios/Ecosia/Core/AdultFilter.swift new file mode 100644 index 0000000000000..b9ce81a41e752 --- /dev/null +++ b/firefox-ios/Ecosia/Core/AdultFilter.swift @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum AdultFilter: String, Codable { + case + off = "n", + moderate = "i", + strict = "y" +} diff --git a/firefox-ios/Ecosia/Core/AppDeviceInfo.swift b/firefox-ios/Ecosia/Core/AppDeviceInfo.swift new file mode 100644 index 0000000000000..0a0890530ffe3 --- /dev/null +++ b/firefox-ios/Ecosia/Core/AppDeviceInfo.swift @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct AppDeviceInfo: Equatable { + + public let platform: String + public let bundleId: String + public let osVersion: String + public let deviceManufacturer: String + public let deviceModel: String + public let locale: String + public let country: String? + public let deviceBuildVersion: String? + public let appVersion: String + public let installReceipt: String? + public let adServicesAttributionToken: String? + + public init(platform: String, + bundleId: String, + osVersion: String, + deviceManufacturer: String, + deviceModel: String, + locale: String, + country: String? = nil, + deviceBuildVersion: String? = nil, + appVersion: String, + installReceipt: String? = nil, + adServicesAttributionToken: String? = nil) { + self.platform = platform + self.bundleId = bundleId + self.osVersion = osVersion + self.deviceManufacturer = deviceManufacturer + self.deviceModel = deviceModel + self.locale = locale + self.country = country + self.deviceBuildVersion = deviceBuildVersion + self.appVersion = appVersion + self.installReceipt = installReceipt + self.adServicesAttributionToken = adServicesAttributionToken + } +} diff --git a/firefox-ios/Ecosia/Core/Bookmarks/Bookmark.swift b/firefox-ios/Ecosia/Core/Bookmarks/Bookmark.swift new file mode 100644 index 0000000000000..182f91a44b536 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Bookmarks/Bookmark.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public typealias Title = String +public typealias Url = String + +public struct BookmarkMetadata: Equatable { + let addedAt: Date? + let modifiedAt: Date? + + public static var empty: BookmarkMetadata { + BookmarkMetadata(addedAt: nil, modifiedAt: nil) + } + + internal var stringValue: String { + var returnValue = "" + if let addedAt = addedAt { + returnValue += " \(String.addDate)=\"\(Int(addedAt.timeIntervalSince1970))\"" + } + if let modifiedAt = modifiedAt { + returnValue += " \(String.lastModified)=\"\(Int(modifiedAt.timeIntervalSince1970))\"" + } + return returnValue + } +} + +public enum BookmarkItem: Equatable { + case folder(Title, [BookmarkItem], BookmarkMetadata) + case bookmark(Title, Url, BookmarkMetadata) +} diff --git a/firefox-ios/Ecosia/Core/Bookmarks/BookmarkParser.swift b/firefox-ios/Ecosia/Core/Bookmarks/BookmarkParser.swift new file mode 100644 index 0000000000000..67b4a5a550a36 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Bookmarks/BookmarkParser.swift @@ -0,0 +1,98 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import SwiftSoup + +private typealias DateTag = String + +public enum BookmarkParserError: Error { + case noLeadingDL, noBody, cancelled +} + +public protocol BookmarkParseable { + func parseBookmarks() async throws -> [BookmarkItem] +} + +public class BookmarkParser: BookmarkParseable { + private let document: Document + private let dispatchQueue: DispatchQueue + + public init(html: String, dispatchQueue: DispatchQueue = .init(label: "org.ecosia.ios-core.bookmarks")) throws { + let document = try SwiftSoup.parse(html) + self.document = try document.normalizedDocumentIfRequired() + self.dispatchQueue = dispatchQueue + } + + public func parseBookmarks() async throws -> [BookmarkItem] { + try await withCheckedThrowingContinuation { continuation in + dispatchQueue.async { [weak self] in + guard let self = self else { + return continuation.resume(throwing: BookmarkParserError.cancelled) + } + do { + let result = try self.parse(element: try self.document.getLeadingDL()) + continuation.resume(with: .success(result)) + } catch { + continuation.resume(throwing: error) + } + } + } + } +} + +private extension BookmarkParser { + func parse(element: Element) throws -> [BookmarkItem] { + var items = [BookmarkItem]() + + let children = try element.getLeadingDL() + .children() + .filter({ try $0.select(.dt).hasText() }) /// only
is a valid bookmark/folder element + + for child in children { + let h3 = try child.select(.h3) + if let nextFolderItem = h3.first() { + guard let title = try? nextFolderItem.text() else { continue } + items.append(.folder(title, try parse(element: child), h3.extractBookmarkMetadata())) + continue /// item is a folder, don't process as bookmark + } + + let link = try child.select(.a) + let href = try link.attr(.href) + let title = try link.text() + + items.append(.bookmark(title, href, link.extractBookmarkMetadata())) + } + + return items + } +} + +private extension Elements { + func extractDate(_ tag: DateTag) -> Date? { + guard + let timeIntervalString = try? attr(tag), + let timeInterval = TimeInterval(timeIntervalString) + else { + return nil + } + return Date(timeIntervalSince1970: timeInterval) + } + + func extractBookmarkMetadata() -> BookmarkMetadata { + BookmarkMetadata( + addedAt: extractDate(.addDate), + modifiedAt: extractDate(.lastModified) + ) + } +} + +private extension Element { + func getLeadingDL() throws -> Element { + guard let leadingDL = try select(.dl).first() else { + throw BookmarkParserError.noLeadingDL + } + return leadingDL + } +} diff --git a/firefox-ios/Ecosia/Core/Bookmarks/BookmarkSerializer.swift b/firefox-ios/Ecosia/Core/Bookmarks/BookmarkSerializer.swift new file mode 100644 index 0000000000000..06892f5f114fd --- /dev/null +++ b/firefox-ios/Ecosia/Core/Bookmarks/BookmarkSerializer.swift @@ -0,0 +1,85 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import SwiftSoup + +public enum BookmarkSerializerError: Error { + case cancelled +} + +public protocol BookmarkSerializable { + func serializeBookmarks(_ bookmarks: [BookmarkItem]) async throws -> String +} + +public class BookmarkSerializer: BookmarkSerializable { + private let dispatchQueue: DispatchQueue + + public init(dispatchQueue: DispatchQueue = .init(label: "org.ecosia.ios-core.bookmarks")) { + self.dispatchQueue = dispatchQueue + } + + public func serializeBookmarks(_ bookmarks: [BookmarkItem]) async throws -> String { + try await withCheckedThrowingContinuation { continuation in + dispatchQueue.async { [weak self] in + guard let self = self else { + return continuation.resume(throwing: BookmarkSerializerError.cancelled) + } + /// The trailing open

tag is part of the Netscape Bookmark file syntax + var html = """ + + + Bookmarks +

Bookmarks

+

+ + """ + + for bookmark in bookmarks { + html += self.bookmarkBody(for: bookmark, indentation: 2) + } + + html += """ + + \(String.indent(by: 1))

+ + """ + + continuation.resume(returning: html) + } + } + } + + func bookmarkBody(for bookmark: BookmarkItem, indentation: Int) -> String { + switch bookmark { + case let .bookmark(title, url, metadata): + return """ + \(String.indent(by: indentation))

\(Entities.escape(title)) + """ + case let .folder(title, children, metadata): + let start = """ + \(String.indent(by: indentation))
\(Entities.escape(title)) + \(String.indent(by: indentation))

+ + """ + + let body = children.map { + bookmarkBody(for: $0, indentation: indentation + 1) + }.joined(separator: "\n") + + let end = """ + + \(String.indent(by: indentation))

+ """ + + return start + body + end + } + } +} + +private extension String { + static func indent(by level: Int) -> String { + return String(repeating: "\t", count: level) + } +} diff --git a/firefox-ios/Ecosia/Core/Bookmarks/Document+Safari.swift b/firefox-ios/Ecosia/Core/Bookmarks/Document+Safari.swift new file mode 100644 index 0000000000000..0ada78a429edc --- /dev/null +++ b/firefox-ios/Ecosia/Core/Bookmarks/Document+Safari.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftSoup + +extension Document { + var isSafariFormat: Bool { + if let leadingDL = try? select(.dl).first(), leadingDL.parents().size() > 2 { + return true + } + return false + } + + func normalizedDocumentIfRequired() throws -> Document { + isSafariFormat ? try normalizedSafariExport() : self + } +} + +private extension Document { + func normalizedSafariExport() throws -> Document { + guard let body = try select("body").first() else { + throw BookmarkParserError.noBody + } + + let newDocument = Document("") + let dlElement = try newDocument.appendElement("DL") + let bodyChildren = body.getChildNodes() + try dlElement.insertChildren(0, bodyChildren) + + return newDocument + } +} diff --git a/firefox-ios/Ecosia/Core/Bookmarks/String+CssQuery.swift b/firefox-ios/Ecosia/Core/Bookmarks/String+CssQuery.swift new file mode 100644 index 0000000000000..135c99f6c2e79 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Bookmarks/String+CssQuery.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension String { + static let dt = "DT" + static let dl = "DL" + static let h3 = "H3" + static let a = "A" + static let href = "href" + static let addDate = "ADD_DATE" + static let lastModified = "LAST_MODIFIED" +} diff --git a/firefox-ios/Ecosia/Core/Bundle.swift b/firefox-ios/Ecosia/Core/Bundle.swift new file mode 100644 index 0000000000000..f058704429f4d --- /dev/null +++ b/firefox-ios/Ecosia/Core/Bundle.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Bundle { + public static let version = marketing + "." + bundle + private static let marketing = main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "4.0.20" + private static let bundle = main.infoDictionary?["CFBundleVersion"] as? String ?? "840" +} diff --git a/firefox-ios/Ecosia/Core/CloudFlareKeyProvider.swift b/firefox-ios/Ecosia/Core/CloudFlareKeyProvider.swift new file mode 100644 index 0000000000000..7bd2af2492f36 --- /dev/null +++ b/firefox-ios/Ecosia/Core/CloudFlareKeyProvider.swift @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct CloudflareKeyProvider { + static let clientId = "CF-Access-Client-Id" + static let clientSecret = "CF-Access-Client-Secret" + + private init() {} +} diff --git a/firefox-ios/Ecosia/Core/Cookie/AIOverviewsCookieHandler.swift b/firefox-ios/Ecosia/Core/Cookie/AIOverviewsCookieHandler.swift new file mode 100644 index 0000000000000..255cead6398b1 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/AIOverviewsCookieHandler.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +final class AIOverviewsCookieHandler: BaseCookieHandler { + + init() { + super.init(cookieName: Cookie.aiOverviews.rawValue) + } + + override func getCookieValue() -> String? { + return User.shared.aiOverviews.description + } + + override func received(_ cookie: HTTPCookie, in cookieStore: CookieStoreProtocol) { + User.shared.aiOverviews = Bool(cookie.value) ?? false + } +} diff --git a/firefox-ios/Ecosia/Core/Cookie/AuthSessionCookieHandler.swift b/firefox-ios/Ecosia/Core/Cookie/AuthSessionCookieHandler.swift new file mode 100644 index 0000000000000..fb1d1b8b134c4 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/AuthSessionCookieHandler.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +final class AuthSessionCookieHandler: BaseCookieHandler { + + init() { + super.init(cookieName: Cookie.authSession.rawValue) + } +} diff --git a/firefox-ios/Ecosia/Core/Cookie/ConsentCookieHandler.swift b/firefox-ios/Ecosia/Core/Cookie/ConsentCookieHandler.swift new file mode 100644 index 0000000000000..7128fb222ef37 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/ConsentCookieHandler.swift @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import WebKit + +final class ConsentCookieHandler: BaseCookieHandler { + + init() { + super.init(cookieName: Cookie.consent.rawValue) + } + + override func getCookieValue() -> String? { + return User.shared.cookieConsentValue + } + + override func received(_ cookie: HTTPCookie, in cookieStore: CookieStoreProtocol) { + User.shared.cookieConsentValue = cookie.value + } +} diff --git a/firefox-ios/Ecosia/Core/Cookie/Cookie.swift b/firefox-ios/Ecosia/Core/Cookie/Cookie.swift new file mode 100644 index 0000000000000..06823dd39d035 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/Cookie.swift @@ -0,0 +1,148 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import WebKit + +// MARK: - Cookie Types + +public enum Cookie: String, CaseIterable { + // https://ecosia.atlassian.net/wiki/spaces/DEV/pages/4128796/Cookies#ECFG + case main = "ECFG" + // https://ecosia.atlassian.net/wiki/spaces/DEV/pages/4128796/Cookies#ECCC + case consent = "ECCC" + // https://ecosia.atlassian.net/wiki/spaces/DEV/pages/4128796/Cookies#ECUNL + case unleash = "ECUNL" + // https://ecosia.atlassian.net/wiki/spaces/DEV/pages/4128796/Cookies#ECAIO + case aiOverviews = "ECAIO" + // https://ecosia.atlassian.net/wiki/spaces/DEV/pages/4128796/Cookies#EASC + case authSession = "EASC" + + // MARK: - URLProvider Management + + private static var _urlProvider: URLProvider? + + /// Sets the URLProvider for all cookie operations. Primarily used for testing. + /// If not set, defaults to EcosiaEnvironment.current.urlProvider + public static func setURLProvider(_ provider: URLProvider) { + _urlProvider = provider + } + + /// Resets the URLProvider to use the default EcosiaEnvironment.current.urlProvider + public static func resetURLProvider() { + _urlProvider = nil + } + + /// Gets the current URLProvider, defaulting to EcosiaEnvironment.current.urlProvider if not explicitly set + static var urlProvider: URLProvider { + return _urlProvider ?? EcosiaEnvironment.current.urlProvider + } + + // MARK: - Init + /// Initialized enum using HTTPCookie object's name. Also checks domain matches Ecosia. + /// - Parameters: + /// - cookie: HTTPCookie + init?(_ cookie: HTTPCookie) { + let ecosiaDomain = Cookie.urlProvider.domain + guard cookie.domain == ".\(ecosiaDomain)" else { + return nil + } + self.init(cookie.name) + } + + init?(_ name: String) { + self.init(rawValue: name) + } + + var name: String { + rawValue + } + + // MARK: - Cookie Handler Factory + + private var handler: CookieHandler { + switch self { + case .main: + return MainCookieHandler() + case .consent: + return ConsentCookieHandler() + case .unleash: + return UnleashCookieHandler() + case .aiOverviews: + return AIOverviewsCookieHandler() + case .authSession: + return AuthSessionCookieHandler() + } + } + + /// Creates a Main Cookie for the specified mode. + /// - Parameters: + /// - mode: The cookie mode (standard or incognito) + /// - Returns: An HTTPCookie configured for the specified mode. + private static func makeMain(withMode mode: CookieMode) -> HTTPCookie? { + return MainCookieHandler(mode: mode).makeCookie() + } + + // MARK: - Public Interface + + /// Processes received cookies. + /// - Parameters: + /// - cookies: An array of HTTPCookie objects. + /// - cookieStore: WKHTTPCookieStore where cookies are set. Used when cookies need to be overwritten. + public static func received(_ cookies: [HTTPCookie], in cookieStore: CookieStoreProtocol) { + cookies.forEach { cookie in + guard let cookieType = Cookie(cookie) else { return } + cookieType.handler.received(cookie, in: cookieStore) + } + } + + /// Creates cookies for all required types for web view configuration. + /// - Parameters: + /// - isPrivate: Whether to create cookies for private browsing mode + /// - Returns: An array of HTTPCookie for all required types + public static func makeRequiredCookies(isPrivate: Bool) -> [HTTPCookie] { + var cookies: [HTTPCookie] = [] + + Cookie.allCases.forEach { cookieType in + switch cookieType { + case .main: + let mainMode: CookieMode = isPrivate ? .incognito : .standard + if let cookie = makeMain(withMode: mainMode) { + cookies.append(cookie) + } + default: + if let cookie = cookieType.handler.makeCookie() { + cookies.append(cookie) + } + } + } + + return cookies + } + + /// Creates cookies related to search settings for `searchSettingsChanged` notification. + /// - Parameter isPrivate: Whether to create cookies for private browsing mode + /// - Returns: An array of HTTPCookie for all search setting specific types + public static func makeSearchSettingsObserverCookies(isPrivate: Bool) -> [HTTPCookie] { + var cookies: [HTTPCookie] = [] + + Cookie.allCases.forEach { cookieType in + switch cookieType { + case .main: + let mainMode: CookieMode = isPrivate ? .incognito : .standard + if let cookie = makeMain(withMode: mainMode) { + cookies.append(cookie) + } + case .aiOverviews: + if let cookie = cookieType.handler.makeCookie() { + cookies.append(cookie) + } + default: + break + } + } + + return cookies + } +} diff --git a/firefox-ios/Ecosia/Core/Cookie/CookieHandler.swift b/firefox-ios/Ecosia/Core/Cookie/CookieHandler.swift new file mode 100644 index 0000000000000..61817e816cd0c --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/CookieHandler.swift @@ -0,0 +1,49 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import WebKit + +// MARK: - Cookie Handler Protocol + +protocol CookieHandler { + var cookieName: String { get } + func makeCookie() -> HTTPCookie? + func received(_ cookie: HTTPCookie, in cookieStore: CookieStoreProtocol) +} + +// MARK: - Base Cookie Handler + +class BaseCookieHandler: CookieHandler { + let cookieName: String + + init(cookieName: String) { + self.cookieName = cookieName + } + + func makeCookie() -> HTTPCookie? { + guard let value = getCookieValue() else { return nil } + return createHTTPCookie(value: value) + } + + func received(_ cookie: HTTPCookie, in cookieStore: CookieStoreProtocol) { + // Override in subclasses + } + + // MARK: - Helper methods + + func createHTTPCookie(value: String) -> HTTPCookie? { + let urlProvider = Cookie.urlProvider + return HTTPCookie(properties: [ + .name: cookieName, + .domain: ".\(urlProvider.domain)", + .path: "/", + .value: value + ]) + } + + func getCookieValue() -> String? { + // Override in subclasses + return nil + } +} diff --git a/firefox-ios/Ecosia/Core/Cookie/CookieStoreProtocol.swift b/firefox-ios/Ecosia/Core/Cookie/CookieStoreProtocol.swift new file mode 100644 index 0000000000000..1e03cbafa82d5 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/CookieStoreProtocol.swift @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import WebKit + +public protocol CookieStoreProtocol { + func allCookies() async -> [HTTPCookie] + func setCookie(_ cookie: HTTPCookie) async +} + +extension WKHTTPCookieStore: CookieStoreProtocol {} diff --git a/firefox-ios/Ecosia/Core/Cookie/MainCookieHandler.swift b/firefox-ios/Ecosia/Core/Cookie/MainCookieHandler.swift new file mode 100644 index 0000000000000..43cf35b929c1a --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/MainCookieHandler.swift @@ -0,0 +1,121 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import WebKit + +public enum CookieMode { + case standard + case incognito +} + +final class MainCookieHandler: BaseCookieHandler { + private let mode: CookieMode + + init(mode: CookieMode = .standard) { + self.mode = mode + super.init(cookieName: Cookie.main.rawValue) + } + + override func makeCookie() -> HTTPCookie? { + return makeCookie(for: mode) + } + + func makeCookie(for mode: CookieMode) -> HTTPCookie? { + let value = cookieValues(for: mode).map { $0.key + "=" + $0.value }.joined(separator: ":") + return createHTTPCookie(value: value) + } + + override func received(_ cookie: HTTPCookie, in cookieStore: CookieStoreProtocol) { + let value = cookie.value + let properties = value.components(separatedBy: ":") + .map { $0.components(separatedBy: "=") } + .filter { $0.count == 2 } + .reduce(into: [:]) { result, item in + result[item[0]] = item[1] + } + extractMainProperties(properties) + } + + // MARK: - Private helpers + + private struct Properties { + static let userId = "cid" + static let suggestions = "as" + static let personalized = "pz" + static let customSettings = "cs" + static let adultFilter = "f" + static let marketCode = "mc" + static let treeCount = "t" + static let language = "l" + static let marketApplied = "ma" + static let marketReapplied = "mr" + static let deviceType = "dt" + static let firstSearch = "fs" + static let addon = "a" + } + + private func cookieValues(for mode: CookieMode) -> [String: String] { + var values = baseValues() + + switch mode { + case .standard: + // Add user-specific values for standard mode + values[Properties.userId] = User.shared.id + values[Properties.treeCount] = String(User.shared.searchCount) + case .incognito: + // Incognito mode uses only base values (no user ID or tree count) + break + } + + return values + } + + private func baseValues() -> [String: String] { + return [ + Properties.adultFilter: User.shared.adultFilter.rawValue, + Properties.marketCode: User.shared.marketCode.rawValue, + Properties.language: Language.current.rawValue, + Properties.suggestions: String(User.shared.autoComplete ? 1 : 0), + Properties.personalized: String(User.shared.personalized ? 1 : 0), + Properties.marketApplied: "1", + Properties.marketReapplied: "1", + Properties.deviceType: "mobile", + Properties.firstSearch: "0", + Properties.addon: "1" + ] + } + + private func extractMainProperties(_ properties: [String: String]) { + var user = User.shared + + properties[Properties.userId].map { + user.id = $0 + } + + properties[Properties.treeCount].flatMap(Int.init).map { + // tree count should only increase or be reset to 0 on logout + if $0 == 0 || $0 > user.searchCount { + user.searchCount = $0 + } + } + + properties[Properties.marketCode].flatMap(Local.init).map { + user.marketCode = $0 + } + + properties[Properties.adultFilter].flatMap(AdultFilter.init).map { + user.adultFilter = $0 + } + + properties[Properties.personalized].flatMap(Int.init).map { NSNumber(value: $0) }.flatMap(Bool.init).map { + user.personalized = $0 + } + + properties[Properties.suggestions].map { + user.autoComplete = ($0 as NSString).boolValue + } + + User.shared = user + } +} diff --git a/firefox-ios/Ecosia/Core/Cookie/UnleashCookieHandler.swift b/firefox-ios/Ecosia/Core/Cookie/UnleashCookieHandler.swift new file mode 100644 index 0000000000000..180ec97fbde39 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Cookie/UnleashCookieHandler.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import WebKit + +final class UnleashCookieHandler: BaseCookieHandler { + + private let unleash: UnleashProtocol.Type + + init(unleash: UnleashProtocol.Type = Unleash.self) { + self.unleash = unleash + super.init(cookieName: Cookie.unleash.rawValue) + } + + override func getCookieValue() -> String? { + guard unleash.isLoaded else { + return nil + } + return Unleash.userId.uuidString.lowercased() + } + + override func received(_ cookie: HTTPCookie, in cookieStore: CookieStoreProtocol) { + if let nativeIdCooke = makeCookie() { + // Force override Unleash cookie with native Id when it changes + if cookie.value != nativeIdCooke.value { + Task { + await cookieStore.setCookie(nativeIdCooke) + } + } + } + } +} diff --git a/firefox-ios/Ecosia/Core/DataClearingIntegration.swift b/firefox-ios/Ecosia/Core/DataClearingIntegration.swift new file mode 100644 index 0000000000000..1a99a5e15625d --- /dev/null +++ b/firefox-ios/Ecosia/Core/DataClearingIntegration.swift @@ -0,0 +1,30 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import OSLog + +/// Utility for integrating native authentication with browser data clearing operations +/// Ensures authentication state remains consistent when user data is cleared +public enum DataClearingIntegration { + + /// Handles native logout when cookies are cleared through browser settings + /// Should be called whenever cookies are cleared to maintain auth state consistency + public static func handleEcosiaAuthCookieClearing() async { + guard EcosiaAuthenticationService.shared.isLoggedIn else { + EcosiaLogger.auth.info("User not logged in - skipping logout on cookie clearing") + return + } + + EcosiaLogger.auth.info("Triggering native logout due to cookie clearing") + + do { + // Perform logout without triggering web logout since cookies are already being cleared + try await EcosiaAuthenticationService.shared.logout(triggerWebLogout: false) + EcosiaLogger.auth.info("Native logout completed successfully during cookie clearing") + } catch { + EcosiaLogger.auth.error("Failed to perform native logout during cookie clearing: \(error)") + } + } +} diff --git a/firefox-ios/Ecosia/Core/Date+TimestampProvider.swift b/firefox-ios/Ecosia/Core/Date+TimestampProvider.swift new file mode 100644 index 0000000000000..c2dcb0f038124 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Date+TimestampProvider.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Date: TimestampProvider { + public var currentTimestamp: TimeInterval { + return self.timeIntervalSince1970 + } +} diff --git a/firefox-ios/Ecosia/Core/EcosiaEnvironment.swift b/firefox-ios/Ecosia/Core/EcosiaEnvironment.swift new file mode 100644 index 0000000000000..e1808052aed9f --- /dev/null +++ b/firefox-ios/Ecosia/Core/EcosiaEnvironment.swift @@ -0,0 +1,6 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +// Type alias to avoid conflict with SwiftUI's Environment +public typealias EcosiaEnvironment = Environment diff --git a/firefox-ios/Ecosia/Core/EcosiaLogger.swift b/firefox-ios/Ecosia/Core/EcosiaLogger.swift new file mode 100644 index 0000000000000..78edcfd8f010d --- /dev/null +++ b/firefox-ios/Ecosia/Core/EcosiaLogger.swift @@ -0,0 +1,127 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Ecosia-specific logging levels following established patterns +public enum LogLevel { + case debug + case info + case warning + case error +} + +/// Protocol for category-specific logging with default implementations +public protocol EcosiaLoggerCategory { + static var prefix: String { get } +} + +public extension EcosiaLoggerCategory { + static func debug(_ message: String) { + EcosiaLogger.debug("\(prefix) \(message)") + } + + static func info(_ message: String) { + EcosiaLogger.info("\(prefix) \(message)") + } + + static func notice(_ message: String) { + EcosiaLogger.warning("\(prefix) \(message)") + } + + static func error(_ message: String) { + EcosiaLogger.error("\(prefix) \(message)") + } +} + +/// Ecosia-specific logger that avoids conflicts with Firefox's logging system +/// Debug and info logs are stripped out in release builds for performance and security +public enum EcosiaLogger { + + static let prefix: String = "Ecosia Logger" + + /// Centralized timestamp formatter for consistent log formatting + private static let timestampFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" + formatter.timeZone = TimeZone.current + return formatter + }() + + /// Generate a timestamp string for log entries + private static var timestamp: String { + return timestampFormatter.string(from: Date()) + } + + /// Log a debug message (only available in DEBUG builds) + public static func debug(_ message: String) { + #if DEBUG + print("[\(timestamp)] \(prefix): 🔍 [DEBUG] \(message)") + #endif + } + + /// Log an info message (only available in DEBUG builds) + public static func info(_ message: String) { + #if DEBUG + print("[\(timestamp)] \(prefix): ℹ️ [INFO] \(message)") + #endif + } + + /// Log a warning message (available in all builds) + public static func warning(_ message: String) { + print("[\(timestamp)] \(prefix): ⚠️ [WARNING] \(message)") + } + + /// Log an error message (available in all builds) + public static func error(_ message: String) { + print("[\(timestamp)] \(prefix): ❌ [ERROR] \(message)") + } + + /// Generic log method with level + public static func log(_ message: String, level: LogLevel) { + switch level { + case .debug: + debug(message) + case .info: + info(message) + case .warning: + warning(message) + case .error: + error(message) + } + } + + // Category-specific loggers + public enum auth: EcosiaLoggerCategory { + public static let prefix = "🔐 [AUTH]" + } + + public enum invisibleTabs: EcosiaLoggerCategory { + public static let prefix = "👻 [TABS]" + } + + public enum tabAutoClose: EcosiaLoggerCategory { + public static let prefix = "⏰ [AUTO-CLOSE]" + } + + public enum session: EcosiaLoggerCategory { + public static let prefix = "🎫 [SESSION]" + } + + public enum cookies: EcosiaLoggerCategory { + public static let prefix = "🍪 [COOKIES]" + } + + public enum general: EcosiaLoggerCategory { + public static let prefix = "🌱 [GENERAL]" + } + + public enum network: EcosiaLoggerCategory { + public static let prefix = "🌐 [NETWORK]" + } + + public enum accounts: EcosiaLoggerCategory { + public static let prefix = "👤 [ACCOUNTS]" + } +} diff --git a/firefox-ios/Ecosia/Core/Encodable+Dictionary.swift b/firefox-ios/Ecosia/Core/Encodable+Dictionary.swift new file mode 100644 index 0000000000000..8676e6033f6b2 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Encodable+Dictionary.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Encodable { + public var dictionary: [String: String] { + (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self))).flatMap { $0 as? [String: String] } ?? [String: String]() + } +} diff --git a/firefox-ios/Ecosia/Core/Environment/Environment.Auth.swift b/firefox-ios/Ecosia/Core/Environment/Environment.Auth.swift new file mode 100644 index 0000000000000..0d7618a0615c2 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Environment/Environment.Auth.swift @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Environment { + struct CloudFlareAuth: Equatable { + let id: String + let secret: String + } + + var cloudFlareAuth: CloudFlareAuth? { + switch self { + case .staging: + let keyId = "CF_ACCESS_CLIENT_ID" + let keySecret = "CF_ACCESS_CLIENT_SECRET" + + guard let id = EnvironmentFetcher.valueFromMainBundleOrProcessInfo(forKey: keyId), + let secret = EnvironmentFetcher.valueFromMainBundleOrProcessInfo(forKey: keySecret) else { return nil } + return CloudFlareAuth(id: id, secret: secret) + + default: + return nil + } + } +} diff --git a/firefox-ios/Ecosia/Core/Environment/Environment.swift b/firefox-ios/Ecosia/Core/Environment/Environment.swift new file mode 100644 index 0000000000000..38216a5d7fb9a --- /dev/null +++ b/firefox-ios/Ecosia/Core/Environment/Environment.swift @@ -0,0 +1,51 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum Environment: Equatable { + case production + case staging + case debug +} + +extension Environment { + + public static var current: Environment { + /* + * Why not xcconfig compilation flags? + * - Project configs had SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; blocking xcconfig inheritance + * - Multiple BetaDebug configs with same name, Xcode uses wrong one + * - EcosiaTesting.xcconfig works because it sets explicit value, not empty string + * + * Solution: Bundle ID detection is more reliable than build config inheritance + */ + guard let bundleId = Bundle.main.bundleIdentifier else { + return .production + } + + switch bundleId { + case "com.ecosia.ecosiaapp": + return .production + case "com.ecosia.ecosiaapp.firefox": + return .staging + default: + return .debug + } + } +} + +extension Environment { + + public var urlProvider: URLProvider { + switch self { + case .production: + return .production + case .staging: + return .staging + case .debug: + return .debug + } + } +} diff --git a/firefox-ios/Ecosia/Core/Environment/URLProvider.swift b/firefox-ios/Ecosia/Core/Environment/URLProvider.swift new file mode 100644 index 0000000000000..7cae92a92f7ab --- /dev/null +++ b/firefox-ios/Ecosia/Core/Environment/URLProvider.swift @@ -0,0 +1,258 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum URLProvider { + + case production + case staging + case debug + + // MARK: - Public Properties + + public var domain: String { + switch self { + case .production, .debug: + return "ecosia.org" + case .staging: + return "ecosia-staging.xyz" + } + } + + public var root: URL { + URL(string: "https://www.\(domain)")! + } + + public var apiRoot: URL { + URL(string: "https://api.\(domain)")! + } + + public var snowplowMicro: String? { + switch self { + case .staging: + return "https://www.\(domain)/analytics-test-micro" + case .production, .debug: + return nil + } + } + + public var snowplow: String { + switch self { + case .production, .debug: + return "sp.ecosia.org" + case .staging: + return "https://osc.ecosia-staging.xyz" + } + } + + var unleash: String { + switch self { + case .production, .debug: + return "prod" + case .staging: + return "staging" + } + } + + public var brazeEndpoint: String { + "sdk.fra-02.braze.eu" + } + + public var statistics: URL { + URL(string: "https://d2wfixp891z15b.cloudfront.net")! + } + + public var financialReportsData: URL { + URL(string: "https://s3.amazonaws.com/blog-en.ecosia.org/financial-reports/data.json")! + } + + public var privacy: URL { + URL(string: "https://www.ecosia.org/privacy")! + } + + public var faq: URL { + URL(string: "https://ecosia.helpscoutdocs.com/")! + } + + public var terms: URL { + URL(string: "https://www.ecosia.org/terms-of-service")! + } + + public var aboutCounter: URL { + URL(string: "https://ecosia.helpscoutdocs.com/article/369-impact-counter")! + } + + public var bookmarksHelp: URL { + URL(string: "https://ecosia.helpscoutdocs.com/article/458-import-export-bookmarks")! + } + + public var referHelp: URL { + URL(string: "https://ecosia.helpscoutdocs.com/article/358-refer-a-friend-ios-only")! + } + + public var financialReports: URL { + switch Language.current { + case .de: + return blog.appendingPathComponent("ecosia-finanzberichte-baumplanzbelege/") + case .fr: + return blog.appendingPathComponent("rapports-financiers-recus-de-plantations-arbres/") + default: + return blog.appendingPathComponent("ecosia-financial-reports-tree-planting-receipts/") + } + } + + public var blog: URL { + switch Language.current { + case .de: + return URL(string: "https://de.blog.ecosia.org/")! + case .fr: + return URL(string: "https://fr.blog.ecosia.org/")! + default: + return URL(string: "https://blog.ecosia.org/")! + } + } + + public var trees: URL { + switch Language.current { + case .de: + return blog.appendingPathComponent("tag/projekte/") + case .fr: + return blog.appendingPathComponent("tag/projets/") + default: + return blog.appendingPathComponent("tag/where-does-ecosia-plant-trees/") + } + } + + public var betaProgram: URL { + switch Language.current { + case .de: + return URL(string: "https://ecosia.typeform.com/to/catmFLuA")! + case .fr: + return URL(string: "https://ecosia.typeform.com/to/oaFZzT0F")! + default: + return URL(string: "https://ecosia.typeform.com/to/EeMLqL3X")! + } + } + + public var betaFeedback: URL { + switch Language.current { + case .de: + return URL(string: "https://ecosia.typeform.com/to/pIQ3uwp9")! + case .fr: + return URL(string: "https://ecosia.typeform.com/to/PRw7550n")! + default: + return URL(string: "https://ecosia.typeform.com/to/LlUGlFT9")! + } + } + + public var support: URL { + switch Language.current { + case .de: + return URL(string: "https://de.support.ecosia.org/")! + case .fr: + return URL(string: "https://fr.support.ecosia.org/")! + default: + return URL(string: "https://support.ecosia.org/")! + } + } + + public var helpPage: URL { + switch Language.current { + case .de: + return support.appendingPathComponent("category/695-ecosia-ios-app") + case .fr: + return support.appendingPathComponent("category/805-ecosia-ios-app") + default: + return support.appendingPathComponent("category/827-ecosia-ios-app") + } + } + + public var trackingProtectionHelpPage: URL { + switch Language.current { + case .de: + return support.appendingPathComponent("article/1024-tracking-protection-mobile") + case .fr: + return support.appendingPathComponent("article/1025-tracking-protection") + default: + return support.appendingPathComponent("article/1023-tracking-protection") + } + } + + public var notifications: URL { + let url = URL(string: "https://api.ecosia.org/v1/notifications")! + return url.appendingQueryItems([ + .init(name: "language", value: Language.current.rawValue), + .init(name: "market", value: User.shared.marketCode.rawValue), + .init(name: "limit", value: "50") + ]) + } + + public enum AISearchOrigin: String { + case ntp = "newtabbutton" + case autocomplete = "autocomplete_app" + } + public func aiSearch(origin: AISearchOrigin?) -> URL { + let baseURL = root.appendingPathComponent("ai-search") + guard let origin = origin else { + return baseURL + } + + return baseURL.appendingQueryItems([URLQueryItem(name: "origin", value: origin.rawValue)]) + } + + public var storeWriteReviewPage: URL { + URL(string: "https://itunes.apple.com/app/id670881887?action=write-review")! + } + + public var seedCounterInfo: URL { + URL(string: "https://support.ecosia.org/article/844-seed-counter")! + } + + public var profileURL: URL { + root.appendingPathComponent("accounts/profile") + } + + // MARK: - Authentication URL Patterns + + /// URL paths that indicate errors in either the signUp or signOut flow + public var errorPaths: [String] { + ["/accounts/error"] + } + + // MARK: - Authentication URLs + + /// Complete URL for user login/sign-up flow + public var signUpURL: URL { + root.appendingPathComponent("accounts/sign-up") + } + + /// Complete URL for user sign-in flow + public var signInURL: URL { + root.appendingPathComponent("accounts/sign-in") + } + + /// Complete URL for user logout/sign-out flow + public var logoutURL: URL { + root.appendingPathComponent("accounts/sign-out") + } + + /// The API Identifier matching the `audience` parameter used by Auth0 when creating the `WebAuth` + public var authApiAudience: URL { + URL(string: "https://auth0.api.ecosia.org/v1/accounts/web")! + } + + // MARK: - Auth0 Configuration + + /// Auth0 domain for authentication (custom domain) + public var auth0Domain: String { + "login.\(domain)" + } + + /// Auth0 cookie domain for session management + /// Returns the same value as `auth0Domain` since they must match for custom domain authentication + public var auth0CookieDomain: String { + auth0Domain + } +} diff --git a/firefox-ios/Ecosia/Core/EnvironmentFetcher.swift b/firefox-ios/Ecosia/Core/EnvironmentFetcher.swift new file mode 100644 index 0000000000000..88408703d801d --- /dev/null +++ b/firefox-ios/Ecosia/Core/EnvironmentFetcher.swift @@ -0,0 +1,28 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct EnvironmentFetcher { + + private init() {} + + /// Fetches a string value associated with the specified key either from the Main Bundle Info Dictionary or the Process Info environment. + /// + /// - Parameters: + /// - key: The key for which to retrieve the associated string value. + /// - Returns: The string value associated with the key, or nil if not found. + public static func valueFromMainBundleOrProcessInfo(forKey key: String) -> String? { + // Attempt to retrieve the value from the Main Bundle Info Dictionary + // If not found, try to retrieve it from the Process Info environment + guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String + ?? ProcessInfo.processInfo.environment[key] else { + // Return nil if the value is not found in either location + return nil + } + + // Return the retrieved value + return value + } +} diff --git a/firefox-ios/Ecosia/Core/Favourites.swift b/firefox-ios/Ecosia/Core/Favourites.swift new file mode 100644 index 0000000000000..a4b65ab945896 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Favourites.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class Favourites { + public var items = [Page]() { + didSet { + PageStore.save(favourites: items) + } + } + + public init() { + items = PageStore.favourites + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/FeatureManagementSessionInitializer.swift b/firefox-ios/Ecosia/Core/FeatureManagement/FeatureManagementSessionInitializer.swift new file mode 100644 index 0000000000000..97ac8bf14d69b --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/FeatureManagementSessionInitializer.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol FeatureManagementSessionInitializer { + + func startSession() async throws -> T? +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/AppUpdateRule.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/AppUpdateRule.swift new file mode 100644 index 0000000000000..0d07febbb7d97 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/AppUpdateRule.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct AppUpdateRule: RefreshingRule { + + private var currentAppVersion: String? + + init(appVersion: String) { + self.currentAppVersion = appVersion + } + + var shouldRefresh: Bool { + currentAppVersion != Unleash.model.appVersion + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/DeviceRegionChangeRule.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/DeviceRegionChangeRule.swift new file mode 100644 index 0000000000000..291e8dde46495 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/DeviceRegionChangeRule.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct DeviceRegionChangeRule: RefreshingRule { + + private var currentRegion: String + + init(localeProvider: RegionLocatable = Locale.current) { + currentRegion = localeProvider.regionIdentifierLowercasedWithFallbackValue + } + + var shouldRefresh: Bool { + currentRegion != Unleash.model.deviceRegion + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/RefreshingRule.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/RefreshingRule.swift new file mode 100644 index 0000000000000..cf8a916559e8f --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/RefreshingRule.swift @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +protocol RefreshingRule { + var shouldRefresh: Bool { get } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/TimeBasedRefreshingRule.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/TimeBasedRefreshingRule.swift new file mode 100644 index 0000000000000..7a976b60c23f4 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/RefreshingComponent/RefreshingRule/TimeBasedRefreshingRule.swift @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct TimeBasedRefreshingRule: RefreshingRule { + + let interval: TimeInterval + let timestampProvider: TimestampProvider + + init(interval: TimeInterval, timestampProvider: TimestampProvider = Date()) { + self.interval = interval + self.timestampProvider = timestampProvider + } + + var shouldRefresh: Bool { + return timestampProvider.currentTimestamp - Unleash.model.updated.timeIntervalSince1970 > interval + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash+RefreshComponent.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash+RefreshComponent.swift new file mode 100644 index 0000000000000..b88b7b0f27aa0 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash+RefreshComponent.swift @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Unleash { + + /// Adds a refreshing rule to the `rules` array. + /// + /// This method checks if a rule of the same type already exists in the `rules` array + /// and adds the new rule only if it's not present. This prevents duplicate rules of the same type. + /// + /// - Parameter rule: The refreshing rule to be added. + static func addRule(_ rule: RefreshingRule) { + if !rules.contains(where: { type(of: $0) == type(of: rule) }) { + rules.append(rule) + } + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash.Model.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash.Model.swift new file mode 100644 index 0000000000000..c8c136910dca4 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash.Model.swift @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Unleash { + public struct Model: Codable { + public var id = UUID() + var toggles = Set() + var updated = Date(timeIntervalSince1970: 0) + var appVersion: String = "" + var deviceRegion: String = "" + public var etag: String = "" + + public subscript(_ name: Toggle.Name) -> Toggle? { + toggles.first { $0.name == name.rawValue } + } + } + + public struct Toggle: Codable, Hashable { + public enum Name: String { + case aiSearchMVP = "ai2-67-ai-search-mvp" + case brazeIntegration = "mob_ios_braze_integration" + case configTest = "mob_ios_staging_config" + case seedCounterNTP = "mob_ios_seed_counter_ntp" + case newsletterCard = "mob_ios_newsletter_card" + case onboardingProductTour = "mob_ios_onboarding_product_tour" + } + + public let name: String + public let enabled: Bool + public let variant: Variant + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.name == rhs.name + } + + public func hash(into: inout Hasher) { + into.combine(name) + } + } + + public struct Variant: Codable { + public let name: String + public let enabled: Bool + public let payload: Payload? + } + + public struct Payload: Codable { + public let type, value: String + } + + struct FeatureResponse: Codable { + let toggles: [Toggle] + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash.swift new file mode 100644 index 0000000000000..db26764226a86 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/Unleash.swift @@ -0,0 +1,186 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol UnleashProtocol { + /// Checks if a toggle with the given name exists and is enabled. + /// - Parameter name: The name of the toggle. + /// - Returns: `true` if the toggle is enabled, `false` otherwise. + static func isEnabled(_ flag: Unleash.Toggle.Name) -> Bool + + /// Indicates whether Unleash has been loaded from filesystem or network + static var isLoaded: Bool { get } +} + +public enum Unleash: UnleashProtocol { + + public typealias Context = [String: String] + + static var model = Model() + public static var userId: UUID { model.id } + private static let queue = DispatchQueue(label: "com.ecosia.ModelManagerQueue") + static var rules: [RefreshingRule] = [] + private static var _isLoaded = false + + /// Indicates whether Unleash has been loaded from filesystem or network + public static var isLoaded: Bool { + return queue.sync { _isLoaded } + } + + /// Locale source for dependency injection (for testing purposes) + static var localeSource: RegionLocatable = Locale.current + + static var currentDeviceRegion: String { + localeSource.regionIdentifierLowercasedWithFallbackValue + } + + static var englishCountryName: String? { + localeSource.englishLocalizedCountryName + } + + public static func queryParameters(appVersion: String) -> Context { + ["userId": userId.uuidString, + "appName": "iOS", + "appVersion": appVersion, + "versionOnInstall": User.shared.versionOnInstall, + "environment": EcosiaEnvironment.current.urlProvider.unleash, + "market": User.shared.marketCode.rawValue, + "deviceRegion": currentDeviceRegion, + // Mapped from device region to enabled Web flags with geo-ip based context field + "country": englishCountryName ?? "Unknown", + "personalCounterSearches": "\(User.shared.searchCount)"] + } + + /// Starts the Unleash feature management session. + /// - Parameters: + /// - client: The HTTP client to use for network requests. Default is `URLSessionHTTPClient`. + /// - request: The base request to be used for the session. Default is `nil`. + /// - env: The environment for the session. Default is `.production`. + /// - force: Indicates whether to force a refresh even if the model is not expired. Default is `false`. + /// - Returns: The updated `Model` after starting the session. + /// - Throws: An error if the session fails to start or save the model. + public static func start(client: HTTPClient = URLSessionHTTPClient(), + request: BaseRequest? = nil, + env: Environment = .production, + appVersion: String) async throws -> Model { + return try await withCheckedThrowingContinuation({ continuation in + Self.queue.async { + Task { + do { + // Load from filesystem if not already happened + if model.updated.timeIntervalSince1970 == 0 { + await load().map({ Self.model = $0 }) + } + + // Mark as loaded after initial load + _isLoaded = true + + // Call backend to refresh the model + Self.model = try await refresh(client: client, + request: request, + model: model, + env: env, + appVersion: appVersion) + + // Save the updated model to the filesystem + try await save(Self.model) + continuation.resume(returning: self.model) + } catch { + continuation.resume(throwing: error) + } + } + } + }) + } + + public static func isEnabled(_ name: Toggle.Name) -> Bool { + model[name]?.enabled ?? false + } + + /// Retrieves the variant of a toggle with the given name. + /// - Parameter name: The name of the toggle. + /// - Returns: The variant of the toggle if it exists, otherwise a default disabled variant. + public static func getVariant(_ name: Toggle.Name) -> Variant { + model[name]?.variant ?? Variant(name: "disabled", enabled: false, payload: nil) + } + + /// Refreshes the Unleash model by making a network request to the backend. + /// - Parameters: + /// - client: The HTTP client to use for network requests. Default is `URLSessionHTTPClient`. + /// - request: The base request to be used for refreshing the model. Default is `nil`. + /// - model: The current model. + /// - env: The environment for the session. + /// - appVersion: The package's hosting app version (`CFShortVersionString`) + /// - Returns: The latest available `Model` after refreshing. + /// - Throws: An error if the model fails to refresh or update. + static func refresh(client: HTTPClient = URLSessionHTTPClient(), + request: BaseRequest? = nil, + model: Model, + env: Environment, + appVersion: String) async throws -> Model { + + guard shouldRefresh else { + // Return the cached model if refresh is not required + return model + } + + // Create a request to refresh the model from the backend + let unleashRemoteRequest = UnleashStartRequest(etag: model.etag, queryParameters: Unleash.queryParameters(appVersion: appVersion)) + + // Initialize the Unleash feature management session + let unleashSessionInitializer = UnleashFeatureManagementSessionInitializer(client: client, + request: request ?? unleashRemoteRequest, + model: model) + + // Start the session and get the latest available model + var latestAvailableModel: Unleash.Model = try await unleashSessionInitializer.startSession()! + latestAvailableModel.updated = .init() + latestAvailableModel.appVersion = appVersion + latestAvailableModel.deviceRegion = currentDeviceRegion + return latestAvailableModel + } + + /// Clear the stored model and sets as unloaded. Does not change the saved file! This is useful for testing purposes. + public static func clearInstanceModel() { + Self.model = .init() + _isLoaded = false + } + + /// Resets the Unleash feature management session and returns the initial model. + /// - Parameters: + /// - client: The HTTP client to use for network requests. Default is `URLSessionHTTPClient`. + /// - env: The environment for the session. + /// - Returns: The initial `Model` after resetting the session. + /// - Throws: An error if the session fails to reset or save the model. + public static func reset(client: HTTPClient = URLSessionHTTPClient(), + env: Environment, + appVersion: String) async throws -> Model { + clearInstanceModel() + try await save(Self.model) + let unleashRemoteRequest = UnleashStartRequest(etag: model.etag, queryParameters: Unleash.queryParameters(appVersion: appVersion)) + return try await start(client: client, + request: unleashRemoteRequest, + env: env, + appVersion: appVersion) + } + + /// Loads the model from the filesystem. + /// - Returns: The loaded `Model` if successful, otherwise `nil`. + static func load() async -> Model? { + try? JSONDecoder().decode(Model.self, from: .init(contentsOf: FileManager.unleash)) + } + + /// Saves the model to the filesystem. + /// - Parameter model: The model to be saved. + /// - Throws: An error if the model fails to be encoded or saved. + static func save(_ model: Model) async throws { + try JSONEncoder().encode(model).write(to: FileManager.unleash, options: .atomic) + } + + /// Determines whether the model should be refreshed based on its refreshing context providers. + static var shouldRefresh: Bool { + return rules.contains(where: { $0.shouldRefresh }) + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashFeatureManagementSessionInitializer.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashFeatureManagementSessionInitializer.swift new file mode 100644 index 0000000000000..3f0d6d7e63764 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashFeatureManagementSessionInitializer.swift @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class UnleashFeatureManagementSessionInitializer: FeatureManagementSessionInitializer { + + private let client: HTTPClient + private let request: BaseRequest + private var model: Unleash.Model + var isSameModel: Bool = false + + public enum Error: Swift.Error { + case network + case noData + } + + public init(client: HTTPClient, request: BaseRequest, model: Unleash.Model) { + self.client = client + self.request = request + self.model = model + } + + public func startSession() async throws -> T? { + + let (data, response) = try await client.perform(request) + + guard let response else { + throw UnleashFeatureManagementSessionInitializer.Error.noData + } + + switch response.statusCode { + case 399...599: + throw UnleashFeatureManagementSessionInitializer.Error.network + case 304: // no changes reported by server -> return cached model + var updatedModel = model + updatedModel.updated = .init() + return updatedModel as? T + case 200: + break + default: + throw UnleashFeatureManagementSessionInitializer.Error.noData + } + + // read out Etag which identifies cached responses + (response.allHeaderFields["Etag"] as? String).map { model.etag = $0 } + + guard let remoteToggles = try? JSONDecoder().decode(Unleash.FeatureResponse.self, from: data) else { + return model as? T + } + + model.toggles = Set(remoteToggles.toggles) + return model as? T + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashRefreshConfigurator.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashRefreshConfigurator.swift new file mode 100644 index 0000000000000..bb693f6858976 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashRefreshConfigurator.swift @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class UnleashRefreshConfigurator { + + public init() {} + + @discardableResult + public func withAppUpdateCheckRule(appVersion: String) -> Self { + let appUpdateRule = AppUpdateRule(appVersion: appVersion) + Unleash.addRule(appUpdateRule) + return self + } + + @discardableResult + public func withTwentyFourHoursCacheExpirationRule() -> Self { + let timeRule = TimeBasedRefreshingRule(interval: TimeInterval.twentyFourHoursTimeInterval) + Unleash.addRule(timeRule) + return self + } + + @discardableResult + public func withDeviceRegionUpdateCheckRule(localeProvider: RegionLocatable = Locale.current) -> Self { + let regionRule = DeviceRegionChangeRule(localeProvider: localeProvider) + Unleash.addRule(regionRule) + return self + } +} diff --git a/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashStartRequest.swift b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashStartRequest.swift new file mode 100644 index 0000000000000..2fe4af52b8347 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FeatureManagement/Unleash/UnleashStartRequest.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct UnleashStartRequest: BaseRequest { + + public var method: HTTPMethod { + .get + } + + public var path: String { + "/v2/toggles" + } + + var etag: String + + public var queryParameters: [String: String]? + + public var additionalHeaders: [String: String]? { + ["If-None-Match": etag] + } + + public var body: Data? +} diff --git a/firefox-ios/Ecosia/Core/FileManager.swift b/firefox-ios/Ecosia/Core/FileManager.swift new file mode 100644 index 0000000000000..1c8503c11ce12 --- /dev/null +++ b/firefox-ios/Ecosia/Core/FileManager.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension FileManager { + static let pages = directory.appendingPathComponent("pages") + static let snapshots = directory.appendingPathComponent("snapshots") + static let user = directory.item("user") + static let tabs = pages.item("tabs") + static let currentTab = pages.item("currentTab") + static let favourites = pages.item("favourites") + static let history = pages.item("history") + static let unleash = directory.item("unleash") + static let news = directory.item("news") + + private static let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] +} + +private extension URL { + func item(_ name: String) -> URL { + appendingPathComponent(name + ".ecosia") + } +} diff --git a/firefox-ios/Ecosia/Core/HTTPClient/HTTPClient.swift b/firefox-ios/Ecosia/Core/HTTPClient/HTTPClient.swift new file mode 100644 index 0000000000000..44c426305c269 --- /dev/null +++ b/firefox-ios/Ecosia/Core/HTTPClient/HTTPClient.swift @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol HTTPClient { + + typealias Result = (Data, HTTPURLResponse?) + + func perform(_ request: BaseRequest) async throws -> Result +} diff --git a/firefox-ios/Ecosia/Core/HTTPClient/HTTPMethod.swift b/firefox-ios/Ecosia/Core/HTTPClient/HTTPMethod.swift new file mode 100644 index 0000000000000..b36132eb8f509 --- /dev/null +++ b/firefox-ios/Ecosia/Core/HTTPClient/HTTPMethod.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum HTTPMethod: String { + case get = "GET" + case post = "POST" +} diff --git a/firefox-ios/Ecosia/Core/HTTPClient/Requestable/BaseRequest.swift b/firefox-ios/Ecosia/Core/HTTPClient/Requestable/BaseRequest.swift new file mode 100644 index 0000000000000..e5503d77c536f --- /dev/null +++ b/firefox-ios/Ecosia/Core/HTTPClient/Requestable/BaseRequest.swift @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol BaseRequest: Requestable {} + +enum RequestError: Error { + case invalidBaseURL + case invalidURLComponents +} + +public extension BaseRequest { + + var baseURL: URL { + environment.urlProvider.apiRoot + } + + var environment: Environment { + .current + } + + func makeURLRequest() throws -> URLRequest { + guard var urlComponents = URLComponents(url: baseURL, resolvingAgainstBaseURL: false) else { + throw RequestError.invalidBaseURL + } + urlComponents.path = path + if let queryParameters { + urlComponents.queryItems = queryParameters.map({ .init(name: $0.key, value: $0.value ) }) + } + guard let url = urlComponents.url else { + throw RequestError.invalidURLComponents + } + + var request = URLRequest(url: url) + request.httpMethod = method.rawValue + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.cachePolicy = .reloadIgnoringLocalCacheData + request.httpBody = body + + if let additionalHeaders { + additionalHeaders.forEach({ request.setValue($0.value, forHTTPHeaderField: $0.key) }) + } + + return request.withCloudFlareAuthParameters() + } +} diff --git a/firefox-ios/Ecosia/Core/HTTPClient/Requestable/Requestable.swift b/firefox-ios/Ecosia/Core/HTTPClient/Requestable/Requestable.swift new file mode 100644 index 0000000000000..ea25ebbd24098 --- /dev/null +++ b/firefox-ios/Ecosia/Core/HTTPClient/Requestable/Requestable.swift @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol Requestable { + + var method: HTTPMethod { get } + + var baseURL: URL { get } + + var path: String { get } + + var environment: Environment { get } + + var queryParameters: [String: String]? { get set } + + var additionalHeaders: [String: String]? { get } + + var body: Data? { get } + + func makeURLRequest() throws -> URLRequest +} diff --git a/firefox-ios/Ecosia/Core/HTTPClient/URLSessionHTTPClient.swift b/firefox-ios/Ecosia/Core/HTTPClient/URLSessionHTTPClient.swift new file mode 100644 index 0000000000000..bef2c2e692484 --- /dev/null +++ b/firefox-ios/Ecosia/Core/HTTPClient/URLSessionHTTPClient.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class URLSessionHTTPClient: HTTPClient { + + public init() {} + + public func perform(_ request: BaseRequest) async throws -> HTTPClient.Result { + let (data, response) = try await URLSession.shared.data(for: request.makeURLRequest()) + return (data, response as? HTTPURLResponse) + } +} diff --git a/firefox-ios/Ecosia/Core/HTTPClient/URLSessionProtocol.swift b/firefox-ios/Ecosia/Core/HTTPClient/URLSessionProtocol.swift new file mode 100644 index 0000000000000..1c9ccf68d1fed --- /dev/null +++ b/firefox-ios/Ecosia/Core/HTTPClient/URLSessionProtocol.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol URLSessionProtocol { + func data(from url: URL) async throws -> (Data, URLResponse) +} + +extension URLSession: URLSessionProtocol { } diff --git a/firefox-ios/Ecosia/Core/History.swift b/firefox-ios/Ecosia/Core/History.swift new file mode 100644 index 0000000000000..a2cc5b5a01d92 --- /dev/null +++ b/firefox-ios/Ecosia/Core/History.swift @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class History { + public var items: [(Date, Page)] { + get { dictionary.sorted { $0.0 < $1.0 }.map { ($0.0, $0.1) } } + set { dictionary = .init(uniqueKeysWithValues: newValue) } + } + + private(set) var dictionary = [Date: Page]() { + didSet { + PageStore.save(history: dictionary) + } + } + + public init() { + dictionary = PageStore.history + } + + public func add(_ page: Page) { + dictionary[Date()] = page + } + + public func delete(_ at: Date) { + dictionary.removeValue(forKey: at) + } + + public func deleteAll() { + dictionary = [:] + } +} diff --git a/firefox-ios/Ecosia/Core/Images.swift b/firefox-ios/Ecosia/Core/Images.swift new file mode 100644 index 0000000000000..d265a8127a407 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Images.swift @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class Images: Publisher { + public var subscriptions = [Subscription]() + var items = Set() + private let session: URLSession + + public init(_ session: URLSession) { + self.session = session + } + + public func load(_ subscriber: AnyObject, url: URL, closure: @escaping (Input) -> Void) { + subscribe(subscriber, closure: closure) + guard let item = items.first(where: { $0.url == url }) + else { + download(url) + return + } + send(item) + } + + public func cancellAll() { + session.getAllTasks { + $0.forEach { $0.cancel() } + } + } + + public func cancel(_ url: URL) { + session.getAllTasks { + $0.first { $0.originalRequest?.url == url }?.cancel() + } + } + + private func download(_ url: URL) { + session.dataTask(with: url) { data, _, _ in + DispatchQueue.main.async { [weak self] in + data.map { + let item = Item(url, $0) + self?.send(item) + self?.items.insert(item) + } + } + }.resume() + } + + public struct Item: Hashable { + public let url: URL + public let data: Data + + init(_ url: URL, _ data: Data) { + self.url = url + self.data = data + } + + public func hash(into: inout Hasher) { + into.combine(url) + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.url == rhs.url + } + } +} diff --git a/firefox-ios/Ecosia/Core/Language.swift b/firefox-ios/Ecosia/Core/Language.swift new file mode 100644 index 0000000000000..26b9c675b147d --- /dev/null +++ b/firefox-ios/Ecosia/Core/Language.swift @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum Language: String, Codable, CaseIterable { + case + de, + en, + es, + it, + fr, + nl, + sv + + public internal(set) static var current = make(for: .current) + + var locale: Local { + switch self { + case .de: return .de_de + case .en: return .en_us + case .es: return .es_es + case .it: return .it_it + case .fr: return .fr_fr + case .nl: return .nl_nl + case .sv: return .sv_se + } + } + + private static let queue = DispatchQueue(label: "\(Bundle.ecosia.bundleIdentifier!).LanguageQueue") + static func make(for locale: Locale) -> Self { + return queue.sync { + locale.withLanguage ?? .en + } + } +} + +private extension Locale { + var withLanguage: Ecosia.Language? { + languageCode.flatMap { + Ecosia.Language(rawValue: $0.lowercased()) + } + } +} diff --git a/firefox-ios/Ecosia/Core/List.swift b/firefox-ios/Ecosia/Core/List.swift new file mode 100644 index 0000000000000..34472d6b332c7 --- /dev/null +++ b/firefox-ios/Ecosia/Core/List.swift @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct List: Decodable where E: Decodable { + var items = [E]() + + init(from: Decoder) throws { + var root = try from.unkeyedContainer() + while !root.isAtEnd { + if let item = try? root.decode(E.self) { + items.append(item) + } else { + _ = try root.nestedContainer(keyedBy: Discard.self) + } + } + } +} + +private enum Discard: CodingKey { } diff --git a/firefox-ios/Ecosia/Core/Local.swift b/firefox-ios/Ecosia/Core/Local.swift new file mode 100644 index 0000000000000..222d68f8a953a --- /dev/null +++ b/firefox-ios/Ecosia/Core/Local.swift @@ -0,0 +1,92 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum Local: String, Codable, CaseIterable { + case + es_ar = "es-ar", + en_au = "en-au", + de_at = "de-at", + fr_be = "fr-be", + nl_be = "nl-be", + pt_br = "pt-br", + bg_bg = "bg-bg", + en_ca = "en-ca", + fr_ca = "fr-ca", + es_cl = "es-cl", + zh_cn = "zh-cn", + es_co = "es-co", + hr_hr = "hr-hr", + da_dk = "da-dk", + fi_fi = "fi-fi", + fr_fr = "fr-fr", + de_de = "de-de", + zh_hk = "zh-hk", + en_in = "en-in", + en_id = "en-id", + en_ie = "en-ie", + it_it = "it-it", + ja_jp = "ja-jp", + lv_lv = "lv-lv", + lt_lt = "lt-lt", + en_my = "en-my", + es_mx = "es-mx", + nl_nl = "nl-nl", + en_nz = "en-nz", + nb_no = "nb-no", + es_pe = "es-pe", + en_ph = "en-ph", + pl_pl = "pl-pl", + pt_pt = "pt-pt", + ru_ru = "ru-ru", + ar_sa = "ar-sa", + en_sa = "en-sa", + en_sg = "en-sg", + sk_sk = "sk-sk", + en_za = "en-za", + ko_kr = "ko-kr", + es_es = "es-es", + sv_se = "sv-se", + de_ch = "de-ch", + fr_ch = "fr-ch", + zh_tw = "zh-tw", + en_th = "en-th", + th_th = "th-th", + tr_tr = "tr-tr", + uk_ua = "uk-ua", + en_gb = "en-gb", + en_us = "en-us", + es_us = "es-us", + es_ve = "es-ve", + en_vn = "en-vn", + vi_vn = "vi-vn", + cz_cz = "cz-cz", + ee_ee = "ee-ee", + gr_gr = "gr-gr", + ro_ro = "ro-ro", + en_ww = "en-ww" + + static func make(for locale: Locale) -> Self { + locale.local ?? locale.withCountry ?? locale.withRegion ?? .en_us + } +} + +private extension Locale { + var local: Local? { + Local(rawValue: identifier.lowercased()) + } + + var withCountry: Local? { + (self as NSLocale).countryCode.flatMap { code in + Local.allCases.first { $0.rawValue.hasSuffix(code.lowercased()) } + } + } + + var withRegion: Local? { + regionCode.flatMap { code in + Local.allCases.first { $0.rawValue.hasSuffix(code.lowercased()) } + } + } +} diff --git a/firefox-ios/Ecosia/Core/Locale+Extensions.swift b/firefox-ios/Ecosia/Core/Locale+Extensions.swift new file mode 100644 index 0000000000000..301444225cf1f --- /dev/null +++ b/firefox-ios/Ecosia/Core/Locale+Extensions.swift @@ -0,0 +1,40 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Locale { + + var identifierWithDashedLanguageAndRegion: String { + identifier.replacingOccurrences(of: "_", with: "-").lowercased() + } + + public var regionIdentifier: String? { + if #available(iOS 16, macOS 13, *) { + return region?.identifier + } else { + return regionCode + } + } + + public var regionIdentifierLowercasedWithFallbackValue: String { + regionIdentifier?.lowercased() ?? "us" + } + + public var englishLocalizedCountryName: String? { + guard let regionIdentifier = regionIdentifier else { return nil } + return Locale(identifier: "en_US").localizedString(forRegionCode: regionIdentifier) + } + + /// Returns the language code in a backward-compatible way + public var languageIdentifier: String? { + if #available(iOS 16, macOS 13, *) { + return language.languageCode?.identifier + } else { + return languageCode + } + } +} + +extension Locale: RegionLocatable {} diff --git a/firefox-ios/Ecosia/Core/MMP/MMP.swift b/firefox-ios/Ecosia/Core/MMP/MMP.swift new file mode 100644 index 0000000000000..a721131a69f60 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/MMP.swift @@ -0,0 +1,72 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import AdServices +import Common + +public enum MMPEvent: String { + case firstSearch = "first_search" + case fifthSearch = "fifth_search" + case tenthSearch = "tenth_search" +} + +public struct MMP { + + private init() {} + + static var appDeviceInfo: AppDeviceInfo { + /// We are hardcoding `iOS` as per the platform parameter + /// as `Singular` MMP doesn't currently support others like `iPadOS` + /// We don't want to modify the `DeviceInfo.platform` as other services may need the correct one. + AppDeviceInfo(platform: "iOS", + bundleId: AppInfo.bundleIdentifier, + osVersion: DeviceInfo.osVersionNumber, + deviceManufacturer: DeviceInfo.manufacturer, + deviceModel: DeviceInfo.deviceModelName, + locale: DeviceInfo.currentLocale, + country: DeviceInfo.currentCountry, + deviceBuildVersion: DeviceInfo.osBuildNumber, + appVersion: AppInfo.ecosiaAppVersion, + installReceipt: AppInfo.installReceipt, + adServicesAttributionToken: AppInfo.adServicesAttributionToken) + } + + public static func sendSession() { + guard User.shared.sendAnonymousUsageData else { return } + + Task { + do { + let mmpProvider: MMPProvider = Singular(includeSKAN: true) + try await mmpProvider.sendSessionInfo(appDeviceInfo: appDeviceInfo) + } catch { + debugPrint(error) + } + } + } + + public static func sendEvent(_ event: MMPEvent) { + guard User.shared.sendAnonymousUsageData else { return } + + Task { + do { + let mmpProvider: MMPProvider = Singular(includeSKAN: true) + try await mmpProvider.sendEvent(event, appDeviceInfo: appDeviceInfo) + } catch { + debugPrint(error) + } + } + } + + public static func handleSearchEvent(_ count: Int) { + let eventMap: [Int: MMPEvent] = [1: .firstSearch, 5: .fifthSearch, 10: .tenthSearch] + if let event = eventMap[count] { + self.sendEvent(event) + } + } +} diff --git a/firefox-ios/Ecosia/Core/MMP/MMPProvider.swift b/firefox-ios/Ecosia/Core/MMP/MMPProvider.swift new file mode 100644 index 0000000000000..c0fad972b5273 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/MMPProvider.swift @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol MMPProvider { + + func sendSessionInfo(appDeviceInfo: AppDeviceInfo) async throws + + func sendEvent(_ event: MMPEvent, appDeviceInfo: AppDeviceInfo) async throws +} diff --git a/firefox-ios/Ecosia/Core/MMP/SKAdNetworkProtocol.swift b/firefox-ios/Ecosia/Core/MMP/SKAdNetworkProtocol.swift new file mode 100644 index 0000000000000..7b534cdbeb8a1 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/SKAdNetworkProtocol.swift @@ -0,0 +1,39 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import StoreKit.SKAdNetwork + +#if os(iOS) + +public protocol SKAdNetworkProtocol { + @available(iOS, introduced: 11.3, deprecated: 15.4) + static func registerAppForAdNetworkAttribution() + @available(iOS, introduced: 14.0, deprecated: 15.4) + static func updateConversionValue(_ conversionValue: Int) + @available(iOS 15.4, *) + static func updatePostbackConversionValue(_ conversionValue: Int) async throws + @available(iOS 16.1, *) + static func updatePostbackConversionValue(_ fineValue: Int, coarseValue: Int?, lockWindow: Bool) async throws +} + +extension SKAdNetwork: SKAdNetworkProtocol { + @available(iOS 16.1, *) + public static func updatePostbackConversionValue(_ fineValue: Int, coarseValue: Int?, lockWindow: Bool) async throws { + var coarseConversionValue: CoarseConversionValue? + switch coarseValue { + case 0: coarseConversionValue = .low + case 1: coarseConversionValue = .medium + case 2: coarseConversionValue = .high + default: break + } + if let value = coarseConversionValue { + try await updatePostbackConversionValue(fineValue, coarseValue: value, lockWindow: lockWindow) + } else { + try await updatePostbackConversionValue(fineValue) + } + } +} + +#endif diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularConversionValueRequest.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularConversionValueRequest.swift new file mode 100644 index 0000000000000..9adea73025479 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularConversionValueRequest.swift @@ -0,0 +1,53 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct SingularConversionValueRequest: BaseRequest { + + struct Parameters: Encodable { + let identifier: String + let platform: String + let bundleId: String + let eventName: String + let osVersion: String + let appVersion: String + + enum CodingKeys: String, CodingKey { + case identifier = "sing" + case platform = "p" + case bundleId = "i" + case eventName = "n" + case osVersion = "ve" + case appVersion = "app_v" + } + + init(identifier: String, eventName: String, appDeviceInfo: AppDeviceInfo) { + self.identifier = identifier + self.platform = appDeviceInfo.platform + self.bundleId = appDeviceInfo.bundleId + self.eventName = eventName + self.osVersion = appDeviceInfo.osVersion + self.appVersion = appDeviceInfo.appVersion + } + } + + var method: HTTPMethod { + .get + } + + var path: String { + "/v2/attribution/conversion-value" + } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? + + init(_ parameters: Parameters, skanParameters: [String: String]) { + self.queryParameters = parameters.dictionary.merging(skanParameters) { current, _ in current } + } +} diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularConversionValueResponse.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularConversionValueResponse.swift new file mode 100644 index 0000000000000..73e34508507b2 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularConversionValueResponse.swift @@ -0,0 +1,21 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct SingularConversionValueResponse: Codable, Equatable { + let conversionValue: Int + let coarseValue: Int? + let lockWindow: Bool? + + enum CodingKeys: String, CodingKey { + case conversionValue = "conversion_value" + case coarseValue = "skan_updated_coarse_value" + case lockWindow = "skan_updated_lock_window_value" + } + + var isValid: Bool { + (0...63 ~= conversionValue) && (0...2 ~= coarseValue ?? 1) + } +} diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularEventRequest.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularEventRequest.swift new file mode 100644 index 0000000000000..23b7754268d7c --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularEventRequest.swift @@ -0,0 +1,50 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +#if os(iOS) + +struct SingularEventRequest: BaseRequest { + + struct Parameters: Encodable { + let identifier: String + let name: String + let platform: String + let bundleId: String + let osVersion: String + + enum CodingKeys: String, CodingKey { + case identifier = "sing" + case name = "n" + case platform = "p" + case bundleId = "i" + case osVersion = "ve" + } + } + + var method: HTTPMethod { + .get + } + + var path: String { + "/v2/attribution/event" + } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? + + init(identifier: String, name: String, info: AppDeviceInfo) { + self.queryParameters = Parameters(identifier: identifier, + name: name, + platform: info.platform, + bundleId: info.bundleId, + osVersion: info.osVersion).dictionary + } +} + +#endif diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularReponse.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularReponse.swift new file mode 100644 index 0000000000000..003d83a34b727 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularReponse.swift @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct SingularResponse: Codable { + let status: String + let errorReason: String? + + enum CodingKeys: String, CodingKey { + case status + case errorReason = "reason" + } + + var isOK: Bool { + status == "ok" + } +} diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularService.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularService.swift new file mode 100644 index 0000000000000..9a683d4718b11 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularService.swift @@ -0,0 +1,64 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +#if os(iOS) + +protocol SingularNotificationRequest: BaseRequest {} +extension SingularSessionInfoSendRequest: SingularNotificationRequest {} +extension SingularEventRequest: SingularNotificationRequest {} + +protocol SingularServiceProtocol { + func sendNotification(request: SingularNotificationRequest) async throws + func getConversionValue(request: SingularConversionValueRequest) async throws -> SingularConversionValueResponse +} + +final class SingularService: SingularServiceProtocol { + + enum Error: Swift.Error { + case network + case dataReturnedError(reason: String) + case noConversionValueReturned + } + + let client: HTTPClient + + init(client: HTTPClient = URLSessionHTTPClient()) { + self.client = client + } + + func sendNotification(request: SingularNotificationRequest) async throws { + let (data, response) = try await client.perform(request) + guard let response, response.statusCode == 200 else { + throw SingularService.Error.network + } + + let dataResult = try JSONDecoder().decode(SingularResponse.self, from: data) + + guard dataResult.isOK, dataResult.errorReason == nil else { + throw SingularService.Error.dataReturnedError(reason: dataResult.errorReason ?? "") + } + } + + func getConversionValue(request: SingularConversionValueRequest) async throws -> SingularConversionValueResponse { + let (data, response) = try await client.perform(request) + + guard let response, response.statusCode == 200 else { + throw SingularService.Error.network + } + + do { + return try JSONDecoder().decode(SingularConversionValueResponse.self, from: data) + } catch DecodingError.keyNotFound { + let fallbackResponse = try JSONDecoder().decode(SingularResponse.self, from: data) + if !fallbackResponse.isOK { + throw Error.dataReturnedError(reason: fallbackResponse.errorReason ?? "") + } + throw Error.noConversionValueReturned // Likely due to no conversion model active for the app + } + } +} + +#endif diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularSessionInfoSendRequest.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularSessionInfoSendRequest.swift new file mode 100644 index 0000000000000..6725fb10b6f79 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Service/SingularSessionInfoSendRequest.swift @@ -0,0 +1,79 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +#if os(iOS) + +struct SingularSessionInfoSendRequest: BaseRequest { + + struct Parameters: Encodable { + let identifier: String + let platform: String + let bundleId: String + let osVersion: String + let deviceManufacturer: String + let deviceModel: String + let locale: String + let country: String? + let deviceBuildVersion: String? + let appVersion: String + let installReceipt: String? + let attributionToken: String? + + enum CodingKeys: String, CodingKey { + case identifier = "sing" + case platform = "p" + case bundleId = "i" + case osVersion = "ve" + case deviceManufacturer = "ma" + case deviceModel = "mo" + case locale = "lc" + case country = "country" + case deviceBuildVersion = "bd" + case appVersion = "app_v" + case installReceipt = "install_receipt" + case attributionToken = "attribution_token" + } + } + + var method: HTTPMethod { + .get + } + + var path: String { + "/v2/attribution/launch" + } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? + + init(identifier: String, info: AppDeviceInfo, skanParameters: [String: String]?) { + var deviceBuildVersion: String? + if let deviceBuildVersionString = info.deviceBuildVersion { + deviceBuildVersion = #"Build\\#(deviceBuildVersionString)"# + } + var parameters = Parameters(identifier: identifier, + platform: info.platform, + bundleId: info.bundleId, + osVersion: info.osVersion, + deviceManufacturer: info.deviceManufacturer, + deviceModel: info.deviceModel, + locale: info.locale, + country: info.country, + deviceBuildVersion: deviceBuildVersion, + appVersion: info.appVersion, + installReceipt: info.installReceipt, + attributionToken: info.adServicesAttributionToken).dictionary + if let skanParameters = skanParameters { + parameters = parameters.merging(skanParameters) { (current, _) in current } + } + self.queryParameters = parameters + } +} + +#endif diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/Singular.swift b/firefox-ios/Ecosia/Core/MMP/Singular/Singular.swift new file mode 100644 index 0000000000000..86d04fc88841a --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/Singular.swift @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +#if os(iOS) + +public struct Singular: MMPProvider { + + let singularService: SingularServiceProtocol + let skanHelper: SingularAdNetworkHelperProtocol? + + init(singularService: SingularServiceProtocol = SingularService(), + skanHelper: SingularAdNetworkHelperProtocol? = SingularAdNetworkHelper()) { + self.singularService = singularService + self.skanHelper = skanHelper + } + + /// Initializer for Singular as MMPProvider. + /// - Parameters: + /// - includeSKAN: If true, all required logic for SKAdNetwork will be executed (e.g. register on first session or fetch updated conversion values from singular server before any event) + public init(includeSKAN: Bool) { + self.singularService = SingularService() + self.skanHelper = includeSKAN ? SingularAdNetworkHelper() : nil + } + + /// Reports a session to the Singular service. + /// - Parameters: + /// - appDeviceInfo: The device info parameters being set to Singular session endpoint. + public func sendSessionInfo(appDeviceInfo: AppDeviceInfo) async throws { + let sessionIdentifier = User.shared.analyticsId.uuidString + + var skanParameters: [String: String]? + if let skanHelper = skanHelper { + if skanHelper.isRegistered { + try? await skanHelper.fetchFromSingularServerAndUpdate(forEvent: .session, + sessionIdentifier: sessionIdentifier, + appDeviceInfo: appDeviceInfo) + } else { + try? await skanHelper.registerAppForAdNetworkAttribution() + } + + skanParameters = skanHelper.persistedValuesDictionary + } + + let request = SingularSessionInfoSendRequest(identifier: sessionIdentifier, info: appDeviceInfo, skanParameters: skanParameters) + try await singularService.sendNotification(request: request) + } + + /// Reports an event to the Singular service. + /// - Parameters: + /// - event: MMPEvent in question out of the supported cases + /// - appDeviceInfo: The device info parameters being set to Singular session endpoint. + public func sendEvent(_ event: MMPEvent, appDeviceInfo: AppDeviceInfo) async throws { + let sessionIdentifier = User.shared.analyticsId.uuidString + let request = SingularEventRequest(identifier: sessionIdentifier, name: event.rawValue, info: appDeviceInfo) + try await singularService.sendNotification(request: request) + } +} + +#endif diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/SingularAdNetworkHelper.swift b/firefox-ios/Ecosia/Core/MMP/Singular/SingularAdNetworkHelper.swift new file mode 100644 index 0000000000000..456108e6855b7 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/SingularAdNetworkHelper.swift @@ -0,0 +1,197 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import StoreKit.SKAdNetwork + +#if os(iOS) + +protocol SingularAdNetworkHelperProtocol { + var persistedValuesDictionary: [String: String] { get } + var isRegistered: Bool { get } + + func registerAppForAdNetworkAttribution() async throws + func fetchFromSingularServerAndUpdate(forEvent event: SingularEvent, sessionIdentifier: String, appDeviceInfo: AppDeviceInfo) async throws +} + +struct SingularAdNetworkHelper: SingularAdNetworkHelperProtocol { + + enum PersistedObject: Hashable, CaseIterable { + static var allCases: [SingularAdNetworkHelper.PersistedObject] { + return [ + .firstSkanCallTimestamp, .lastSkanCallTimestamp, .conversionValue, .previousFineValue, + .coarseValue(window: .first), .coarseValue(window: .second), .coarseValue(window: .third), + .previousCoarseValue(window: .first), .previousCoarseValue(window: .second), .previousCoarseValue(window: .third), + .windowLockTimestamp(window: .first), .windowLockTimestamp(window: .second), .windowLockTimestamp(window: .third), + .errorCode + ] + } + + case firstSkanCallTimestamp + case lastSkanCallTimestamp + case conversionValue + case coarseValue(window: SkanWindow) + case previousFineValue // Only expected to exist for first window + case previousCoarseValue(window: SkanWindow) + case windowLockTimestamp(window: SkanWindow) + case errorCode + + var key: String { + switch self { + case .firstSkanCallTimestamp: return "first_skan_call_timestamp" + case .lastSkanCallTimestamp: return "last_skan_call_timestamp" + case .conversionValue: return "current_conversion_value" + case .coarseValue(let window): return "\(window.rawValue)_coarse_value" + case .previousFineValue: return "prev_fine_value" + case .previousCoarseValue(let window): return "\(window.rawValue)_prev_coarse_value" + case .windowLockTimestamp(let window): return "\(window.rawValue)_window_lock_timestamp" + case .errorCode: return "skan_error_code" + } + } + + var queryKey: String { + switch self { + case .firstSkanCallTimestamp: return "skan_first_call_to_skadnetwork_timestamp" + case .lastSkanCallTimestamp: return "skan_last_call_to_skadnetwork_timestamp" + case .conversionValue: return "skan_current_conversion_value" + case .coarseValue(let window): return "\(window.rawValue)_coarse" + case .previousFineValue: return "prev_fine_value" + case .previousCoarseValue(let window): return "\(window.rawValue)_prev_coarse_value" + case .windowLockTimestamp(let window): return "\(window.rawValue)_window_lock" + case .errorCode: return "_skerror" + } + } + } + + enum SkanWindow: String { + case first = "p0" + case second = "p1" + case third = "p2" + case over + + static let firstSkanWindowInSec = 3600 * 24 * 2 + static let secondSkanWindowInSec = 3600 * 24 * 7 + static let thirdSkanWindowInSec = 3600 * 24 * 35 + + init(timeDiff: Int) { + switch timeDiff { + case 0...SkanWindow.firstSkanWindowInSec: self = .first + case SkanWindow.firstSkanWindowInSec...SkanWindow.secondSkanWindowInSec: self = .second + case SkanWindow.secondSkanWindowInSec...SkanWindow.thirdSkanWindowInSec: self = .third + default: self = .over + } + } + } + + enum Error: Swift.Error { + case invalidConversionValues + } + + private var currentTimestamp: Int { + return Int(timestampProvider.currentTimestamp) + } + var persistedValuesDictionary: [String: String] { + var dictionary = [String: String]() + PersistedObject.allCases.forEach { obj in + if let value = getUserDefaultsInteger(for: obj) { + dictionary[obj.queryKey] = String(value) + } + } + return dictionary + } + var isRegistered: Bool { + return getUserDefaultsInteger(for: .firstSkanCallTimestamp) != nil + } + + private let skan: SKAdNetworkProtocol.Type + private let objectPersister: ObjectPersister + private let timestampProvider: TimestampProvider + private let singularService: SingularServiceProtocol + + init(skan: SKAdNetworkProtocol.Type = SKAdNetwork.self, + objectPersister: ObjectPersister = UserDefaults.standard, + timestampProvider: TimestampProvider = Date(), + singularService: SingularServiceProtocol = SingularService()) { + self.skan = skan + self.objectPersister = objectPersister + self.timestampProvider = timestampProvider + self.singularService = singularService + } + + func registerAppForAdNetworkAttribution() async throws { + guard !isRegistered else { return } + + if #available(iOS 15.4, *) { + do { + try await skan.updatePostbackConversionValue(0) + } catch { + setUserDefaults(for: .errorCode, value: (error as NSError).code) + } + } else { + skan.registerAppForAdNetworkAttribution() + } + setUserDefaults(for: .firstSkanCallTimestamp, value: currentTimestamp) + setUserDefaults(for: .lastSkanCallTimestamp, value: currentTimestamp) + persistUpdatedValues(fineValue: 0, coarseValue: nil) + } + + func fetchFromSingularServerAndUpdate(forEvent event: SingularEvent = .session, sessionIdentifier: String, appDeviceInfo: AppDeviceInfo) async throws { + let firstCallTimestamp = getUserDefaultsInteger(for: .firstSkanCallTimestamp) ?? 0 + guard SkanWindow(timeDiff: currentTimestamp - firstCallTimestamp) != .over else { + return + } + + let request = SingularConversionValueRequest(.init(identifier: sessionIdentifier, eventName: event.rawValue, appDeviceInfo: appDeviceInfo), + skanParameters: persistedValuesDictionary) + let response = try await singularService.getConversionValue(request: request) + if !response.isValid { + throw Error.invalidConversionValues + } + + let conversionValue = response.conversionValue + let coarseValue = response.coarseValue + let lockWindow = response.lockWindow ?? false + if #available(iOS 16.1, *) { + try? await skan.updatePostbackConversionValue(conversionValue, coarseValue: coarseValue, lockWindow: lockWindow) + } else if #available(iOS 15.4, *) { + try? await skan.updatePostbackConversionValue(conversionValue) + } else if #available(iOS 14, *) { + skan.updateConversionValue(conversionValue) + } + persistUpdatedValues(fineValue: conversionValue, + coarseValue: coarseValue, + lockWindow: lockWindow) + } + + private func persistUpdatedValues(fineValue: Int, coarseValue: Int?, lockWindow: Bool = false) { + let window = SkanWindow(timeDiff: currentTimestamp - (getUserDefaultsInteger(for: .firstSkanCallTimestamp) ?? 0)) + guard window != .over else { return } + + if window == .first { + let persistedFineValue: Int? = getUserDefaultsInteger(for: .conversionValue) + setUserDefaults(for: .conversionValue, value: fineValue) + setUserDefaults(for: .previousFineValue, value: persistedFineValue) + } + + let persistedCoarseValue: Int? = getUserDefaultsInteger(for: .coarseValue(window: window)) + setUserDefaults(for: .coarseValue(window: window), value: coarseValue) + setUserDefaults(for: .previousCoarseValue(window: window), value: persistedCoarseValue) + + if lockWindow { + setUserDefaults(for: .windowLockTimestamp(window: window), value: currentTimestamp) + } + + setUserDefaults(for: .lastSkanCallTimestamp, value: currentTimestamp) + } + + private func setUserDefaults(for object: PersistedObject, value: Int?) { + objectPersister.set(value, forKey: object.key) + } + + private func getUserDefaultsInteger(for object: PersistedObject) -> Int? { + return objectPersister.object(forKey: object.key) as? Int + } +} + +#endif diff --git a/firefox-ios/Ecosia/Core/MMP/Singular/SingularEvent.swift b/firefox-ios/Ecosia/Core/MMP/Singular/SingularEvent.swift new file mode 100644 index 0000000000000..63b3af9da5bc5 --- /dev/null +++ b/firefox-ios/Ecosia/Core/MMP/Singular/SingularEvent.swift @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +enum SingularEvent: String { + case session = "__SESSION__" +} diff --git a/firefox-ios/Ecosia/Core/Market.swift b/firefox-ios/Ecosia/Core/Market.swift new file mode 100644 index 0000000000000..38eaafd3c6db0 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Market.swift @@ -0,0 +1,28 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +// Reference: https://github.com/ecosia/core/blob/main/common/js/universal/markets.js + +public struct Market: Decodable { + public let value: Local + public let label: String + public let languageInLabel: Bool + + public init(from decoder: Decoder) throws { + let root = try decoder.container(keyedBy: CodingKeys.self) + value = try root.decode(Local.self, forKey: .value) + label = try root.decode(String.self, forKey: .label) + if let stringValue = try? root.decode(String.self, forKey: .languageInLabel) { + languageInLabel = Bool(stringValue) ?? false + } else { + languageInLabel = false + } + } + + private enum CodingKeys: String, CodingKey { + case value, label, languageInLabel + } +} diff --git a/firefox-ios/Ecosia/Core/Navigation/EcosiaAuthRedirector.swift b/firefox-ios/Ecosia/Core/Navigation/EcosiaAuthRedirector.swift new file mode 100644 index 0000000000000..76cb998557d0d --- /dev/null +++ b/firefox-ios/Ecosia/Core/Navigation/EcosiaAuthRedirector.swift @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct EcosiaAuthRedirector { + + private static let returnToParameterName = "returnTo" + + public static func redirectURLForSignIn(_ url: URL, redirectURLString: String?, urlProvider: URLProvider = Environment.current.urlProvider) -> URL? { + guard isSignInURL(url, urlProvider: urlProvider) else { return nil } + return redirectURL(for: url, redirectURLString: redirectURLString) + } + + public static func redirectURL(for url: URL, redirectURLString: String?) -> URL? { + guard shouldRewrite(url), + let redirectURLString, + let redirectURL = URL(string: redirectURLString) + else { + return nil + } + + return urlWithRedirectParameter(url, redirectURL: redirectURL) + } + + private static func isSignInURL(_ url: URL, urlProvider: URLProvider = Environment.current.urlProvider) -> Bool { + url.isEcosia(urlProvider) && url.relativePath == urlProvider.signInURL.relativePath + } + + private static func shouldRewrite(_ url: URL) -> Bool { + guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + return false + } + guard let queryItems = components.queryItems else { + return true + } + return !queryItems.contains(where: { $0.name == returnToParameterName }) + } + + private static func urlWithRedirectParameter(_ url: URL, redirectURL: URL) -> URL { + let redirectItem = URLQueryItem(name: returnToParameterName, value: redirectURL.absoluteString) + return url.appendingQueryItems([redirectItem]) + } +} diff --git a/firefox-ios/Ecosia/Core/Navigation/EcosiaURLInterceptor.swift b/firefox-ios/Ecosia/Core/Navigation/EcosiaURLInterceptor.swift new file mode 100644 index 0000000000000..76ba801f376f0 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Navigation/EcosiaURLInterceptor.swift @@ -0,0 +1,61 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Represents types of Ecosia URLs that are intercepted for native handling +public enum EcosiaInterceptedURLType { + case signUp + case signIn + case signOut + case profile + case none + + /// Determines the intercepted URL type based on the path + /// - Parameters: + /// - path: The URL path (lowercased recommended) + /// - urlProvider: The URL provider containing path patterns + public init(path: String, urlProvider: URLProvider) { + if urlProvider.signUpURL.relativePath == path { + self = .signUp + } else if urlProvider.signInURL.relativePath == path { + self = .signIn + } else if urlProvider.logoutURL.relativePath == path { + self = .signOut + } else if urlProvider.profileURL.relativePath == path { + self = .profile + } else { + self = .none + } + } +} + +/// Handles detection and classification of Ecosia URLs that should be intercepted +/// for native handling instead of web navigation +public struct EcosiaURLInterceptor { + private let urlProvider: URLProvider + + /// Creates a new URL interceptor + /// - Parameter urlProvider: The URL provider to use for path matching. Defaults to current environment. + public init(urlProvider: URLProvider = Environment.current.urlProvider) { + self.urlProvider = urlProvider + } + + /// Determines if a URL should be intercepted and returns its type + /// - Parameter url: The URL to check + /// - Returns: The intercepted URL type, or `.none` if it shouldn't be intercepted + public func interceptedType(for url: URL) -> EcosiaInterceptedURLType { + guard url.isEcosia(urlProvider) else { return .none } + + let path = url.path.lowercased() + return EcosiaInterceptedURLType(path: path, urlProvider: urlProvider) + } + + /// Checks if a URL should be intercepted + /// - Parameter url: The URL to check + /// - Returns: `true` if the URL should be intercepted, `false` otherwise + public func shouldIntercept(_ url: URL) -> Bool { + interceptedType(for: url) != .none + } +} diff --git a/firefox-ios/Ecosia/Core/News/News.swift b/firefox-ios/Ecosia/Core/News/News.swift new file mode 100644 index 0000000000000..ee225ef6498e7 --- /dev/null +++ b/firefox-ios/Ecosia/Core/News/News.swift @@ -0,0 +1,84 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class News: StatePublisher { + public var subscriptions = [Subscription<[NewsModel]>]() + public var state: [NewsModel]? { + items.sorted { $0.publishDate > $1.publishDate } + } + private let dispatch = DispatchQueue(label: "", qos: .utility) + private let characters = ["'": "'"] + + private var decoder: JSONDecoder { + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + decoder.dateDecodingStrategy = .iso8601 + return decoder + } + + private(set) var items = Set() { + didSet { + DispatchQueue.main.async { [weak self ] in + self?.state.map { self?.send($0) } + } + } + } + + public init() { + dispatch.async { [weak self] in + self?.restore() + } + } + + var needsUpdate: Bool { + guard !items.isEmpty else { return true } + return Calendar.current.dateComponents([.day], from: User.shared.news, to: .init()).day! >= 1 + } + + public func load(session: URLSession, force: Bool = false) { + guard needsUpdate || force else { return } + session.dataTask(with: EcosiaEnvironment.current.urlProvider.notifications) { [weak self] data, _, _ in + self?.dispatch.async { + guard + let data = data, + let new = try? self?.decoder.decode([NewsModel].self, from: data) + else { + return + } + let cleaned = new.compactMap { self?.clean($0) } + self?.items = .init(cleaned + (self?.items ?? [])) + self?.save() + } + }.resume() + } + + private func restore() { + dispatch.async { [weak self] in + if let news = try? JSONDecoder().decode([NewsModel].self, from: .init(contentsOf: FileManager.news)) { + self?.items = .init(news.filter { $0.language == Language.current }) + } + } + } + + private func save() { + dispatch.async { [weak self] in + guard let items = self?.items, !items.isEmpty else { return } + do { + try JSONEncoder().encode(items).write(to: FileManager.news, options: .atomic) + User.shared.news = Date() + } catch {} + } + } + + private func clean(_ item: NewsModel) -> NewsModel { + var item = item + item.text = item.text.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression) + item.text = characters.reduce(item.text) { text, char in + text.replacingOccurrences(of: char.0, with: char.1) + } + return item + } +} diff --git a/firefox-ios/Ecosia/Core/News/NewsModel.swift b/firefox-ios/Ecosia/Core/News/NewsModel.swift new file mode 100644 index 0000000000000..aa1f0c87a4d25 --- /dev/null +++ b/firefox-ios/Ecosia/Core/News/NewsModel.swift @@ -0,0 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct NewsModel: Codable, Hashable { + let id: Int + public internal(set) var text: String + public let language: Language + public let publishDate: Date + public let imageUrl: URL + public let targetUrl: URL + public let trackingName: String + + public func hash(into: inout Hasher) { + into.combine(id) + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.id == rhs.id + } +} diff --git a/firefox-ios/Ecosia/Core/ObjectPersister.swift b/firefox-ios/Ecosia/Core/ObjectPersister.swift new file mode 100644 index 0000000000000..194e013a69600 --- /dev/null +++ b/firefox-ios/Ecosia/Core/ObjectPersister.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol ObjectPersister { + func set(_ value: Any?, forKey: String) + func object(forKey: String) -> Any? +} diff --git a/firefox-ios/Ecosia/Core/Pages/Page.swift b/firefox-ios/Ecosia/Core/Pages/Page.swift new file mode 100644 index 0000000000000..b098ca28b3a12 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Pages/Page.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct Page: Codable { + public let url: URL + public let title: String + + public init(url: URL, title: String) { + self.url = url + self.title = title + } +} diff --git a/firefox-ios/Ecosia/Core/Pages/PageStore.swift b/firefox-ios/Ecosia/Core/Pages/PageStore.swift new file mode 100644 index 0000000000000..e5e4512c5615a --- /dev/null +++ b/firefox-ios/Ecosia/Core/Pages/PageStore.swift @@ -0,0 +1,64 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct PageStore { + static let queue = DispatchQueue(label: "", qos: .utility) + + private init() { } + + static func save(tabs: [Tab]) { + queue.async { + createDirectory() + try? JSONEncoder().encode(tabs).write(to: FileManager.tabs, options: .atomic) + } + } + + static func save(currentTab: Int?) { + queue.async { + if let currentTab = currentTab { + createDirectory() + try? JSONEncoder().encode(currentTab).write(to: FileManager.currentTab, options: .atomic) + } else if FileManager.default.fileExists(atPath: FileManager.currentTab.path) { + try? FileManager.default.removeItem(at: FileManager.currentTab) + } + } + } + + static func save(favourites: [Page]) { + queue.async { + createDirectory() + try? JSONEncoder().encode(favourites).write(to: FileManager.favourites, options: .atomic) + } + } + + static func save(history: [Date: Page]) { + queue.async { + createDirectory() + try? JSONEncoder().encode(history).write(to: FileManager.history, options: .atomic) + } + } + + static var tabs: [Tab] { + (try? JSONDecoder().decode([Tab].self, from: .init(contentsOf: FileManager.tabs))) ?? [] + } + + static var currentTab: Int? { + try? JSONDecoder().decode(Int.self, from: .init(contentsOf: FileManager.currentTab)) + } + + static var favourites: [Page] { + (try? JSONDecoder().decode([Page].self, from: .init(contentsOf: FileManager.favourites))) ?? [] + } + + static var history: [Date: Page] { + (try? JSONDecoder().decode([Date: Page].self, from: .init(contentsOf: FileManager.history))) ?? [:] + } + + private static func createDirectory() { + guard !FileManager.default.fileExists(atPath: FileManager.pages.path) else { return } + try? FileManager.default.createDirectory(at: FileManager.pages, withIntermediateDirectories: true) + } +} diff --git a/firefox-ios/Ecosia/Core/Publisher.swift b/firefox-ios/Ecosia/Core/Publisher.swift new file mode 100644 index 0000000000000..8c8d701e5f216 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Publisher.swift @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol Publisher: AnyObject { + associatedtype Input + var subscriptions: [Subscription] { get set } +} + +public extension Publisher { + func send(_ input: Input) { + subscriptions.removeAll { $0.subscriber == nil } + subscriptions.forEach { $0.closure(input) } + } + + func subscribe(_ subscriber: AnyObject, closure: @escaping (Input) -> Void) { + guard !subscriptions.contains(where: { $0.subscriber === subscriber }) else { return } + subscriptions.append(.init(subscriber: subscriber, closure: closure)) + } + + func unsubscribe(_ subscriber: AnyObject) { + subscriptions.removeAll { $0.subscriber === subscriber } + } +} + +public struct Subscription { + weak var subscriber: AnyObject? + let closure: (Input) -> Void +} + +public protocol StatePublisher: Publisher { + var state: Input? { get } +} + +public extension StatePublisher { + func subscribeAndReceive(_ subscriber: AnyObject, closure: @escaping (Input) -> Void) { + subscribe(subscriber, closure: closure) + state.map(closure) + } +} diff --git a/firefox-ios/Ecosia/Core/Referrals/ReferralClaimRequest.swift b/firefox-ios/Ecosia/Core/Referrals/ReferralClaimRequest.swift new file mode 100644 index 0000000000000..d3d4d45309869 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Referrals/ReferralClaimRequest.swift @@ -0,0 +1,32 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct ReferralClaimRequest: BaseRequest { + struct Claim: Codable { + let referrer: String + let claim: String + + private enum CodingKeys: String, CodingKey { + case + referrer = "referral_code", + claim = "claim_code" + } + } + + var method: HTTPMethod { .post } + + var path: String { "/v1/referrals/claim/" } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? + + init(referrer: String, claim: String) { + self.body = try? JSONEncoder().encode(Claim(referrer: referrer, claim: claim)) + } +} diff --git a/firefox-ios/Ecosia/Core/Referrals/ReferralCreateCodeRequest.swift b/firefox-ios/Ecosia/Core/Referrals/ReferralCreateCodeRequest.swift new file mode 100644 index 0000000000000..a791a8fc9c332 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Referrals/ReferralCreateCodeRequest.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct ReferralCreateCodeRequest: BaseRequest { + var method: HTTPMethod { .post } + + var path: String { "/v1/referrals/referral/" } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? +} diff --git a/firefox-ios/Ecosia/Core/Referrals/ReferralRefreshCodeRequest.swift b/firefox-ios/Ecosia/Core/Referrals/ReferralRefreshCodeRequest.swift new file mode 100644 index 0000000000000..1bddc0751b89b --- /dev/null +++ b/firefox-ios/Ecosia/Core/Referrals/ReferralRefreshCodeRequest.swift @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct ReferralRefreshCodeRequest: BaseRequest { + var method: HTTPMethod { .get } + + var path: String { "/v1/referrals/referral/\(code)" } + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? + + var body: Data? + + let code: String + init(code: String) { + self.code = code + } +} diff --git a/firefox-ios/Ecosia/Core/Referrals/Referrals.Model.swift b/firefox-ios/Ecosia/Core/Referrals/Referrals.Model.swift new file mode 100644 index 0000000000000..2a53eeead6bd4 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Referrals/Referrals.Model.swift @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Referrals { + public enum Error: Int, Swift.Error { + case noConnection = -1 + case badRequest = 400 + case notFound = 404 + case alreadyUsed = 409 + case invalidCode = 422 + case genericError = 500 + } + + public struct Model: Codable, Equatable { + public static let treesPerReferred = 1 + public var code: String? + public var claims = 0 + public var isClaimed = false + public var isNewClaim = false + public var pendingClaim: String? + var updated: Date = .distantPast + var knownClaims = 0 + + public var newClaims: Int { + return claims - knownClaims + } + + public var count: Int { + return claims + (isClaimed ? 1 : 0) + } + + public mutating func accept() { + knownClaims = claims + isNewClaim = false + } + } + + struct CodeInfo: Codable { + let code: String + let claims: Int + + public init(from decoder: Decoder) throws { + let root = try decoder.container(keyedBy: CodingKeys.self) + code = try root.decode(String.self, forKey: .code) + claims = (try? root.decode(Int.self, forKey: .claims)) ?? 0 + } + + public init(code: String, claims: Int) { + self.code = code + self.claims = claims + } + + private enum CodingKeys: String, CodingKey { + case + code, + claims = "claims_count" + } + } +} diff --git a/firefox-ios/Ecosia/Core/Referrals/Referrals.swift b/firefox-ios/Ecosia/Core/Referrals/Referrals.swift new file mode 100644 index 0000000000000..2f0a2b53bdd2c --- /dev/null +++ b/firefox-ios/Ecosia/Core/Referrals/Referrals.swift @@ -0,0 +1,182 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// The `Referrals` class is responsible for handling referral-related operations, +/// including refreshing referral codes, creating new codes, and claiming referrals. +public class Referrals: Publisher { + + /// Subscriptions to the referral model updates. + public var subscriptions = [Subscription]() + + /// Deeplink to enter the Referral's claim + public static let deepLinkPath = "ecosia://invite/" + + /// Link shown in the Modal screen as well as the invite message + public static let sharingLinkRoot = "https://ecosia.co/app?referrer=" + + /// The HTTP client used for performing network requests. + let client: HTTPClient + + /// Initializes a new instance of `Referrals` with the specified HTTP client. + /// - Parameter client: The HTTP client to use. Defaults to `URLSessionHTTPClient`. + public init(client: HTTPClient = URLSessionHTTPClient()) { + self.client = client + } + + /// Indicates whether the referral information needs to be updated. + var needsUpdate: Bool { + return Calendar.current.dateComponents([.hour], from: User.shared.referrals.updated, to: .init()).hour! >= 24 + } + + /// Indicates whether the referral information is currently being refreshed. + var isRefreshing = false + + /// Refreshes the referral information. + /// - Parameters: + /// - force: A Boolean value indicating whether to force a refresh. Defaults to `false`. + /// - createCode: A Boolean value indicating whether to create a new code if one does not exist. Defaults to `false`. + /// - Throws: An error if the refresh operation fails. + public func refresh(force: Bool = false, createCode: Bool = false) async throws { + // Check if code is present + guard let code = User.shared.referrals.code else { + // only fetch new code if desired + guard createCode else { + return + } + + let info = try await self.fetchCode() + await self.update(info) + return + } + + // only refresh if forced or needed + guard force || needsUpdate else { + return + } + + guard !isRefreshing else { + return + } + isRefreshing = true + defer { + self.isRefreshing = false + } + + // Refresh count for given code + do { + let info = try await self.refreshCode(code) + await self.update(info) + } catch { + await self.updateErrorDate() + throw error + } + } + + /// Creates a new referral code. + /// - Returns: The created referral code information. + /// - Throws: An error if the creation operation fails. + func createCode() async throws -> CodeInfo { + let request = ReferralCreateCodeRequest() + let (data, response) = try await client.perform(request) + guard response != nil else { + throw Referrals.Error.noConnection + } + return try JSONDecoder().decode(CodeInfo.self, from: data) + } + + /// Fetches the referral code information. + /// - Returns: The fetched referral code information. + /// - Throws: An error if the fetch operation fails. + func fetchCode() async throws -> CodeInfo { + // pretend success if we have a code already + if let code = User.shared.referrals.code { + return .init(code: code, claims: User.shared.referrals.claims) + } + return try await createCode() + } + + /// Refreshes the referral code information. + /// - Parameter code: The referral code to refresh. + /// - Returns: The refreshed referral code information. + /// - Throws: An error if the refresh operation fails. + func refreshCode(_ code: String) async throws -> CodeInfo { + let request = ReferralRefreshCodeRequest(code: code) + let (data, response) = try await client.perform(request) + guard let response = response else { + throw Referrals.Error.noConnection + } + switch response.statusCode { + case Referrals.Error.notFound.rawValue: + return try await createCode() + case 200: + return try JSONDecoder().decode(CodeInfo.self, from: data) + default: + let error: Referrals.Error? = .init(rawValue: response.statusCode) + throw error ?? .genericError + } + } + + /// Claims a referral using the specified referrer. + /// - Parameter referrer: The referrer identifier. + /// - Throws: An error if the claim operation fails. + public func claim(referrer: String) async throws { + var code: String + if let storedCode = User.shared.referrals.code { + code = storedCode + } else { + let info = try await self.fetchCode() + await self.update(info) + code = info.code + } + try await self.claim(referrer: referrer, claim: code) + await self.storeClaim() + } + + /// Claims a referral using the specified referrer and claim code. + /// - Parameters: + /// - referrer: The referrer identifier. + /// - claim: The claim code. + /// - Throws: An error if the claim operation fails. + private func claim(referrer: String, claim: String) async throws { + let request = ReferralClaimRequest(referrer: referrer, claim: claim) + let (_, response) = try await client.perform(request) + guard let response = response else { + throw Referrals.Error.noConnection + } + guard response.statusCode == 201 else { + let error: Referrals.Error? = .init(rawValue: response.statusCode) + throw error ?? .genericError + } + } + + /// Updates the date on error to refresh the cooldown period. + @MainActor + private func updateErrorDate() { + User.shared.referrals.updated = Date() + } + + /// Updates the referral information. + /// - Parameter info: The referral code information to update. + @MainActor + private func update(_ info: CodeInfo) { + var referrals = User.shared.referrals + referrals.code = info.code + referrals.claims = info.claims + referrals.updated = Date() + User.shared.referrals = referrals + send(referrals) + } + + /// Stores the claim information and updates the referral state. + @MainActor + private func storeClaim() { + var referrals = User.shared.referrals + referrals.isClaimed = true + referrals.isNewClaim = true + User.shared.referrals = referrals + send(referrals) + } +} diff --git a/firefox-ios/Ecosia/Core/RegionLocatable.swift b/firefox-ios/Ecosia/Core/RegionLocatable.swift new file mode 100644 index 0000000000000..4078990d0dc85 --- /dev/null +++ b/firefox-ios/Ecosia/Core/RegionLocatable.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +/// Utilized mainly for the Unleash refresh logic and accommodate testability +/// see: `DeviceRegionChangeProvider.swift` +public protocol RegionLocatable { + var regionIdentifierLowercasedWithFallbackValue: String { get } + var englishLocalizedCountryName: String? { get } +} diff --git a/firefox-ios/Ecosia/Core/Scheme.swift b/firefox-ios/Ecosia/Core/Scheme.swift new file mode 100644 index 0000000000000..0a2f0764a71e0 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Scheme.swift @@ -0,0 +1,37 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public enum Scheme: String { + case + http, + https, + gmsg, + other + + public enum Policy { + case + allow, + cancel + } + + var policy: Policy { + switch self { + case .gmsg: + return .cancel + default: + return .allow + } + } + + var isBrowser: Bool { + switch self { + case .http, .https: + return true + default: + return false + } + } +} diff --git a/firefox-ios/Ecosia/Core/SearchesCounter.swift b/firefox-ios/Ecosia/Core/SearchesCounter.swift new file mode 100644 index 0000000000000..7be72c2480788 --- /dev/null +++ b/firefox-ios/Ecosia/Core/SearchesCounter.swift @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class SearchesCounter: StatePublisher { + public var subscriptions = [Subscription]() + public var state: Int? { + return User.shared.searchCount + } + + public init() { + NotificationCenter.default.addObserver(self, selector: #selector(searchesCounterChanged), name: .searchesCounterChanged, object: nil) + } + + @objc private func searchesCounterChanged() { + send(state!) + } +} diff --git a/firefox-ios/Ecosia/Core/Statistics/FinancialReports.swift b/firefox-ios/Ecosia/Core/Statistics/FinancialReports.swift new file mode 100644 index 0000000000000..e8fa8bc18150e --- /dev/null +++ b/firefox-ios/Ecosia/Core/Statistics/FinancialReports.swift @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class FinancialReports { + public struct Report: Decodable, Equatable { + public internal(set) var totalIncome: Double + public internal(set) var numberOfTreesFinanced: Double + } + + public static let shared = FinancialReports() + public internal(set) var latestMonth: Date = .init(timeIntervalSince1970: 1685577600) + public internal(set) var latestReport: Report = .init(totalIncome: 3206010, + numberOfTreesFinanced: 961642) + + public var localizedMonthAndYear: String { + let formatter = DateFormatter() + formatter.dateFormat = "MMMM yyyy" + formatter.locale = Locale.current + formatter.timeZone = TimeZone(abbreviation: "GMT") + + let localizedMonth = formatter.string(from: latestMonth) + return localizedMonth + } + + init() { } + + public func fetchAndUpdate(urlSession: URLSessionProtocol = URLSession.shared) async throws { + let (data, _) = try await urlSession.data(from: EcosiaEnvironment.current.urlProvider.financialReportsData) + + let response = try JSONSerialization.jsonObject(with: data) as? [String: Any] + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-M" + dateFormatter.timeZone = .init(abbreviation: "UTC") + let months = response?.keys.compactMap { dateFormatter.date(from: $0) } ?? [] + let latestMonth = months.reduce(Date.distantPast) { $0 > $1 ? $0 : $1 } + let latestKey = dateFormatter.string(from: latestMonth) + let latestObject = response?[latestKey] as? [String: Any] ?? [:] + + self.latestMonth = latestMonth + self.latestReport = try JSONDecoder().decode(Report.self, from: JSONSerialization.data(withJSONObject: latestObject)) + } +} diff --git a/firefox-ios/Ecosia/Core/Statistics/InvestmentsProjection.swift b/firefox-ios/Ecosia/Core/Statistics/InvestmentsProjection.swift new file mode 100644 index 0000000000000..d6028b632bd66 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Statistics/InvestmentsProjection.swift @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class InvestmentsProjection: Publisher { + public static let shared = InvestmentsProjection() + public var subscriptions = [Subscription]() + let timer = DispatchSource.makeTimerSource(queue: .main) + + init() { + timer.activate() + timer.setEventHandler { [weak self] in + guard let count = self?.totalInvestedAt(Date()) else { return } + self?.send(count) + } + let secondsToOneEuro = max(1/Statistics.shared.investmentPerSecond, 1) + timer.schedule(deadline: .now(), repeating: secondsToOneEuro) + } + + public func totalInvestedAt(_ date: Date) -> Int { + let statistics = Statistics.shared + let deltaTimeInSeconds = date.timeIntervalSince(statistics.totalInvestmentsLastUpdated) + return .init(deltaTimeInSeconds * statistics.investmentPerSecond + statistics.totalInvestments) + } +} diff --git a/firefox-ios/Ecosia/Core/Statistics/Statistics.swift b/firefox-ios/Ecosia/Core/Statistics/Statistics.swift new file mode 100644 index 0000000000000..d95ecd06c6983 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Statistics/Statistics.swift @@ -0,0 +1,79 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class Statistics { + public struct Response: Decodable { + var results: [Result] + } + public struct Result: Decodable { + var name: String + var value: String + var lastUpdated: String? + + enum StatisticName: String, Decodable { + case treesPlanted = "Total Trees Planted" + case timePerTree = "Time per tree (seconds)" + case searchesPerTree = "Searches per tree" + case activeUsers = "Active Users" + case eurToUsdMultiplier = "EUR=>USD" + case investmentPerSecond = "Investments amount per second" + case totalInvestments = "Total investments amount" + } + + func statisticName() -> StatisticName? { StatisticName(rawValue: name) } + + func doubleValue() -> Double? { Double(value) } + + func lastUpdatedDate() -> Date? { + guard let dateString = lastUpdated else { return nil } + let formatter = ISO8601DateFormatter() + formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + return formatter.date(from: dateString) + } + } + + public static let shared = Statistics() + public internal(set) var treesPlanted = Double(113016418) + public internal(set) var treesPlantedLastUpdated = Date(timeIntervalSince1970: 1604671200) + public internal(set) var timePerTree = Double(1.3) + public internal(set) var searchesPerTree = Double(50) + public internal(set) var activeUsers = Double(20000000) + public internal(set) var eurToUsdMultiplier = Double(1.08) + public internal(set) var investmentPerSecond = Double(0.35) + public internal(set) var totalInvestments = Double(76776000) + public internal(set) var totalInvestmentsLastUpdated = Date(timeIntervalSince1970: 1685404800) + + init() { } + + public func fetchAndUpdate(urlSession: URLSessionProtocol = URLSession.shared) async throws { + let (data, _) = try await urlSession.data(from: EcosiaEnvironment.current.urlProvider.statistics) + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + let response = try decoder.decode(Response.self, from: data) + response.results.forEach { statistic in + switch statistic.statisticName() { + case .treesPlanted: + if let value = statistic.doubleValue(), + let date = statistic.lastUpdatedDate() { + treesPlanted = value + treesPlantedLastUpdated = date + } + case .timePerTree: timePerTree = statistic.doubleValue() ?? timePerTree + case .searchesPerTree: searchesPerTree = statistic.doubleValue() ?? searchesPerTree + case .activeUsers: activeUsers = statistic.doubleValue() ?? activeUsers + case .eurToUsdMultiplier: eurToUsdMultiplier = statistic.doubleValue() ?? eurToUsdMultiplier + case .investmentPerSecond: investmentPerSecond = statistic.doubleValue() ?? investmentPerSecond + case .totalInvestments: + if let value = statistic.doubleValue(), + let date = statistic.lastUpdatedDate() { + totalInvestments = value + totalInvestmentsLastUpdated = date + } + case nil: break + } + } + } +} diff --git a/firefox-ios/Ecosia/Core/Statistics/TreesProjection.swift b/firefox-ios/Ecosia/Core/Statistics/TreesProjection.swift new file mode 100644 index 0000000000000..378a6dcb59194 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Statistics/TreesProjection.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class TreesProjection: Publisher { + public static let shared = TreesProjection() + public var subscriptions = [Subscription]() + let timer = DispatchSource.makeTimerSource(queue: .main) + + init() { + timer.activate() + timer.setEventHandler { [weak self] in + guard let count = self?.treesAt(Date()) else { return } + self?.send(count) + } + timer.schedule(deadline: .now(), repeating: Statistics.shared.timePerTree) + } + + public func treesAt(_ date: Date) -> Int { + let statistics = Statistics.shared + let timeSinceLastUpdate = date.timeIntervalSince(statistics.treesPlantedLastUpdated) + return .init(timeSinceLastUpdate / statistics.timePerTree + statistics.treesPlanted - 1) + } +} diff --git a/firefox-ios/Ecosia/Core/Tabs/Tab.swift b/firefox-ios/Ecosia/Core/Tabs/Tab.swift new file mode 100644 index 0000000000000..d755c7520f218 --- /dev/null +++ b/firefox-ios/Ecosia/Core/Tabs/Tab.swift @@ -0,0 +1,21 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct Tab: Codable, Identifiable { + public var page: Page? + public let id: UUID + + public init(page: Page?) { + self.page = page + id = .init() + } +} + +public extension Tab { + var snapshot: Data? { + return try? Data(contentsOf: FileManager.snapshots.appendingPathComponent(id.uuidString)) + } +} diff --git a/firefox-ios/Ecosia/Core/Tabs/Tabs.swift b/firefox-ios/Ecosia/Core/Tabs/Tabs.swift new file mode 100644 index 0000000000000..e92ef7bfa418d --- /dev/null +++ b/firefox-ios/Ecosia/Core/Tabs/Tabs.swift @@ -0,0 +1,100 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public final class Tabs { + public var current: Int? { + didSet { + PageStore.save(currentTab: current) + } + } + + public private(set) var items = [Tab]() { + didSet { + PageStore.save(tabs: items) + } + } + + let queue = DispatchQueue(label: "", qos: .utility) + + public init() { + items = PageStore.tabs + if let current = PageStore.currentTab { + self.current = current < items.count ? current : nil + } + } + + public func new(_ url: URL?) { + var items = self.items + items.removeAll { $0.page == nil } + let new = Tab(page: url.map { .init(url: $0, title: "") }) + current = items.count + items.append(new) + self.items = items + } + + public func close(_ id: UUID) { + guard let index = items.firstIndex(where: { $0.id == id }) else { return } + if current != nil { + if current == index { + current = nil + } else if index < current! { + current = current! - 1 + } + } + deleteSnapshot(items[index].id) + items.remove(at: index) + } + + public func clear() { + items = [] + current = nil + queue.async { + if FileManager.default.fileExists(atPath: FileManager.snapshots.path) { + try? FileManager.default.removeItem(at: FileManager.snapshots) + } + } + } + + public func update(_ tab: UUID, page: Page) { + items.firstIndex { $0.id == tab }.map { + items[$0].page = page + } + } + + public func page(_ tab: UUID) -> Page? { + items.first { $0.id == tab }?.page + } + + public func image(_ id: UUID, completion: @escaping (Data?) -> Void) { + queue.async { + let data = try? Data(contentsOf: FileManager.snapshots.appendingPathComponent(id.uuidString)) + DispatchQueue.main.async { + completion(data) + } + } + } + + public func save(_ image: Data, with: UUID) { + queue.async { + if !FileManager.default.fileExists(atPath: FileManager.snapshots.path) { + var url = FileManager.snapshots + var resources = URLResourceValues() + resources.isExcludedFromBackup = true + try? url.setResourceValues(resources) + try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) + } + try? image.write(to: FileManager.snapshots.appendingPathComponent(with.uuidString), options: .atomic) + } + } + + func deleteSnapshot(_ id: UUID) { + queue.async { + if FileManager.default.fileExists(atPath: FileManager.snapshots.appendingPathComponent(id.uuidString).path) { + try? FileManager.default.removeItem(at: FileManager.snapshots.appendingPathComponent(id.uuidString)) + } + } + } +} diff --git a/firefox-ios/Ecosia/Core/TimeInterval+Extensions.swift b/firefox-ios/Ecosia/Core/TimeInterval+Extensions.swift new file mode 100644 index 0000000000000..091cbf1676e45 --- /dev/null +++ b/firefox-ios/Ecosia/Core/TimeInterval+Extensions.swift @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension TimeInterval { + + static let twentyFourHoursTimeInterval: TimeInterval = 24*60*60 +} diff --git a/firefox-ios/Ecosia/Core/TimestampProvider.swift b/firefox-ios/Ecosia/Core/TimestampProvider.swift new file mode 100644 index 0000000000000..8c4ddc5f5bfa3 --- /dev/null +++ b/firefox-ios/Ecosia/Core/TimestampProvider.swift @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol TimestampProvider { + var currentTimestamp: TimeInterval { get } +} diff --git a/firefox-ios/Ecosia/Core/URL+Extensions.swift b/firefox-ios/Ecosia/Core/URL+Extensions.swift new file mode 100644 index 0000000000000..66ce8c69acedd --- /dev/null +++ b/firefox-ios/Ecosia/Core/URL+Extensions.swift @@ -0,0 +1,158 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension URL { + + public enum EcosiaQueryItemName: String { + case + page = "p", + query = "q", + typeTag = "tt", + userId = "_sp" + } + + public enum EcosiaSearchVertical: String, CaseIterable { + case search + case images + case news + case videos + + init?(path: String) { + let pathWithNoLeadingSlash = String(path.dropFirst()) + self.init(rawValue: pathWithNoLeadingSlash) + } + } + + public static func ecosiaSearchWithQuery(_ query: String, urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> URL { + var components = URLComponents(url: urlProvider.root, resolvingAgainstBaseURL: false)! + components.path = "/search" + components.queryItems = [item(name: .query, value: query), item(name: .typeTag, value: "iosapp")] + return components.url! + } + + /// Check whether the URL being browsed will present the SERP out of a search or a search suggestion + public func isEcosiaSearchQuery(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> Bool { + guard isEcosia(urlProvider), + let components = URLComponents(url: self, resolvingAgainstBaseURL: false) else { + return false + } + return components.path == "/search" + } + + public func isEcosiaSearchVertical(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> Bool { + getEcosiaSearchVerticalPath(urlProvider) != nil + } + + public func getEcosiaSearchVerticalPath(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> String? { + guard isEcosia(urlProvider), + let components = components else { + return nil + } + return EcosiaSearchVertical(path: components.path)?.rawValue + } + + public func getEcosiaSearchQuery(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> String? { + guard isEcosia(urlProvider), + let components = components else { + return nil + } + return components.queryItems?.first(where: { + $0.name == EcosiaQueryItemName.query.rawValue + })?.value + } + + public func getEcosiaSearchPage(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> Int? { + guard isEcosia(urlProvider), + let components = components else { + return nil + } + if let pageNumber = components.queryItems?.first(where: { + $0.name == EcosiaQueryItemName.page.rawValue + })?.value { + return Int(pageNumber) + } + return nil + } + + /// Check whether the URL should be Ecosified. At the moment this is true for every Ecosia URL. + public func shouldEcosify(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> Bool { + return isEcosia(urlProvider) + } + + public func ecosified(isIncognitoEnabled: Bool, urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> URL { + guard isEcosia(urlProvider), + var components = components + else { return self } + + // Remove existing userId if present + components.queryItems?.removeAll(where: { $0.name == EcosiaQueryItemName.userId.rawValue }) + + /* + The `sendAnonymousUsageData` is set by the native UX component in settings + that determines whether the app would send the events to Snowplow. + To align the business logic, this parameter will also function as a condition + that decides whether we would send our AnalyticsID as query paramter for + searches. In this scenario thuogh, the naming is a bit misleanding, thus + checking for the negative evaluation of it. + */ + let shouldAnonymizeUserId = isIncognitoEnabled || + !User.shared.hasAnalyticsCookieConsent || + !User.shared.sendAnonymousUsageData + let userId = shouldAnonymizeUserId ? UUID(uuid: UUID_NULL).uuidString : User.shared.analyticsId.uuidString + + guard let urlWithoutUserId = components.url else { return self } + return urlWithoutUserId.appendingQueryItems([Self.item(name: .userId, value: userId)]) + } + + public var policy: Scheme.Policy { + (scheme + .flatMap(Scheme.init(rawValue:)) ?? .other) + .policy + } + + private subscript(_ itemName: EcosiaQueryItemName) -> String? { + components?.queryItems?.first { $0.name == itemName.rawValue }?.value + } + + public func isBrowser() -> Bool { + scheme.flatMap(Scheme.init(rawValue:))?.isBrowser ?? false + } + + public func isEcosia(_ urlProvider: URLProvider = EcosiaEnvironment.current.urlProvider) -> Bool { + let hasURLProviderDomainSuffix = host?.hasSuffix(urlProvider.domain) == true + return isBrowser() && hasURLProviderDomainSuffix + } + + private var components: URLComponents? { + URLComponents(url: self, resolvingAgainstBaseURL: false) + } + + private static func item(name: EcosiaQueryItemName, value: String) -> URLQueryItem { + .init(name: name.rawValue, value: value) + } + + /// Appends query items to a URL in a version-safe way. + /// - Parameter queryItems: The query items to append to the URL + /// - Returns: A new URL with the query items appended + /// - Note: This method can be removed once the minimum deployment target is iOS 16+, + /// as URL.append(queryItems:) is available natively from iOS 16. + public func appendingQueryItems(_ queryItems: [URLQueryItem]) -> URL { + if #available(iOS 16.0, *) { + var url = self + url.append(queryItems: queryItems) + return url + } else { + guard var components = URLComponents(url: self, resolvingAgainstBaseURL: false) else { + return self + } + if components.queryItems == nil { + components.queryItems = [] + } + components.queryItems?.append(contentsOf: queryItems) + return components.url ?? self + } + } +} diff --git a/firefox-ios/Ecosia/Core/URLRequest+Extensions.swift b/firefox-ios/Ecosia/Core/URLRequest+Extensions.swift new file mode 100644 index 0000000000000..1810e69aebf44 --- /dev/null +++ b/firefox-ios/Ecosia/Core/URLRequest+Extensions.swift @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension URLRequest { + + public mutating func withCloudFlareAuthParameters(environment: Environment = EcosiaEnvironment.current) -> URLRequest { + if let auth = environment.cloudFlareAuth { + setValue(auth.id, forHTTPHeaderField: CloudflareKeyProvider.clientId) + setValue(auth.secret, forHTTPHeaderField: CloudflareKeyProvider.clientSecret) + } + return self + } + + /// This function provides an additional HTTP request header when loading SERP through native UI (i.e. submitting a search) + /// to help SERP decide which market to serve. + public mutating func addLanguageRegionHeader() { + setValue(Locale.current.identifierWithDashedLanguageAndRegion, forHTTPHeaderField: "x-ecosia-app-language-region") + } +} diff --git a/firefox-ios/Ecosia/Core/URLSessionConfiguration+Extensions.swift b/firefox-ios/Ecosia/Core/URLSessionConfiguration+Extensions.swift new file mode 100644 index 0000000000000..2524528937973 --- /dev/null +++ b/firefox-ios/Ecosia/Core/URLSessionConfiguration+Extensions.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +extension URLSessionConfiguration { + + public func withCloudFlareAuthParameters(environment: Environment = EcosiaEnvironment.current) -> URLSessionConfiguration { + if let auth = environment.cloudFlareAuth { + let headers = [ + [CloudflareKeyProvider.clientId]: auth.secret, + [CloudflareKeyProvider.clientSecret]: auth.id + ] + httpAdditionalHeaders = headers + } + return self + } +} diff --git a/firefox-ios/Ecosia/Core/User.swift b/firefox-ios/Ecosia/Core/User.swift new file mode 100644 index 0000000000000..bfc00d36021bc --- /dev/null +++ b/firefox-ios/Ecosia/Core/User.swift @@ -0,0 +1,307 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UserNotifications +import Combine + +extension Notification.Name { + static let searchesCounterChanged = Notification.Name("searchesCounterChanged") + public static let searchSettingsChanged = Notification.Name("searchSettingsChanged") +} + +public struct User: Codable, Equatable { + public static var shared = User() { + didSet { + guard shared != oldValue else { return } + shared.save() + + if shared.hasNewSearchSetting(compared: oldValue) { + DispatchQueue.main.async { + NotificationCenter.default.post(name: .searchSettingsChanged, object: nil) + } + } + } + } + + // MARK: Search Settings + public var marketCode = Local.make(for: .current) + public var adultFilter = AdultFilter.moderate + public var autoComplete = true + public var personalized = false + public var aiOverviews = true + + // MARK: Privacy Settings + public var sendAnonymousUsageData = true + public internal(set) var cookieConsentValue: String? + public var hasAnalyticsCookieConsent: Bool { + guard let cookieConsentValue else { + return false + } + return cookieConsentValue.contains("a") + } + + // MARK: NTP Customization + public var showTopSites = true + public var topSitesRows = 4 + public var showClimateImpact = true + public var showEcosiaNews = true + + // MARK: Install + public var install = Date() + public var analyticsId = UUID() + public var versionOnInstall = "0.0.0" + public var firstTime = true + + // MARK: Other + public var news = Date.distantPast + public var migrated = false + public var referrals = Referrals.Model() + public var seedCount: Int { + EcosiaAuthUIStateProvider.shared.seedCount + } + public internal(set) var id: String? + public var whatsNewItemsVersionsShown = Set() + public internal(set) var analyticsUserState = AnalyticsStateContext() + + public var searchCount = 0 { + didSet { + guard oldValue != searchCount else { return } + DispatchQueue.main.async { + NotificationCenter.default.post(name: .searchesCounterChanged, object: nil) + } + } + } + + var state = [String: String]() + + private enum CodingKeys: String, CodingKey { + case + install, + versionOnInstall, + news, + analyticsId, + marketCode, + adultFilter, + autoComplete, + firstTime, + personalized, + aiOverviews, + sendAnonymousUsageData, + topSitesRows, + showClimateImpact, + showEcosiaNews, + migrated, + referrals, + id, + state, + cookieConsentValue, + whatsNewItemsVersionsShown, + analyticsUserState + // Reusing previous decoding keys + case searchCount = "treeCount" + case showTopSites = "topSites" + } + + public init(from decoder: Decoder) throws { + let root = try decoder.container(keyedBy: CodingKeys.self) + install = (try? root.decode(Date.self, forKey: .install)) ?? .init() + versionOnInstall = (try? root.decode(String.self, forKey: .versionOnInstall)) ?? "0.0.0" + news = (try? root.decode(Date.self, forKey: .news)) ?? .distantPast + analyticsId = (try? root.decode(UUID.self, forKey: .analyticsId)) ?? .init() + marketCode = (try? root.decode(Local.self, forKey: .marketCode)) ?? Local.make(for: .current) + adultFilter = (try? root.decode(AdultFilter.self, forKey: .adultFilter)) ?? .moderate + autoComplete = (try? root.decode(Bool.self, forKey: .autoComplete)) ?? true + firstTime = (try? root.decode(Bool.self, forKey: .firstTime)) ?? true + personalized = (try? root.decode(Bool.self, forKey: .personalized)) ?? false + aiOverviews = (try? root.decode(Bool.self, forKey: .aiOverviews)) ?? true + sendAnonymousUsageData = (try? root.decode(Bool.self, forKey: .sendAnonymousUsageData)) ?? true + topSitesRows = (try? root.decode(Int.self, forKey: .topSitesRows)) ?? 4 + showTopSites = (try? root.decode(Bool.self, forKey: .showTopSites)) ?? true + showClimateImpact = (try? root.decode(Bool.self, forKey: .showClimateImpact)) ?? true + showEcosiaNews = (try? root.decode(Bool.self, forKey: .showEcosiaNews)) ?? true + migrated = (try? root.decode(Bool.self, forKey: .migrated)) ?? false + referrals = (try? root.decode(Referrals.Model.self, forKey: .referrals)) ?? .init() + id = try? root.decode(String.self, forKey: .id) + searchCount = (try? root.decode(Int.self, forKey: .searchCount)) ?? 0 + state = (try? root.decode([String: String].self, forKey: .state)) ?? [:] + cookieConsentValue = try? root.decode(String.self, forKey: .cookieConsentValue) + whatsNewItemsVersionsShown = (try? root.decode(Set.self, forKey: .whatsNewItemsVersionsShown)) ?? [] + analyticsUserState = (try? root.decode(AnalyticsStateContext.self, forKey: .analyticsUserState)) ?? .init() + } + + init() { + if let stored = self.stored { + id = stored.id + adultFilter = stored.adultFilter + marketCode = stored.marketCode + searchCount = stored.searchCount + autoComplete = stored.autoComplete + firstTime = stored.firstTime + analyticsId = stored.analyticsId + personalized = stored.personalized + aiOverviews = stored.aiOverviews + sendAnonymousUsageData = stored.sendAnonymousUsageData + migrated = stored.migrated + state = stored.state + news = stored.news + topSitesRows = stored.topSitesRows + showTopSites = stored.showTopSites + showClimateImpact = stored.showClimateImpact + showEcosiaNews = stored.showEcosiaNews + referrals = stored.referrals + install = stored.install + versionOnInstall = stored.versionOnInstall + cookieConsentValue = stored.cookieConsentValue + whatsNewItemsVersionsShown = stored.whatsNewItemsVersionsShown + analyticsUserState = stored.analyticsUserState + } else { + save() + } + } + + private var stored: User? { + try? JSONDecoder().decode(User.self, from: .init(contentsOf: FileManager.user)) + } + + static let queue = DispatchQueue(label: "", qos: .utility) + private func save() { + let user = self + User.queue.async { + try? JSONEncoder().encode(user).write(to: FileManager.user, options: .atomic) + } + } +} + +// MARK: Helper methods +extension User { + + public var showsReferralSpotlight: Bool { + guard install < Calendar.current.date(byAdding: .day, value: -3, to: .init())! else { return false } + return state[Key.referralSpotlight.rawValue].map(Bool.init) != false + } + + public var showsInactiveTabsTooltip: Bool { + state[Key.inactiveTabsTooltip.rawValue].map(Bool.init) != false + } + + public var showsBookmarksImportExportTooltip: Bool { + state[Key.bookmarksImportExportTooltipShown.rawValue].map(Bool.init) != false + } + + public var shouldShowImpactIntro: Bool { + state[Key.impactIntro.rawValue].map(Bool.init) != false + } + + public mutating func hideImpactIntro() { + state[Key.impactIntro.rawValue] = "\(false)" + } + + public mutating func showImpactIntro() { + state[Key.impactIntro.rawValue] = "\(true)" + } + + public mutating func hideReferralSpotlight() { + state[Key.referralSpotlight.rawValue] = "\(false)" + } + + public mutating func showInactiveTabsTooltip() { + state[Key.inactiveTabsTooltip.rawValue] = "\(true)" + } + + public mutating func hideInactiveTabsTooltip() { + state[Key.inactiveTabsTooltip.rawValue] = "\(false)" + } + + public mutating func hideBookmarksImportExportTooltip() { + state[Key.bookmarksImportExportTooltipShown.rawValue] = "\(false)" + } + + public var shouldShowDefaultBrowserSettingNudgeCard: Bool { + state[Key.isDefaultBrowserSettingNudgeCardShown.rawValue].map(Bool.init) != true + } + + public mutating func showDefaultBrowserSettingNudgeCard() { + state[Key.isDefaultBrowserSettingNudgeCardShown.rawValue] = "\(false)" + } + + public mutating func hideDefaultBrowserSettingNudgeCard() { + state[Key.isDefaultBrowserSettingNudgeCardShown.rawValue] = "\(true)" + } + + public var shouldShowAccountImpactNudgeCard: Bool { + state[Key.isAccountImpactNudgeCardDismissed.rawValue].map(Bool.init) != true + } + + public mutating func showAccountImpactNudgeCard() { + state[Key.isAccountImpactNudgeCardDismissed.rawValue] = "\(false)" + } + + public mutating func hideAccountImpactNudgeCard() { + state[Key.isAccountImpactNudgeCardDismissed.rawValue] = "\(true)" + } + + enum Key: String { + case + referralSpotlight, + impactIntro = "counterIntro", // Reusing previous key + inactiveTabsTooltip, + bookmarksImportExportTooltipShown, + isNewUserSinceBookmarksImportExportHasBeenShipped, + isDefaultBrowserSettingNudgeCardShown, + isAccountImpactNudgeCardDismissed + } +} + +// MARK: Search Setting Helper +extension User { + private struct SearchSetting: Equatable { + let marketCode: Local + let adultFilter: AdultFilter + let autoComplete: Bool + let personalized: Bool + let aiOverviews: Bool + } + + private var searchSetting: SearchSetting { + .init(marketCode: marketCode, + adultFilter: adultFilter, + autoComplete: autoComplete, + personalized: personalized, + aiOverviews: aiOverviews) + } + + func hasNewSearchSetting(compared to: User) -> Bool { + searchSetting != to.searchSetting + } +} + +// MARK: User state context +extension User { + + /// Mimics the high level push notification states + public enum PushNotificationState: String, Codable { + case enabled + case disabled + case notDetermined = "not_determined" + } + + /// The values to pass into the Analytics User State Dedicated context + public struct AnalyticsStateContext: Codable, Equatable { + var pushNotificationState: PushNotificationState = .notDetermined + + enum CodingKeys: String, CodingKey { + case pushNotificationState = "push_notification_state" + } + } + + /// Updates the Analytics' User State given the current App's User Notification's status + public mutating func updatePushNotificationUserStateWithAnalytics(from status: UNAuthorizationStatus) { + analyticsUserState.pushNotificationState = switch status { + case .authorized, .ephemeral, .provisional: .enabled + case .denied: .disabled + default: .notDetermined + } + } +} diff --git a/firefox-ios/Ecosia/Core/UserDefaults+ObjectPersister.swift b/firefox-ios/Ecosia/Core/UserDefaults+ObjectPersister.swift new file mode 100644 index 0000000000000..6db3b98f47e27 --- /dev/null +++ b/firefox-ios/Ecosia/Core/UserDefaults+ObjectPersister.swift @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension UserDefaults: ObjectPersister {} diff --git a/firefox-ios/Ecosia/Ecosia.docc/Ecosia.md b/firefox-ios/Ecosia/Ecosia.docc/Ecosia.md new file mode 100644 index 0000000000000..eda0b878299fe --- /dev/null +++ b/firefox-ios/Ecosia/Ecosia.docc/Ecosia.md @@ -0,0 +1,304 @@ +# 🌳 Ecosia for iOS + + + + + + + +
Ecosia-iOSEcosia-iOSEcosia-iOS
+ +This is the entry point of all-things Ecosia. +It contains info on the way we got the project structure, how we interface it with Firefox, how we release and keep localizations aligned. + +## 🧰 Ecosia Framework + +The Ecosia Framework aims to be a wrapper of all our Ecosia isolated implementation and logic. +Some of the Ecosia codebase still lives under the main project `Client/Ecosia` but the goal is to bring as much codebase as possible as part of this dedicated framework. + +## 🤝 Getting involved + +We encourage you to participate in those open source projects. We love Pull Requests, Issue Reports, Feature Requests or any kind of positive contribution. Please read the [Mozilla Community Participation Guidelines](https://www.mozilla.org/en-US/about/governance/policies/participation/) and our [Contributing guidelines](https://github.com/mozilla-mobile/firefox-ios/blob/main/CONTRIBUTING.md) first. + +- You can [file a new issue](https://github.com/mozilla-mobile/firefox-ios/issues/new/choose) or research [existing bugs](https://github.com/mozilla-mobile/firefox-ios/issues) + +If more information is required or you have any questions then we suggest reaching out to us via: +- Chat on Element channel [#fx-ios](https://chat.mozilla.org/#/room/#fx-ios:mozilla.org) and [#focus-ios](https://chat.mozilla.org/#/room/#focus-ios:mozilla.org) for general discussion, or write DMs to specific teammates for questions. +- Open a [Github discussion](https://github.com/mozilla-mobile/firefox-ios/discussions) which can be used for general questions. + +Want to contribute on the codebase but don't know where to start? Here is a list of [issues that are contributor friendly](https://github.com/mozilla-mobile/firefox-ios/labels/Contributor%20OK), but make sure to read the [Contributing guidelines](https://github.com/mozilla-mobile/firefox-ios/blob/main/CONTRIBUTING.md) first. + +### ⁒ Update Ecosia Comments + +To ensure consistency when commenting code in Firefox for Ecosia updates, you could document the following approach: + +Commenting Guidelines for Ecosia Code in Firefox: + 1. One-liner Comments: +Use `//` for introducing new code or brief explanations. + +``` +// Ecosia: Update appversion predicate +let appVersionPredicate = (appVersionString?.contains("Ecosia") ?? false) == true +``` + + 2. Block Comments: +Use `/* */` when commenting out existing Firefox code for easier readability and conflict resolution. + +``` +/* Ecosia: Update appversion predicate +let appVersionPredicate = (appVersionString?.contains("Firefox") ?? false) == true +*/ +let appVersionPredicate = (appVersionString?.contains("Ecosia") ?? false) == true +``` + +### After cloning (for Ecosians) +----------- + +#### 🪝 Git Hooks + +This project uses custom Git hooks to enforce commit message formatting and other automated tasks. +To ensure that these hooks are installed correctly in your local `.git/hooks` directory, you need to run the provided setup script after cloning the repository. + +- Navigate into the project directory +- Run the setup script to install the Git hooks: `./setup_hooks.sh` + +This script will copy all the necessary hooks (such as `prepare-commit-msg`) to your local `.git/hooks` directory, ensuring they are executable. + +## ⚙️ Building the code + +### 🧼 SwiftLint + +We use [SwiftLint](https://github.com/realm/SwiftLint) to enforce Swift style and conventions. Make sure to install it so that linting runs correctly when building. + +```shell +brew install swiftlint +``` + +1. Install the latest [Xcode developer tools](https://developer.apple.com/xcode/downloads/) from Apple. +2. Install, [Brew](https://brew.sh), Node, and a Python3 virtualenv for localization scripts: + ```shell + brew update + brew install node + pip3 install virtualenv + ``` +3. Clone the repository: + ```shell + git clone https://github.com/ecosia/ios-browser + ``` +4. Install Node.js dependencies, build user scripts and update content blocker: + ```shell + cd ios-browser + sh ./bootstrap.sh + ``` +5. Open `firefox-ios/Client.xcodeproj` in Xcode. +6. Make sure to select the `Ecosia` [scheme](https://developer.apple.com/documentation/xcode/build-system?changes=_2) in Xcode. +7. Select the destination device you want to build on. +8. Run the app with `Cmd + R` or by pressing the `build and run` button. + +⚠️ Important: In case you have dependencies issues with SPM, please try the following: +- Xcode -> File -> Packages -> Reset Package Caches +- This will also require you to have a working github integration set up in xcode (see Settings > Accounts > Source Control Accounts) + +### 📝 Building User Scripts +----------------- + +User Scripts (JavaScript injected into the `WKWebView`) are compiled, concatenated, and minified using [webpack](https://webpack.js.org/). User Scripts to be aggregated are placed in the following directories: + +```none +/Client +|-- /Frontend + |-- /UserContent + |-- /UserScripts + |-- /AllFrames + | |-- /AtDocumentEnd + | |-- /AtDocumentStart + |-- /MainFrame + |-- /AtDocumentEnd + |-- /AtDocumentStart +``` + +This reduces the total possible number of User Scripts down to four. The compiled output from concatenating and minifying the User Scripts placed in these folders resides in `/Client/Assets` and are named accordingly: + +* `AllFramesAtDocumentEnd.js` +* `AllFramesAtDocumentStart.js` +* `MainFrameAtDocumentEnd.js` +* `MainFrameAtDocumentStart.js` + +To simplify the build process, these compiled files are checked-in to this repository. When adding or editing User Scripts, these files can be re-compiled with `webpack` manually. This requires Node.js to be installed, and all required `npm` packages can be installed by running `npm install` in the project's root directory. User Scripts can be compiled by running the following `npm` command in the root directory of the project: + +```shell +npm run build +``` + +The `CURRENT_PROJECT_VERSION` being set to `0` indicates that it is not being used for local testing. The outcoming build number is updated by the CI, matching the CI run number (e.g. `8023`). + +## 🏅 Get certificates and profiles + +``` +brew install fastlane +``` + +Our certs and profiles are managed centrally by [fastlane match](https://docs.fastlane.tools/actions/match/). Find the repo [here](https://github.com/ecosia/IosSearchSigning) + +You might need to set up your Ruby stack: +``` +sudo gem install bundler:2.3.4 +bundle install + +# you may need to link your ruby if you have a ruby from brew +echo 'export PATH="/opt/homebrew/opt/ruby/bin:$PATH"' >> ~/.zshrc + +# you may need to install a ruby <4 +# https://formulae.brew.sh/formula/rbenv +brew install rbenv +echo 'eval "$(rbenv init -)"' >> ~/.zshrc +source ~/.zshrc + +rbenv install 3.2.2 +rbenv global 3.2.2 + +``` +Run `bundle exec fastlane match --readonly` to add certs and profiles to your system. You can append `-p "keychain password"` to avoid keychain prompts during the process. The passphrase to decrypt the repo can be found in LastPass. + +**Command:** +```shell +bundle exec fastlane match --readonly +``` + +**Expected output:** +```text +[...] +[14:22:17]: Passphrase for Match storage: [get the password from the password manager] +``` + +### Registering new devices + +To run the app on a new device, register it on the Apple Developer Portal and re-generate the provisioning profiles using `fastlane match`. + +1. Plug in your device and register it via the [`register_devices`](https://docs.fastlane.tools/actions/register_devices/) action. You will be prompted for the device name and UDID: + ```shell + bundle exec fastlane run register_devices + ``` +2. Re-generate the provisioning profiles to include the new device by running `match` with `--force_for_new_devices` for both **development** (for local development) and **ad hoc** (for Firebase releases): + ```shell + bundle exec fastlane match development --force_for_new_devices + bundle exec fastlane match adhoc --force_for_new_devices + ``` + This flag makes `match` check whether the device count has changed since the last run and automatically re-generate the provisioning profiles if necessary. See the [fastlane match docs](https://docs.fastlane.tools/actions/match/#registering-new-devices) for more details. +3. Open Xcode, select the **EcosiaBeta** (or **Ecosia**) scheme, choose your device and run (`Cmd + R`). + +## 🗣️ Translations + +We are using [Transifex](https://docs.transifex.com/client/introduction) for managing our translations. + +### Install the transifex client using pip + +```bash +curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash +``` + +#### Configure your `~/.transifexrc` file + +```bash +[https://www.transifex.com] +api_hostname = https://api.transifex.com +hostname = https://www.transifex.com +username = +password = +rest_hostname = https://rest.api.transifex.com +token = +``` + +### Translations need to be pulled and commited manually + +Pulling translation from the web + +```bash +tx pull -fs +``` + +Test and commit the new translations. There exists schemes for testing other languages in the simulator. + +### Adding new strings + +#### Via CLI + +1. Pull the source file +2. Add the new strings to the English source file `Client/Ecosia/L10N/en.lproj/Ecosia.strings` +3. Push it to Transifex + +```bash +tx pull -fs +tx push -s +``` + +### Ecosify Mozilla Strings (only needed after upgrade) + +We do a rebrand of the Strings from Mozilla. Usually this step is only needed after an upgrade as we keep our changes in version control (as of opposite to Mozilla). +Firefox already imports and versions their strings, which means they will have been added to our codebase once we rebase. +After that, you can use the existing python script to update all strings on the folder containing the project file. + +```bash +# brand all the files as they contain the term 'Firefox' a lot +python3 ecosify-strings.py firefox-ios +``` + +## 🚀 Release + +Follow the instructions from our [confluence page](https://ecosia.atlassian.net/wiki/spaces/MOB/pages/2460680288/How+to+release) + +### How to update the release notes + +Make sure that `fastlane` and `transifex`-cli is installed. + +### Add source release notes to transifex (en-US) + +> ℹ️ Updating the source file in the project and merging it into `main` will automatically push it to Transifex as well since the Github integration is in place. + +> 🔔 Make sure that an _inflight_ version exists in AppStore Connect. If not, create one. + +- Create a new branch off `main` and modify the English release notes [here](/fastlane/metadata/en-US/release_notes.txt) +- Open a PR with the modified English release note text file against `main` branch +- Once approved, *Squash and Merge* the code to `main`. (The transifex integration will pick up the push) +- Transifex will create a PR and update it with the release notes in all available languages :hourglass_flowing_sand: +- *Squash and Merge* the code to `main` via a PR and a GitHubAction workflow will be triggered to upload the newly translated release notes + +### Add language translations + +- Make sure that all languages are translated in the transifex [web interface](https://app.transifex.com/ecosia/ecosia-ios-search-app/release_notestxt/) and found their way to `main` + +- Verify the translations in the Transifex-made PR + +- Squash and Merge the PR + +- The GitHub Action Workflow `Upload release notes to AppStore` will take care of the upload + +#### In case you need a manual update + +- Push via the update translation via `deliver` to the AppStore + + ```bash + bundle exec fastlane deliver --app-version 8.2.0 + ``` + +## 🧪 Unit tests + +* Run tests against `EcosiaBeta` scheme. With the standard CMD+U it picks the test plan (Xcode) + +## ✅ Acceptance testing + +Check https://github.com/ecosia/mobile-acceptance-testing for details + +## 📸 Snapshot Testing + +We built our snapshot testing setup with `SnapshotTestHelper` to streamline UI checks. Here’s the gist: + +- **Dynamic Setup**: We create UI components on-the-fly for testing, ensuring they're set up with current data and state. + +- **Config Flexibility**: The tool handles multiple themes and devices, simulating how UI looks across different environments. + +- **Localization**: It supports testing in various languages by adjusting the app’s locale dynamically, crucial for ensuring the UI displays correctly in all supported languages. + +- **Comparison**: We capture snapshots of the UI and compare them to reference images to spot any unintended changes. + +More details [here](SNAPSHOT_TESTING_WIKI.md) diff --git a/firefox-ios/Ecosia/Ecosia.docc/SNAPSHOT_TESTING_WIKI.md b/firefox-ios/Ecosia/Ecosia.docc/SNAPSHOT_TESTING_WIKI.md new file mode 100644 index 0000000000000..54cc50a12f901 --- /dev/null +++ b/firefox-ios/Ecosia/Ecosia.docc/SNAPSHOT_TESTING_WIKI.md @@ -0,0 +1,377 @@ +# 📸 Snapshot Testing Library + +The SnapshotTesting library is a Swift package that allows you to capture screenshots of iOS views and compare them over time, ensuring your UI does not change unexpectedly. It is highly effective in preventing visual regressions during development. +[Repo link](https://github.com/pointfreeco/swift-snapshot-testing?tab=readme-ov-file) + +## SnapshotTestHelper + +SnapshotTestHelper is a utility class designed to facilitate snapshot testing across different UI themes, device configurations, and locales for both UIView and UIViewController. It abstracts complex snapshot configurations and provides a simplified API for performing localized snapshot tests. + +### Key Functions a.k.a. what happens under the hood 👀 + +#### performSnapshot + +- **Purpose**: Executes the snapshot test with specified configurations. +- **Parameters**: + - `initializer`: A closure that returns the UI component to be tested. + - `locales`: An array of locales to test the component in different languages. + - `wait`: Duration to wait before taking the snapshot, allowing UI to stabilize. + - `precision`: The accuracy of the snapshot comparison. + - `file`, `testName`, `line`: Standard XCTest parameters for identifying the test source. + +The device name, orientation, and locales of the current test run are retrieved from the `environment.json` file. + +#### assertSnapshot + +- **Purpose**: Public interfaces for asserting snapshots of UIView and UIViewController. +- **Parameters**: + - Includes parameters for initializing content, device simulation, locale settings, and test configurations. + +#### setLocale + +- **Purpose**: Sets the application’s locale to simulate different languages. +- **Implementation Details**: Updates the UserDefaults to reflect the chosen locale and swaps the main Bundle to use localized resources. + +#### setupContent + +- **Purpose**: Configures the UIWindow with the content to be snapshot. +- **Details**: Adds the content to the window, sets appropriate bounds, and ensures it is ready for display and snapshotting. + +### Snapshot Testing Configuration + +This JSON configuration is designed for automated snapshot testing of an iOS application. It defines the locales, devices, and specific test plans that should be used during the snapshot tests. + +#### Breakdown of the JSON Structure: + +#### 1. Locales (`"locales"`) +This array lists all the language locales that the app will be tested in. In this example, the locales are: +- English (`"en"`) +- Italian (`"it"`) +- German (`"de"`) +- Spanish (`"es"`) +- Dutch (`"nl"`) + +#### 2. Devices (`"devices"`) +This array specifies the devices on which the snapshot tests will be executed. Each device entry includes: +- **`"name"`**: The name of the device (e.g., `"iPhone SE (2nd generation)"`). +- **`"orientation"`**: The orientation in which the device should be tested (e.g., `"portrait"` or `"landscape"`). +- **`"os"`**: The operating system version to simulate (e.g., `"17.5"`). If `"os"` is not specified, the default would be used. +> Make sure the devices you are going to list down are present or available in the machine you are going to run the tests. + +#### 3. Test Bundles (`"testBundles"`) +This array defines the test bundles, which are groups of tests to be executed under the specified configurations. Each test plan contains: +- **`"name"`**: The name of the test bundle (e.g., `"EcosiaSnapshotTests"`). +- **`"testClasses"`**: An array of test classes to be executed within this plan. Each test class includes: + - **`"name"`**: The name of the test class (e.g., `"OnboardingTests"`). + - **`"devices"`**: An array specifying the devices this test class should run on. If `"all"` is specified, the test will run on all devices listed in the `"devices"` section. + - **`"locales"`**: An array specifying the locales this test class should be tested in. If `"all"` is specified, the test will run in all locales listed in the `"locales"` section. + +#### Purpose + +This JSON file configures the settings for automated snapshot tests, which involve capturing screenshots of the app’s UI to ensure it looks correct across various devices and locales. + +- **Locales**: Helps ensure that the app displays correctly in multiple languages. +- **Devices**: Ensures that the UI is responsive and correctly rendered on different screen sizes and orientations. +- **Test Plans**: Organizes which tests should run on which devices and in which locales, allowing for comprehensive testing without manually configuring each test. + +This configuration is typically consumed by a script or a test runner that uses this information to execute the specified tests, taking screenshots of the app's UI in the defined scenarios and comparing them to reference images to detect any unintended changes. + +### Localization Support 🗣️ + +SnapshotTestHelper can perform snapshots in various languages by dynamically setting the application’s locale before rendering the UI. This is particularly useful for apps supporting multiple languages, ensuring that all localized strings appear correctly in the UI across different device configurations. + +### Example Usage + +#### Testing a UIViewController (Welcome Screen) + +```swift +func testWelcomeScreen() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + Welcome(delegate: MockWelcomeDelegate()) + }, wait: 1.0) +} +``` + +This test initializes a Welcome view controller with a mock delegate and asserts its appearance in English by default against an iPhone12Pro form factor in portrait mode. + +#### Testing a UIView (NTPLogoCell) + +```swift +func testNTPLogoCell() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + NTPLogoCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + }) +} +``` + +This test captures a snapshot of the NTPLogoCell, a custom view, to ensure its visual layout remains consistent across updates. + +This documentation should help developers understand how to leverage SnapshotTestHelper for comprehensive UI testing, including handling different themes, devices, and languages. + +### FAQ ⁉️ + +

+ What if I want to test on different devices? + +To perform snapshot tests on different devices, you can specify the devices as part of the `snapshot_configuration.json`. +SnapshotTestHelper will take care of retrieving all the details and configure the test environment to simulate the screen size and resolution of the specified devices. + +**Example:** +```json +{ + "locales": [ + "en", + "it", + "de", + "es", + "nl" + ], + "devices": [ + { + "name": "iPhone SE (3rd generation)", + "orientation": "portrait", + "os": "17.5" + }, + { + "name": "iPhone 15 Pro", + "orientation": "portrait", + "os": "17.5", + "isDefaultTestDevice": true + }, + { + "name": "iPhone 15 Pro Max", + "orientation": "landscape" + }, + { + "name": "iPad Pro (12.9-inch) (6th generation)", + "orientation": "portrait" + } + ], + "testBundles": [ + { + "name": "EcosiaSnapshotTests", + "testClasses": [ + { + "name": "OnboardingTests", + "devices": ["all", "portrait"], + "locales": ["all"] + }, + { + "name": "NTPComponentTests", + "devices": ["iPhone 15 Pro"], + "locales": ["all"] + } + ] + } + ] +} +``` + +This configuration executes the `OnboardingTests` class which is part of the `EcosiaSnapshotTests` on both an iPhone SE and iPad Pro in portrait mode for the English and Spanish languages. +
+ +
+ What if I want to test with different languages? + +To perform snapshot tests on different locales, you can specify the devices as part of the `snapshot_configuration.json`. All available locales are declared in the `locales` array. +SnapshotTestHelper will take care of retrieving all the details and configure the test environment to the specified languages. + +```json +{ + "locales": [ + "en", + "it", + "de", + "es", + "nl" + ], + "devices": [ + { + "name": "iPhone SE (3rd generation)", + "orientation": "portrait", + "os": "17.5" + }, + { + "name": "iPhone 15 Pro", + "orientation": "portrait", + "os": "17.5", + "isDefaultTestDevice": true + }, + { + "name": "iPhone 15 Pro Max", + "orientation": "landscape" + }, + { + "name": "iPad Pro (12.9-inch) (6th generation)", + "orientation": "portrait" + } + ], + "testBundles": [ + { + "name": "EcosiaSnapshotTests", + "testClasses": [ + { + "name": "OnboardingTests", + "devices": [ + "iPhone SE (2nd generation)", + "iPad Pro (12.9-inch) (4th generation)" + ], + "locales": [ + "en", + "it", + "es" + ] + }, + { + "name": "NTPComponentTests", + "devices": ["iPhone 15 Pro"], + "locales": ["all"] + } + ] + } + ] +} +``` + +This configuration executes the `OnboardingTests` class which is part of the `EcosiaSnapshotTests` on both an iPhone SE and iPad Pro in portrait mode for the English, Italian and Spanish languages. +
+ +
+ What if I want to add another device in landscape orientation? + +Here’s how you can add a device in landscape orientation: + +In your DeviceType enum, ensure you have a landscape configuration set up for the device: + +```swift +enum DeviceType: String, CaseIterable { + case iPhone12Pro_Portrait + case iPhone12Pro_Landscape // Define the landscape configuration + + var config: ViewImageConfig { + switch self { + case .iPhone12Pro_Portrait: + return ViewImageConfig.iPhone12Pro(.portrait) + case .iPhone12Pro_Landscape: + return ViewImageConfig.iPhone12Pro(.landscape) + } + } + + static func from(deviceName: String, orientation: String) -> DeviceType { + switch (deviceName, orientation) { + case ("iPhone 12 Pro", "portrait"): + return .iPhone12Pro_Portrait + case ("iPhone 12 Pro", "landscape"): + return .iPhone12Pro_Landscape + default: + fatalError("Device Name \(deviceName) and Orientation \(orientation) not found. Please add them correctly.") + } + } +} +``` + +Do not forget to also declare the new device in the `snapshot_configuration.json` as well 👇 + +```json +{ + "locales": [ + "en", + "it", + "de", + "es", + "nl" + ], + "devices": [ + { + "name": "iPhone SE (2nd generation)", + "orientation": "portrait", + "os": "17.5" + }, + { + "name": "iPhone 12 Pro", + "orientation": "landscape", + "os": "17.5" + } + ... + ], + "testBundles": [ + { + "name": "EcosiaSnapshotTests", + "testClasses": [ + { + "name": "OnboardingTests", + "devices": ["all"], + "locales": ["all"] + } + ... + ] + } + ] +} +``` +
+ +
+ What if I want to perform a certain test class against all devices in portrait orientation? + +You can specify the requirements as part of the devices list of that test class. The script will take care of selecting only the devices matching the desired orientation. + +```json +{ + "locales": [ + "en", + "it", + "de", + "es", + "nl" + ], + "devices": [ + { + "name": "iPhone SE (2nd generation)", + "orientation": "portrait", + "os": "17.5" + }, + { + "name": "iPhone 15 Pro", + "orientation": "portrait", + "os": "17.5" + }, + { + "name": "iPhone 12 Pro", + "orientation": "landscape", + "os": "17.5" + } + ... + ], + "testBundles": [ + { + "name": "EcosiaSnapshotTests", + "testClasses": [ + { + "name": "OnboardingTests", + "devices": ["all", "portrait"], + "locales": ["all"] + } + ... + ] + } + ] +} +``` + +
+ +
+ How does the compare logic work? + +The SnapshotTesting library captures screenshots of your UI components and compares these images against reference images stored in your project. If a reference image does not exist, it is created on the first run, meaning the initial test will always “pass” by creating the needed baseline images. + +On subsequent test runs, the newly captured snapshot is compared pixel by pixel against the reference image. If differences are detected beyond the specified precision threshold, the test fails, and the differences can be reviewed visually in Xcode. This helps identify unintended changes or regressions in the UI layout and appearance. + +
+ +### Key Points 🎯: + +- Reference images are stored in your project directory under a folder typically named __Snapshots__. +- The precision parameter allows for control over how exact the comparison needs to be, accommodating minor rendering differences across environments. +- Failed tests will provide a visual diff image showing highlighted differences between the reference and the test snapshots. \ No newline at end of file diff --git a/firefox-ios/Ecosia/Ecosia.h b/firefox-ios/Ecosia/Ecosia.h new file mode 100644 index 0000000000000..84cd6108c21be --- /dev/null +++ b/firefox-ios/Ecosia/Ecosia.h @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +#import + +//! Project version number for Ecosia. +FOUNDATION_EXPORT double EcosiaVersionNumber; + +//! Project version string for Ecosia. +FOUNDATION_EXPORT const unsigned char EcosiaVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/firefox-ios/Ecosia/Entitlements/AppExtensions/Ecosia.entitlements b/firefox-ios/Ecosia/Entitlements/AppExtensions/Ecosia.entitlements new file mode 100644 index 0000000000000..e2a833b8bdbc4 --- /dev/null +++ b/firefox-ios/Ecosia/Entitlements/AppExtensions/Ecosia.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.com.ecosia.ecosiaapp + + keychain-access-groups + + $(AppIdentifierPrefix)com.ecosia.ecosiaapp + + + diff --git a/firefox-ios/Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements b/firefox-ios/Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements new file mode 100644 index 0000000000000..f989c1c3ce357 --- /dev/null +++ b/firefox-ios/Ecosia/Entitlements/AppExtensions/EcosiaBeta.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.com.ecosia.ecosiaapp.firefox + + keychain-access-groups + + $(AppIdentifierPrefix)com.ecosia.ecosiaapp.firefox + + + diff --git a/firefox-ios/Ecosia/Entitlements/Ecosia.entitlements b/firefox-ios/Ecosia/Entitlements/Ecosia.entitlements new file mode 100644 index 0000000000000..f2665f4942112 --- /dev/null +++ b/firefox-ios/Ecosia/Entitlements/Ecosia.entitlements @@ -0,0 +1,36 @@ + + + + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + iCloud.ecosia + + com.apple.developer.icloud-services + + CloudDocuments + + com.apple.developer.ubiquity-container-identifiers + + iCloud.ecosia + + com.apple.developer.web-browser + + com.apple.developer.browser.app-installation + + com.apple.security.application-groups + + group.com.ecosia.ecosiaapp + + keychain-access-groups + + $(AppIdentifierPrefix)com.ecosia.ecosiaapp + + com.apple.developer.associated-domains + + webcredentials:login.ecosia.org + + + diff --git a/firefox-ios/Ecosia/Entitlements/EcosiaBeta.entitlements b/firefox-ios/Ecosia/Entitlements/EcosiaBeta.entitlements new file mode 100644 index 0000000000000..a4ef133ae5b61 --- /dev/null +++ b/firefox-ios/Ecosia/Entitlements/EcosiaBeta.entitlements @@ -0,0 +1,36 @@ + + + + + com.apple.developer.icloud-container-identifiers + + iCloud.ecosia + + com.apple.developer.icloud-services + + CloudDocuments + + com.apple.developer.ubiquity-container-identifiers + + iCloud.ecosia + + com.apple.developer.web-browser + + com.apple.security.application-groups + + group.com.ecosia.ecosiaapp.firefox + + keychain-access-groups + + $(AppIdentifierPrefix)com.ecosia.ecosiaapp.firefox + + aps-environment + development + com.apple.developer.browser.app-installation + + com.apple.developer.associated-domains + + webcredentials:login.ecosia-staging.xyz + + + diff --git a/firefox-ios/Ecosia/Experiments/Unleash/AISearchMVPExperiment.swift b/firefox-ios/Ecosia/Experiments/Unleash/AISearchMVPExperiment.swift new file mode 100644 index 0000000000000..1bd3b6eeb647e --- /dev/null +++ b/firefox-ios/Ecosia/Experiments/Unleash/AISearchMVPExperiment.swift @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct AISearchMVPExperiment { + + private init() {} + + public static var isEnabled: Bool { + Unleash.isEnabled(.aiSearchMVP) && !isControl + } + + private static var variant: Unleash.Variant { + Unleash.getVariant(.aiSearchMVP) + } + + // More variants might be introduced, but control should remain the same + private static let controlVariantName: String = "st_3000_0" + public static var isControl: Bool { + variant.name == controlVariantName + } +} diff --git a/firefox-ios/Ecosia/Experiments/Unleash/BrazeIntegrationExperiment.swift b/firefox-ios/Ecosia/Experiments/Unleash/BrazeIntegrationExperiment.swift new file mode 100644 index 0000000000000..31e74c262eaf4 --- /dev/null +++ b/firefox-ios/Ecosia/Experiments/Unleash/BrazeIntegrationExperiment.swift @@ -0,0 +1,14 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct BrazeIntegrationExperiment { + + private init() {} + + static var isEnabled: Bool { + Unleash.isEnabled(.brazeIntegration) + } +} diff --git a/firefox-ios/Ecosia/Experiments/Unleash/OnboardingProductTourExperiment.swift b/firefox-ios/Ecosia/Experiments/Unleash/OnboardingProductTourExperiment.swift new file mode 100644 index 0000000000000..b505512397951 --- /dev/null +++ b/firefox-ios/Ecosia/Experiments/Unleash/OnboardingProductTourExperiment.swift @@ -0,0 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct OnboardingProductTourExperiment { + + private init() {} + + public static var isEnabled: Bool { + Unleash.isEnabled(.onboardingProductTour) && !isControl + } + + private static var variant: Unleash.Variant { + Unleash.getVariant(.onboardingProductTour) + } + + private static let controlVariantName: String = "control" + public static var isControl: Bool { + variant.name == controlVariantName + } +} diff --git a/firefox-ios/Ecosia/Extensions/AppInfo+Ecosia.swift b/firefox-ios/Ecosia/Extensions/AppInfo+Ecosia.swift new file mode 100644 index 0000000000000..e699228c12857 --- /dev/null +++ b/firefox-ios/Ecosia/Extensions/AppInfo+Ecosia.swift @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import AdServices +import Common + +extension AppInfo { + + public static var ecosiaAppVersion: String { + return applicationBundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String + } + + private static let hasAttributedAppleSearchDownloadKey = "hasAttributedAppleSearchDownloadKey" + + /// Only available for iOS 14.3 and later (will return nil on earlier versions). + /// Returns nil after the first time, so that no unwanted new token is generated. + /// If an error is caught, it will return nil and retry next time it is fetched. + static var adServicesAttributionToken: String? { + guard #available(iOS 14.3, *), + !UserDefaults.standard.bool(forKey: hasAttributedAppleSearchDownloadKey) else { + return nil + } + do { + let attributionToken = try AAAttribution.attributionToken() + UserDefaults.standard.set(true, forKey: hasAttributedAppleSearchDownloadKey) + return attributionToken + } catch { + return nil + } + } + + public static var installReceipt: String? { + + guard let receiptURL = Bundle.main.appStoreReceiptURL, let receiptData = try? Data(contentsOf: receiptURL) else { + return nil + } + + return receiptData.base64EncodedString(options: []) + } +} diff --git a/firefox-ios/Ecosia/Extensions/Bundle+Ecosia.swift b/firefox-ios/Ecosia/Extensions/Bundle+Ecosia.swift new file mode 100644 index 0000000000000..db0cd16eb208f --- /dev/null +++ b/firefox-ios/Ecosia/Extensions/Bundle+Ecosia.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Bundle { + public static var ecosia: Bundle { + Bundle(identifier: "com.ecosia.framework.Ecosia")! + } +} diff --git a/firefox-ios/Ecosia/Extensions/DeviceInfo+Ecosia.swift b/firefox-ios/Ecosia/Extensions/DeviceInfo+Ecosia.swift new file mode 100644 index 0000000000000..04b9ce994ac6a --- /dev/null +++ b/firefox-ios/Ecosia/Extensions/DeviceInfo+Ecosia.swift @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import UIKit +import Common + +extension DeviceInfo { + + static let manufacturer = "apple" + + static var currentLocale: String { + Locale.current.identifier + } + + static var currentCountry: String? { + Locale.current.regionIdentifierLowercasedWithFallbackValue + } + + static var platform: String { + UIDevice.current.systemName + } + + static var osVersionNumber: String { + UIDevice.current.systemVersion + } + + static var osBuildNumber: String? { + + // e.g. Version 16.4 (Build 20E247) + let fullSystemVersionString = ProcessInfo().operatingSystemVersionString + + // Regex to extract the build version + let regex = try? NSRegularExpression(pattern: "\\(Build\\s(.*?)\\)", options: []) + + // Retrieve the check result + guard let match = regex?.firstMatch(in: fullSystemVersionString, + options: [], + range: NSRange(fullSystemVersionString.startIndex..., in: fullSystemVersionString)) else { + return nil + } + + // Retrieve the range + guard let range = Range(match.range(at: 1), in: fullSystemVersionString) else { + return nil + } + + // Retrieve the build number + // e.g 20E247 + return String(fullSystemVersionString[range]) + } + + static var deviceModelName: String { + UIDevice.current.name + } +} diff --git a/firefox-ios/Ecosia/Extensions/Task+Sleep.swift b/firefox-ios/Ecosia/Extensions/Task+Sleep.swift new file mode 100644 index 0000000000000..b85cc2cb8d535 --- /dev/null +++ b/firefox-ios/Ecosia/Extensions/Task+Sleep.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Task where Success == Never, Failure == Never { + /// Sleep for a given duration in seconds, with iOS 15 compatibility + /// When iOS 16+ becomes minimum, this can be replaced with Task.sleep(for: .seconds()) + static func sleep(duration: TimeInterval) async throws { + if #available(iOS 16.0, *) { + try await Task.sleep(for: .seconds(duration)) + } else { + try await Task.sleep(nanoseconds: UInt64(duration * 1_000_000_000)) + } + } +} diff --git a/firefox-ios/Ecosia/Extensions/View+Extensions.swift b/firefox-ios/Ecosia/Extensions/View+Extensions.swift new file mode 100644 index 0000000000000..4c421069b83d6 --- /dev/null +++ b/firefox-ios/Ecosia/Extensions/View+Extensions.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +/// A SwiftIUI extension that allows views to have a transparent background +public extension View { + func transparentScrolling() -> some View { + if #available(iOS 16.0, *) { + return scrollContentBackground(.hidden) + } else { + return onAppear { + UITextView.appearance().backgroundColor = .clear + } + } + } +} diff --git a/firefox-ios/Ecosia/FeatureManagement/FeatureManagement.swift b/firefox-ios/Ecosia/FeatureManagement/FeatureManagement.swift new file mode 100644 index 0000000000000..20cea51ac34d7 --- /dev/null +++ b/firefox-ios/Ecosia/FeatureManagement/FeatureManagement.swift @@ -0,0 +1,47 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +public struct FeatureManagement { + + // MARK: - Initialization + + private init() {} + + // MARK: - Configuration + + /// Fetches the feature configuration asynchronously. + public static func fetchConfiguration() async { + do { + try await start() + } catch { + debugPrint(error) + } + } + + // MARK: - Private Methods + + /// Starts the feature management process asynchronously. + /// + /// - Throws: An error if the feature management process encounters an issue. + @MainActor + private static func start() async throws { + Self.addRefreshingRules() + do { + try await _ = Unleash.start(env: .current, appVersion: AppInfo.ecosiaAppVersion) + } catch { + debugPrint(error) + } + } + + /// Adds refreshing rules for feature management. + private static func addRefreshingRules() { + UnleashRefreshConfigurator() + .withAppUpdateCheckRule(appVersion: AppInfo.ecosiaAppVersion) + .withDeviceRegionUpdateCheckRule() + .withTwentyFourHoursCacheExpirationRule() + } +} diff --git a/firefox-ios/Ecosia/Helpers/AppInfoProvider/AppVersionInfoProvider.swift b/firefox-ios/Ecosia/Helpers/AppInfoProvider/AppVersionInfoProvider.swift new file mode 100644 index 0000000000000..6158ef7f8e520 --- /dev/null +++ b/firefox-ios/Ecosia/Helpers/AppInfoProvider/AppVersionInfoProvider.swift @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol AppVersionInfoProvider { + var version: String { get } +} diff --git a/firefox-ios/Ecosia/Helpers/AppInfoProvider/DefaultAppVersionInfoProvider.swift b/firefox-ios/Ecosia/Helpers/AppInfoProvider/DefaultAppVersionInfoProvider.swift new file mode 100644 index 0000000000000..6b71cc62ed5ea --- /dev/null +++ b/firefox-ios/Ecosia/Helpers/AppInfoProvider/DefaultAppVersionInfoProvider.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +public struct DefaultAppVersionInfoProvider: AppVersionInfoProvider { + + public init() { } + + public var version: String { + return AppInfo.ecosiaAppVersion + } +} diff --git a/firefox-ios/Ecosia/Helpers/EcosiaInstallType/EcosiaInstallType+Extensions.swift b/firefox-ios/Ecosia/Helpers/EcosiaInstallType/EcosiaInstallType+Extensions.swift new file mode 100644 index 0000000000000..6c39cc534f1ed --- /dev/null +++ b/firefox-ios/Ecosia/Helpers/EcosiaInstallType/EcosiaInstallType+Extensions.swift @@ -0,0 +1,35 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// This extension provides functionality to evaluate the current Ecosia installation type based on the provided app version information. +extension EcosiaInstallType { + + /// Evaluates the current Ecosia installation type and updates it if necessary. + /// + /// - Parameters: + /// - versionProvider: An instance of `AppVersionInfoProvider` used to obtain the current app version. Defaults to `DefaultAppVersionProvider`.` + /// - storeUpgradeVersion: A Bool that defines whether the function should also store the value for the new upgraded version + /// + /// - Note: This function checks if it's not the user's first time and the current Ecosia installation type is unknown. If so, it sets the type to fresh and updates the current version. Additionally, it checks if the persisted version differs from the provided version and sets the type to upgrade while updating the current version. + /// + /// - Warning: Ensure that `User.shared.firstTime` and `versionProvider.version` are correctly initialized before calling this function. + /// + public static func evaluateCurrentEcosiaInstallType(withVersionProvider versionProvider: AppVersionInfoProvider = DefaultAppVersionInfoProvider(), storeUpgradeVersion: Bool = false) { + + if User.shared.firstTime && + EcosiaInstallType.get() == .unknown { + EcosiaInstallType.set(type: .fresh) + EcosiaInstallType.updateCurrentVersion(version: versionProvider.version) + User.shared.versionOnInstall = versionProvider.version + } + + if EcosiaInstallType.persistedCurrentVersion() != versionProvider.version { + EcosiaInstallType.set(type: .upgrade) + guard storeUpgradeVersion else { return } + EcosiaInstallType.updateCurrentVersion(version: versionProvider.version) + } + } +} diff --git a/firefox-ios/Ecosia/Helpers/EcosiaInstallType/EcosiaInstallType.swift b/firefox-ios/Ecosia/Helpers/EcosiaInstallType/EcosiaInstallType.swift new file mode 100644 index 0000000000000..42c8732ce7fbc --- /dev/null +++ b/firefox-ios/Ecosia/Helpers/EcosiaInstallType/EcosiaInstallType.swift @@ -0,0 +1,63 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Represents the type of Ecosia installation. +public enum EcosiaInstallType: String { + + /// Represents a fresh installation of Ecosia. + case fresh + + /// Represents an upgrade from a previous version of Ecosia. + case upgrade + + /// Represents an unknown installation type. + case unknown + + // MARK: - Internal Properties + + /// The key used to store and retrieve the install type from UserDefaults. + static let installTypeKey = "ecosiaInstallTypeKey" + + /// The key used to store and retrieve the current installed version from UserDefaults. + static let currentInstalledVersionKey = "ecosiaCurrentInstalledVersionKey" + + // MARK: - Public Methods + + /// Retrieves the current Ecosia install type from UserDefaults. + /// + /// - Returns: The current Ecosia install type. If not found, returns `.unknown`. + public static func get() -> EcosiaInstallType { + guard let rawValue = UserDefaults.standard.string(forKey: Self.installTypeKey), + let type = EcosiaInstallType(rawValue: rawValue) + else { return unknown } + + return type + } + + /// Sets the Ecosia install type in UserDefaults. + /// + /// - Parameter type: The Ecosia install type to be set. + static func set(type: EcosiaInstallType) { + UserDefaults.standard.set(type.rawValue, forKey: Self.installTypeKey) + } + + /// Retrieves the persisted current version of Ecosia from UserDefaults. + /// + /// - Returns: The persisted current version. If not found, returns an empty string. + public static func persistedCurrentVersion() -> String { + guard let currentVersion = UserDefaults.standard.string(forKey: Self.currentInstalledVersionKey) else { return "" } + return currentVersion + } + + /// Updates the persisted current version of Ecosia in UserDefaults. + /// + /// - Parameter version: The version to be persisted. + static func updateCurrentVersion(version: String) { + UserDefaults.standard.set(version, forKey: Self.currentInstalledVersionKey) + } +} + +extension EcosiaInstallType: Equatable {} diff --git a/firefox-ios/Ecosia/Helpers/Version/Version+Extensions.swift b/firefox-ios/Ecosia/Helpers/Version/Version+Extensions.swift new file mode 100644 index 0000000000000..7ba87dcc8e842 --- /dev/null +++ b/firefox-ios/Ecosia/Helpers/Version/Version+Extensions.swift @@ -0,0 +1,49 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +/// Extension handling previous version retrieval and saving current version. +extension Version { + + /// Save the specified app version to `UserDefaults`. + /// + /// This method takes a `Version` object and saves its description to `UserDefaults` + /// using a parametrized key. + /// + /// - Parameters: + /// - key: The `String` representing the value by which we would like to store our version. + /// - prefs: The `UserDefaults` instance to use for saving the version. + /// Defaults to `UserDefaults.standard`. + static func saved(forKey key: String, using prefs: UserDefaults = UserDefaults.standard) -> Version? { + guard let savedKey = prefs.string(forKey: key) else { return nil } + return Version(savedKey) + } + + /// Update the specified app version to `UserDefaults`. + /// + /// This method takes a `String` key to utilize to its `UserDefaults` + /// + /// - Parameters: + /// - key: The `String` representing the value by which we would like to store our version. + /// - provider: The `AppVersionInfoProvider` serving the `version` `String`. + /// Defaults to `DefaultAppVersionInfoProvider`. + /// - prefs: The `UserDefaults` instance to use for saving the version. + /// Defaults to `UserDefaults.standard`. + static func updateFromCurrent(forKey key: String, + provider: AppVersionInfoProvider = DefaultAppVersionInfoProvider(), + using prefs: UserDefaults = UserDefaults.standard) { + prefs.set(provider.version, forKey: key) + } +} + +/// Extension handling the gather of the current Ecosia App Version. +extension Version { + + /// A string representation of the current Ecosia App Version. + static var ecosiaCurrent: Version { + Version(AppInfo.ecosiaAppVersion)! + } +} diff --git a/firefox-ios/Ecosia/Helpers/Version/Version.swift b/firefox-ios/Ecosia/Helpers/Version/Version.swift new file mode 100644 index 0000000000000..e8469bae0bbf0 --- /dev/null +++ b/firefox-ios/Ecosia/Helpers/Version/Version.swift @@ -0,0 +1,80 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Represents a semantic version of an app. +/// +/// A semantic version is typically represented as a series of numbers separated by dots, e.g., "1.0.0". +public struct Version: CustomStringConvertible { + + var major: Int + var minor: Int + var patch: Int + + /// Initializes a new `Version` from a string representation. + /// + /// - Parameter versionString: A string containing the semantic version, e.g., "1.0.0". + public init?(_ versionString: String) { + let components = versionString.split(separator: ".") + guard components.count == 3, + let major = Int(components[0]), + let minor = Int(components[1]), + let patch = Int(components[2]) else { + return nil + } + + self.major = major + self.minor = minor + self.patch = patch + } + + /// A string representation of the `Version`. + public var description: String { + return "\(major).\(minor).\(patch)" + } +} + +extension Version: Comparable { + + /// Compares two `Version` instances for equality. + /// + /// - Parameters: + /// - lhs: A `Version`. + /// - rhs: Another `Version`. + /// + /// - Returns: `true` if both instances represent the same version, `false` otherwise. + public static func == (lhs: Version, rhs: Version) -> Bool { + return lhs.major == rhs.major && lhs.minor == rhs.minor && lhs.patch == rhs.patch + } + + /// Compares two `Version` instances to determine their ordering. + /// + /// - Parameters: + /// - lhs: A `Version`. + /// - rhs: Another `Version`. + /// + /// - Returns: `true` if the instance on the left should come before the one on the right, `false` otherwise. + public static func < (lhs: Version, rhs: Version) -> Bool { + if lhs.major != rhs.major { + return lhs.major < rhs.major + } + if lhs.minor != rhs.minor { + return lhs.minor < rhs.minor + } + return lhs.patch < rhs.patch + } +} + +extension Version: Hashable { + + /// Adds this value to the given hasher. + /// + /// - Parameter hasher: The hasher to use when combining the components of this instance. + public func hash(into hasher: inout Hasher) { + hasher.combine(major) + hasher.combine(minor) + hasher.combine(patch) + } +} diff --git a/firefox-ios/Ecosia/Info.plist b/firefox-ios/Ecosia/Info.plist new file mode 100644 index 0000000000000..59d4351087bf5 --- /dev/null +++ b/firefox-ios/Ecosia/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 18.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/firefox-ios/Ecosia/L10N/String.swift b/firefox-ios/Ecosia/L10N/String.swift new file mode 100644 index 0000000000000..4b61d02a6fe21 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/String.swift @@ -0,0 +1,303 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension String { + public static func localized(_ key: Key) -> String { + localized(key.rawValue) + } + + public static func localized(_ string: String) -> String { + Bundle.ecosia.localizedString(forKey: string, value: "", table: "Ecosia") + } + + public static func localizedPlural(_ key: Key, num: Int) -> String { + String(format: Bundle.ecosia.localizedString(forKey: key.rawValue, value: "", table: "Plurals"), num) + } + + public enum Key: String, CaseIterable { + case addMoreDetailAboutYourFeedback = "Add more detail about your feedback..." + case aiSearch = "AI Search" + case aiOverviewsTitle = "Overviews" + case aiOverviewsDescription = "Show AI-generated overviews at the top of search results" + case allRegions = "All regions" + case autocomplete = "Autocomplete" + case climateImpact = "Climate Impact" + case closeAll = "Close all" + case couldNotLoadSeedCounter = "Could not load seed counter" + case couldNotLoadSeedCounterMessage = "Something went wrong with displaying your seeds. Please try again later." + case customizeHomepage = "Customize homepage" + case daysAgo = "%@ days ago" + case defaultBrowserPromptExperimentButton = "Set as default in Settings" + case defaultBrowserPromptExperimentDescription = "People who set Ecosia as their default browser plant %@ per year." + case defaultBrowserPromptExperimentDescriptionTitle = "Did you know?" + case defaultBrowserPromptExperimentDescriptionHighlight = "6 more trees" + case defaultBrowserPromptExperimentTitle = "Make all your browsing green" + case ecosiaNews = "Ecosia news" + case ecosiaRecommends = "Ecosia recommends" + case estimatedImpact = "Estimated impact" + case estimatedTrees = "Estimated trees" + case ecosiaErrorViewAccessibilityImageLabel = "Image associated to the error, showing a warning signal" + case exploreEcosia = "Explore Ecosia" + case financialReports = "Financial reports" + case forceDarkMode = "Force Dark Mode" + case turnOffDarkMode = "Turn off Dark Mode" + case getStarted = "Get started" + case goBack = "Go back" + case gotIt = "Got it" + case home = "Home" + case homepage = "Homepage" + case welcomeTo = "Welcome to" + case realChangeAtYourFingertips = "Real change\nat your fingertips" + case joinMillionsPeople = "Join 20 million people making a difference every day" + case invalidReferralLink = "Invalid referral link!" + case invalidReferralLinkMessage = "Your referral link is wrong or not valid for you. Please check it and try again." + case invertColors = "Invert website colors" + case inviteFriends = "Invite friends" + case inviteFriendsSpotlight = "Help plant trees by inviting friends" + case keepUpToDate = "Keep up to date with the latest news from our projects and more" + case youveContributed = "You’ve contributed to plant a tree with your friend!" + case learnMore = "Learn more" + case linkAlreadyUsedTitle = "Link already used" + case linkAlreadyUsedMessage = "You can only use an invitation link once." + case moderate = "Moderate" + case seeAll = "See all" + case multiplyImpact = "Multiply impact" + case yourImpact = "Your impact" + case yourInvites = "Your invites" + case growingTogether = "Growing together" + case myImpactDescription = "This is the estimated number of trees you have contributed to planting by using Ecosia." + case mySearches = "My searches" + case myTrees = "My trees" + case networkError = "Network error!" + case next = "Next" + case new = "New" + case ntpFirstSearchTitle = "Get started with Ecosia" + case ntpFirstSearchDescription = "Try a search and discover how you're helping fight climate change by using Ecosia." + case noConnectionMessage = "We couldn't verify your link. Please check your internet connection and try again." + case noConnectionNSURLErrorTitle = "No connection" + case noConnectionNSURLErrorMessage = "Please check your internet connection and try again" + case noConnectionNSURLErrorRefresh = "Refresh" + case off = "Off" + case onAverageItTakes = "On average it takes around 45 searches to plant a tree" + case openInSafari = "Open In Safari" + case personalizedResults = "Personalized results" + case plantTreesWhile = "Plant trees while you browse the web" + case privacy = "Privacy" + case privateTab = "Private" + case privateEmpty = "Ecosia won’t remember the pages you visited, your search history or your autofill information once you close a tab. Your searches still contribute to trees." + case relevantResults = "Relevant results based on past searches" + case readMore = "Read More" + case referrals = "%d referral(s)" + case referralAccepted = "A friend accepted your invitation and each of you will help plant 1 tree!" + case referralsAccepted = "%@ friends accepted your invitation and each of you will help plant %@ trees!" + case safeSearch = "Safe search" + case search = "Search" + case searches = "%d search(es)" + case searchAndPlant = "Search the web to plant trees..." + case searchRegion = "Search region" + case sendFeedback = "Send feedback" + case shownUnderSearchField = "Shown under the search field" + case signUp = "Sign up" + case signIn = "Sign in" + case maybeLater = "Maybe later" + case signInErrorMessage = "Something went wrong. Please sign in again." + case signOutErrorMessage = "Something went wrong. Please sign out again." + case showOnHomepage = "Show on homepage" + case startPlanting = "Plant your first tree" + case strict = "Strict" + case tapCounter = "Tap your tree counter to share Ecosia with friends and plant more trees" + case terms = "Terms and conditions" + case today = "Today" + case togetherWeCan = "Together, we can reforest our planet. Tap your counter to spread the word!" + case topSites = "Top Sites" + case totalEcosiaTrees = "Total Ecosia trees" + case treesPlural = "%d tree(s)" + case trees = "TREES" + case treesUpdate = "Trees update" + case treesPlantedWithEcosia = "TREES PLANTED WITH ECOSIA" + case useTheseCompanies = "Start using these green companies to plant more trees and become more sustainable" + case version = "Version %@" + case viewMyImpact = "View my impact" + case weUseTheProfit = "We use the profit from your searches to plant trees where they are needed most" + case helpUsImprove = "Help us improve our new app" + case letUsKnowWhat = "Let us know what you like, dislike, and want to see in the future." + case shareYourFeedback = "Share your feedback" + case sitTightWeAre = "Sit tight, we are getting ready for you…" + case weHitAGlitch = "We hit a glitch" + case weAreMomentarilyUnable = "We are momentarily unable to load all of your settings." + case continueMessage = "Continue" + case retryMessage = "Retry" + case showTopSites = "Show Top Sites" + case helpYourFriendsBecome = "Help your friends become climate active and plant trees together" + case friendsJoined = "%d friend(s) joined" + case acceptedInvites = "accepted invite(s)" + case invitingAFriend = "Inviting a friend" + case inviteYourFriends = "Invite your friends" + case sendAnInvite = "Send an invite with your unique invitation link" + case theyDownloadTheApp = "They download the Ecosia app" + case viaTheAppStore = "Via the App Store or Google Play" + case theyOpenYourInviteLink = "They open your invite link (iOS only)" + case yourFriendClicks = "Your friend opens the second link from the invite message" + case eachOfYouHelpsPlant = "You both are helping the planet" + case whenAFriendUses = "Using Ecosia, you’re both making a difference for the environment" + case noBookmarksYet = "No bookmarks yet" + case AddYourFavoritePages = "Add your favorite pages to your bookmarks and they will appear here" + case noArticles = "No articles on your reading list" + case openArticlesInReader = "Open articles in Reader View by tapping the page icon in the address bar" + case saveArticlesToReader = "Save articles to your Reading list by tapping on ‘Add to Reading List’ in the options while in Reader View" + case noHistory = "No history" + case websitesYouHave = "Websites you’ve recently visited will show up here" + case noDownloadsYet = "No downloads yet" + case whenYouDownloadFiles = "When you download files they will show up here" + case messageMentioningActiveUsers = "Check this out: Ecosia plants trees when you search the web! 🌳\nJoin me and %@M+ others and start planting today." + case tapLinkToConfirm = "If you’re using an iPhone or iPad, tap here to confirm you’ve joined:" + case seeTheCollectiveImpact = "See the collective impact you are having with the Ecosia community" + case theSimplestWay = "The simplest way to be \n climate-active every day while \n browsing the web" + case totalIncome = "Total income" + case treesFinanced = "Trees financed" + case skip = "Skip" + case treesPlanted = "Trees planted" + case sustainableShoes = "sustainable shoes" + case before = "Before ..." + case after = "After" + case treesPlantedByEcosia = "trees planted by Ecosia" + case treesPlantedByEcosiaCapitalized = "Trees planted by Ecosia" + case numberAsStringWithPlusSymbol = "%@+" + case dedicatedToClimateAction = "dedicated to climate action" + case activeProjects = "Active projects" + case countries = "Countries" + case treesPlantedPlural = "Tree(s) planted" + case howItWorks = "How it works" + case notNow = "Not now" + case groupYourImpact = "Group your impact" + case customization = "Customization" + case clearAll = "Clear all" + case searchBarHint = "To make entering info easier, the toolbar can be set to the bottom of the screen" + case buyTrees = "Buy trees in the Ecosia tree store to delight a friend - or treat yourself" + case plantTreesAndEarn = "Plant trees and earn eco-friendly rewards with Treecard" + case sponsored = "Sponsored" + case inviteYourFriendsToCheck = "Increase your positive impact on the planet by inviting your friends to join Ecosia." + case sharingYourLink = "Sharing your link" + case copy = "Copy" + case moreSharingMethods = "More sharing methods" + case copied = "Copied!" + case plantTreesWithMe = "Plant trees with me on Ecosia" + case ecosiaLogoAccessibilityLabel = "Ecosia logo" + case done = "Done" + case findInPage = "Find in page" + case exportBookmarks = "Export bookmarks" + case importBookmarks = "Import bookmarks" + case exportingBookmarks = "Exporting Bookmarks…" + case importingBookmarks = "Importing Bookmarks…" + case importedBookmarkFolderName = "Imported Bookmarks (%@)" + case bookmarksImportFailedTitle = "Importing failed" + case bookmarksExportFailedTitle = "Exporting failed" + case bookmarksImportExportFailedMessage = "Something went wrong, please try again." + case bookmarksPanelMore = "More" + case bookmarksImported = "Bookmarks imported" + case bookmarksExported = "Bookmarks exported" + case bookmarksEmptyViewItem0 = "Tap the bookmark icon when you find a page you want to save." + case bookmarksEmptyViewItem1 = "You can also import bookmarks:" + case bookmarksEmptyViewItem1NumberedItem0 = "Export your bookmarks from another browser." + case bookmarksEmptyViewItem1NumberedItem1 = "Tap on the link below to import the file of your bookmarks" + case bookmarksToolTipText = "Tap here to import bookmarks from other browsers." + case cancel = "Cancel" + case open = "Open" + case openExternalLinkTitle = "Open link in external app?" + case openExternalLinkDescription = "%@ wants to open this application." + case sendUsageDataSettingsTitle = "Send usage data" + case sendUsageDataSettingsDescription = "To improve our browser apps, we collect usage statistics from your device. These are anonymous and protect your privacy." + case impactSectionAccessibilityHint = "Open the Your Impact section" + case impactSectionAccessibilityLabel = "Your Impact section, highlithing the trees planted by yourself and the Ecosia community overall. You have contributed to plant %@ trees. The total number of trees planted by the Ecosia community has reached %@" + case whatsNewViewTitle = "What's new" + case whatsNewFirstItemTitle9_0_0 = "Collective action" + case whatsNewFirstItemDescription9_0_0 = "See the climate impact you are having together with the rest of the Ecosia community." + case whatsNewSecondItemTitle9_0_0 = "Customizable home page" + case whatsNewSecondItemDescription9_0_0 = "Tailor your home page to show the information that’s most relevant to you." + case whatsNewFirstItemTitle10_0_0 = "Zooming" + case whatsNewFirstItemDescription10_0_0 = "You can now easily zoom in and out on any page through options." + case whatsNewSecondItemTitle10_0_0 = "Password manager" + case whatsNewSecondItemDescription10_0_0 = "We’ve added 1Password to Ecosia to help you to manage your passwords securely." + case configurableNudgeCardCloseButtonAccessibilityLabel = "Close card button" + case newsletterNTPCardExperimentTitle = "Be the first to know" + case newsletterNTPCardExperimentDescription = "Subscribe to our monthly newsletter for updates on your climate impact." + case defaultBrowserSettingTitle = "Default browser" + case defaultBrowserCardTitle = "Make Ecosia your default browser app" + case defaultBrowserCardDescription = "Safely open all links from other apps in Ecosia" + case defaultBrowserCardDetailTitle = "Use Ecosia as default" + case defaultBrowserCardDetailInstructionStep1 = "Open **Settings**" + case defaultBrowserCardDetailInstructionStep2 = "Select **Default Browser App**" + case defaultBrowserCardDetailInstructionStep3 = "Choose **Ecosia**" + case defaultBrowserCardDetailButton = "Make default in settings" + + // MARK: - Feedback View + case reportIssue = "Report an issue" + case helpCenter = "Help Center" + case suggestionOrFeedback = "Suggestion or feedback" + case whatWouldYouLikeToShare = "What would you like to share?" + case send = "Send" + case close = "Close" + case thankYouForYourFeedback = "Thank you for your feedback!" + + // MARK: Rating Prompt Dialog + case settingsRatingPromptYes = "Yes" + case settingsRatingPromptNo = "No" + case settingsRatingPromptTitle = "Do you enjoy Ecosia?" + + // MARK: - WebView Modal + case failedToLoadPage = "Failed to load page" + case tryAgain = "Try Again" + case errorIcon = "Error" + + // MARK: - Level System + case level = "Level" + case ecocurious = "Ecocurious" + case greenExplorer = "Green explorer" + case planetPal = "Planet pal" + case seedlingSupporter = "Seedling supporter" + case biodiversityBestie = "Biodiversity bestie" + case forestFriend = "Forest friend" + case wildlifeProtector = "Wildlife protector" + case ecoExplorer = "Eco explorer" + case rainforestReviver = "Rainforest reviver" + case planetProtector = "Planet protector" + case carbonNeutralizer = "Carbon neutralizer" + case seekerOfSustainability = "Seeker of sustainability" + case branchBuilder = "Branch builder" + case ecoEnthusiast = "Eco enthusiast" + case carbonCutter = "Carbon cutter" + case seedSower = "Seed sower" + case emissionEliminator = "Emission eliminator" + + // MARK: - Account Impact View + case guestUser = "Guest user" + case seedsSymbolizeYourOwnImpact = "Seeds symbolize your own impact" + case collectSeedsEveryDayYouUse = "Collect seeds every day you use Ecosia and see the climate impact you're having." + case learnMoreAboutSeeds = "Learn more about seeds" + case sustainabilitySage = "Sustainability sage" + case earthAdvocate = "Earth advocate" + case seedSuperstar = "Seed superstar" + case yourEcosia = "Your Ecosia" + case yourProfile = "Your profile" + case signOut = "Sign out" + case seedCountAccessibilityLabel = "%d seeds" + case userAvatarAccessibilityLabel = "User avatar" + case defaultAvatarAccessibilityLabel = "Default avatar" + case accountButtonAccessibilityLabel = "Account: %@" + case accountButtonAccessibilityHint = "Opens your account and impact details" + case userLevelAccessibilityLabel = "Level: %@" + case profileButtonAccessibilityHint = "Opens your profile page" + case signOutButtonAccessibilityHint = "Sign out of your account" + + // MARK: - Product Tour Spotlight + case serpSpotlightStep1Title = "You browse with 100% clean energy" + case serpSpotlightStep1Description = "You're helping push dirty energy off the grid! We produce more solar and wind energy than your searches take." + case serpSpotlightStep2Title = "We use profits for the planet" + case serpSpotlightStep2Description = "Unlike any other search engines, we use 100% of the profits that we make from ads for climate action!" + case protectionSpotlightTitle = "We protect your data like the climate" + case protectionSpotlightDescription = "We block unnecessary trackers so you can browse privately, without worries about privacy." + } +} diff --git a/firefox-ios/Ecosia/L10N/de.lproj/Ecosia.strings b/firefox-ios/Ecosia/L10N/de.lproj/Ecosia.strings new file mode 100644 index 0000000000000..cdbe58ab90e23 Binary files /dev/null and b/firefox-ios/Ecosia/L10N/de.lproj/Ecosia.strings differ diff --git a/firefox-ios/Ecosia/L10N/de.lproj/Plurals.stringsdict b/firefox-ios/Ecosia/L10N/de.lproj/Plurals.stringsdict new file mode 100644 index 0000000000000..ec945b69de334 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/de.lproj/Plurals.stringsdict @@ -0,0 +1,102 @@ + + + + + %d tree(s) + + NSStringLocalizedFormatKey + %#@trees@ + trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d Baum + other + %d Bäume + + + %d search(es) + + NSStringLocalizedFormatKey + %#@searches@ + searches + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d Suchanfrage + other + %d Suchanfragen + + + %d referral(s) + + NSStringLocalizedFormatKey + %#@referrals@ + referrals + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d Empfehlung + other + %d Empfehlungen + + + %d friend(s) joined + + NSStringLocalizedFormatKey + %#@friends@ + friends + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d Freund*in überzeugt + other + %d Freund*innen überzeugt + + + Tree(s) planted + + NSStringLocalizedFormatKey + %#@Trees@ + Trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Gepflanzte Bäume + other + Gepflanzte Bäume + + + accepted invite(s) + + NSStringLocalizedFormatKey + %#@invites@ + invites + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + angenommene Einladung + other + angenommene Einladungen + + + + diff --git a/firefox-ios/Ecosia/L10N/en.lproj/Ecosia.strings b/firefox-ios/Ecosia/L10N/en.lproj/Ecosia.strings new file mode 100644 index 0000000000000..345ce04523be0 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/en.lproj/Ecosia.strings @@ -0,0 +1,301 @@ +"%@ days ago" = "%@ days ago"; +"AI Search" = "AI Search"; +"Autocomplete" = "Autocomplete"; +"Blog" = "Blog"; +"Close all" = "Close all"; +"Ecosia recommends" = "Ecosia recommends"; +"Ecosia won’t remember the pages you visited, your search history or your autofill information once you close a tab. Your searches still contribute to trees." = "Ecosia won’t save the pages you visit, your search history or your autofill information. Your searches will still contribute toward planting trees."; +"Explore Ecosia" = "Explore Ecosia"; +"Financial reports" = "Financial reports"; +"Force Dark Mode" = "Force Dark Mode"; +"Get started" = "Get started"; +"Home" = "Home"; +"Invert website colors" = "Invert website colors"; +"Keep up to date with the latest news from our projects and more" = "Keep up to date with the latest news from our projects and more"; +"Learn more" = "Learn more"; +"Maybe later" = "Maybe later"; +"Moderate" = "Moderate"; +"See all" = "See all"; +"My searches" = "My searches"; +"New" = "New"; +"Off" = "Off"; +"Open In Safari" = "Open in Safari"; +"Private" = "Private"; +"Personalized results" = "Personalized results"; +"Plant trees while you browse the web" = "Plant trees while you browse the web"; +"Privacy" = "Privacy"; +"Read More" = "Read More"; +"Relevant results based on past searches" = "Relevant results based on past searches"; +"Safe search" = "Safe search"; +"Search" = "Search"; +"Search the web to plant trees..." = "Search the web to plant trees..."; +"Search region" = "Search region"; +"Send feedback" = "Send feedback"; +"Shown under the search field" = "Shown under the search field"; +"Sign in" = "Sign in"; +"Start using these green companies to plant more trees and become more sustainable" = "Start using these green companies to plant more trees and become more sustainable"; +"Ecosia news" = "Ecosia news"; +"Strict" = "Strict"; +"TREES" = "Trees"; +"TREES PLANTED WITH ECOSIA" = "Trees planted with Ecosia"; +"Turn off Dark Mode" = "Turn off Dark Mode"; +"Terms and conditions" = "Terms and conditions"; +"Version %@" = "Version %@"; +"We use the profit from your searches to plant trees where they are needed most" = "We use the profit from your searches to plant trees where they are needed most"; +"Help us improve our new app" = "Help us improve our new app"; +"Let us know what you like, dislike, and want to see in the future." = "Let us know what you like, dislike, and want to see in the future."; +"Share your feedback" = "Share your feedback"; +"Sit tight, we are getting ready for you…" = "Sit tight, we are getting ready for you…"; +"We hit a glitch" = "We hit a glitch"; +"We are momentarily unable to load all of your settings." = "We are momentarily unable to load all of your settings."; +"Continue" = "Continue"; +"Retry" = "Retry"; +"Show Top Sites" = "Show Top Sites"; +"All regions" = "All regions"; +"Refresh" = "Refresh"; +"Please check your internet connection and try again" = "Please check your internet connection and try again"; +"No connection" = "No connection"; +"Done" = "Done"; +"Find in page" = "Find in page"; +"Cancel" = "Cancel"; +"Open" = "Open"; +"Open link in external app?" = "Open link in external app?"; +"Overviews" = "Overviews"; +"%@ wants to open this application." = "%@ wants to open this application."; +"Send usage data" = "Send usage data"; +"To improve our browser apps, we collect usage statistics from your device. These are anonymous and protect your privacy." = "To improve our browser apps, we collect usage statistics from your device. These are anonymous and protect your privacy."; +"Climate Impact" = "Climate Impact"; +"Customize homepage" = "Customize homepage"; +"Homepage"= "Homepage"; +"Show on homepage" = "Show on homepage"; +"Show AI-generated overviews at the top of search results" = "Show AI-generated overviews at the top of search results"; +"Top Sites" = "Top Sites"; +"Not now" = "Not now"; +"Image associated to the error, showing a warning signal" = "Image associated to the error, showing a warning signal"; + +// Impact +"A friend accepted your invitation and each of you will help plant 1 tree!" = "A friend has accepted your invite! You have each helped plant 1 extra tree."; +"Estimated impact" = "Estimated impact"; +"Estimated trees" = "Estimated trees"; +"%@ friends accepted your invitation and each of you will help plant %@ trees!" = "%@ friends have accepted your invite! You have helped plant %@ extra trees."; +"You’ve contributed to plant a tree with your friend!" = "You have contributed a new tree! Thanks for joining Ecosia."; +"Invalid referral link!" = "Invalid referral link!"; +"Invite friends" = "Invite friends"; +"Help plant trees by inviting friends" = "Help plant trees by inviting friends"; +"Multiply impact" = "Multiply impact"; +"My trees" = "My trees"; +"Network error!" = "Network error!"; +"On average it takes around 45 searches to plant a tree" = "On average it takes around 45 searches to plant a tree"; +"Plant your first tree" = "Plant your first tree"; +"Tap your tree counter to share Ecosia with friends and plant more trees" = "Tap your tree counter to share Ecosia with friends and plant more trees"; +"This is the estimated number of trees you have contributed to planting by using Ecosia." = "This is the estimated number of trees you have contributed to planting by using Ecosia."; +"Together, we can reforest our planet. Tap your counter to spread the word!" = "Together, we can reforest our planet. Tap your counter to spread the word!"; +"Total Ecosia trees" = "Total Ecosia trees"; +"View my impact" = "View my impact"; +"We couldn't verify your link. Please check your internet connection and try again." = "We couldn't verify your link. Please check your internet connection and try again."; +"Your referral link is wrong or not valid for you. Please check it and try again." = "Your referral link is wrong or not valid for you. Please check it and try again."; +"Invite your friends" = "Invite your friends"; +"Send an invite with your unique invitation link" = "Send an invite with your unique invitation link"; +"They download the Ecosia app" = "They download the Ecosia app"; +"Via the App Store or Google Play" = "Via the App Store or Google Play"; +"They open your invite link (iOS only)" = "They open your invite link (iOS only)"; +"Your friend opens the second link from the invite message" = "Your friend opens the second link from the invite message"; +"You both are helping the planet" = "You both are helping the planet"; +"Using Ecosia, you’re both making a difference for the environment" = "Using Ecosia, you’re both making a difference for the environment"; +"Inviting a friend" = "Inviting a friend"; +"No bookmarks yet" = "No bookmarks yet"; +"Add your favorite pages to your bookmarks and they will appear here" = "Add your favorite pages to your bookmarks and they will appear here"; +"No articles on your reading list" = "No articles on your reading list"; +"Open articles in Reader View by tapping the page icon in the address bar" = "Open articles in Reader View by tapping the page icon in the address bar"; +"Save articles to your Reading list by tapping on ‘Add to Reading List’ in the options while in Reader View" = "Save articles to your Reading list by tapping on ‘Add to Reading List’ in the options while in Reader View"; +"No history" = "No history"; +"Websites you’ve recently visited will show up here" = "Websites you’ve recently visited will show up here"; +"No downloads yet" = "No downloads yet"; +"When you download files they will show up here" = "When you download files they will show up here"; +"Plant trees with me on Ecosia" = "Plant trees with me on Ecosia"; +"Check this out: Ecosia plants trees when you search the web! 🌳\nJoin me and %@M+ others and start planting today." = "Check this out: Ecosia plants trees when you search the web! 🌳\nJoin me and %@M+ others and start planting today."; +"If you’re using an iPhone or iPad, tap here to confirm you’ve joined:" = "If you’re using an iPhone or iPad, tap here to confirm you’ve joined:"; +"Link already used" = "Link already used"; +"You can only use an invitation link once." = "You can only use an invitation link once."; +"Increase your positive impact on the planet by inviting your friends to join Ecosia." = "Increase your positive impact on the planet by inviting your friends to join Ecosia."; +"Sharing your link" = "Sharing your link"; +"Copy" = "Copy"; +"More sharing methods" = "More sharing methods"; +"Copied!" = "Copied!"; + +// Rebrand +"See the collective impact you are having with the Ecosia community" = "See the collective impact you are having with the Ecosia community"; +"Your impact" = "Your impact"; +"How it works" = "How it works"; +"My Impact" = "My Impact"; +"Group your impact" = "Grow your impact"; + +// Tour +"Skip" = "Skip"; +"The simplest way to be \n climate-active every day while \n browsing the web" = "The simplest way to be \n climate active every day while \n browsing the web"; +"sustainable shoes" = "sustainable shoes"; +"Trees planted" = "Trees planted"; +"Before ..." = "Before..."; +"After" = "After"; +"trees planted by Ecosia" = "trees planted by Ecosia"; +"Trees planted by Ecosia" = "Trees planted by Ecosia"; +"dedicated to climate action" = "dedicated to climate action"; +"Active projects" = "Active projects"; +"%@+" = "%@+"; +"Countries" = "Countries"; +"Total income" = "Total income"; +"Trees financed" = "Trees financed"; +"Your invites" = "Your friends"; +"Growing together" = "Growing together"; +"Help your friends become climate active and plant trees together" = "Help your friends become climate active and plant trees together"; +"Customization" = "Customization"; +"Clear all" = "Clear all"; +"To make entering info easier, the toolbar can be set to the bottom of the screen" = "To make entering info easier, the toolbar can be set to the bottom of the screen"; + +// Promo +"Buy trees in the Ecosia tree store to delight a friend - or treat yourself" = "Buy trees in the Ecosia Tree Store to delight a friend - or treat yourself"; +"Plant trees and earn eco-friendly rewards with Treecard" = "Plant trees and earn eco-friendly rewards with Treecard"; +"Sponsored" = "Sponsored"; + +// Accessibility Labels +"Ecosia logo" = "Ecosia logo"; +"Open the Your Impact section" = "Open the Your Impact section"; +"Your Impact section, highlithing the trees planted by yourself and the Ecosia community overall. You have contributed to plant %@ trees. The total number of trees planted by the Ecosia community has reached %@" = "Your Impact section, highlithing the trees planted by yourself and the Ecosia community overall. You have contributed to plant %@ trees. The total number of trees planted by the Ecosia community has reached %@"; + +// Bookmark import/export +"Export bookmarks" = "Export bookmarks"; +"Import bookmarks" = "Import bookmarks"; +"Exporting Bookmarks…" = "Exporting Bookmarks…"; +"Importing Bookmarks…" = "Importing Bookmarks…"; +"Imported Bookmarks (%@)" = "Imported Bookmarks (%@)"; +"Importing failed" = "Importing failed"; +"Exporting failed" = "Exporting failed"; +"Something went wrong, please try again." = "Something went wrong, please try again."; +"More" = "More"; +"Bookmarks imported" = "Bookmarks imported"; +"Bookmarks exported" = "Bookmarks exported"; + +// Bookmarks Empty View +"Tap the bookmark icon when you find a page you want to save." = "Tap the bookmark icon when you find a page you want to save."; +"You can also import bookmarks:" = "You can also import bookmarks:"; +"Export your bookmarks from another browser." = "Export your bookmarks from another browser."; +"Tap on the link below to import the file of your bookmarks" = "Tap on the link below to import the file of your bookmarks"; + +// Bookmarks Tooltip +"Tap here to import bookmarks from other browsers." = "Tap here to import bookmarks from other browsers."; + +// Whats New Page +"What's new" = "What's new"; +"Collective action" = "Collective action"; +"See the climate impact you are having together with the rest of the Ecosia community." = "See the climate impact you are having together with the rest of the Ecosia community."; +"Customizable home page" = "Customizable home page"; +"Tailor your home page to show the information that’s most relevant to you." = "Tailor your home page to show the information that’s most relevant to you."; +"Zooming" = "Zooming"; +"You can now easily zoom in and out on any page through options." = "You can now easily zoom in and out on any page through options."; +"Password manager" = "Password manager"; +"We’ve added 1Password to Ecosia to help you to manage your passwords securely." = "We’ve added 1Password to Ecosia to help you to manage your passwords securely."; +"Got it" = "Got it"; + +// Newsletter +"Be the first to know" = "Be the first to know"; +"Subscribe to our monthly newsletter for updates on your climate impact." = "Subscribe to our monthly newsletter for updates on your climate impact."; +"Sign up" = "Sign up"; + +// Default Browser +"Set as default in Settings" = "Set as default in Settings"; +"People who set Ecosia as their default browser plant %@ per year." = "People who set Ecosia as their default browser plant %@ per year."; +"Did you know?" = "Did you know?"; +"6 more trees" = "6 more trees"; +"Make all your browsing green" = "Make all your browsing green"; +"Default browser" = "Default browser"; +"Make Ecosia your default browser app" = "Make Ecosia your default browser app"; +"Safely open all links from other apps in Ecosia" = "Safely open all links from other apps in Ecosia"; +"Use Ecosia as default" = "Use Ecosia as default"; +"Open **Settings**" = "Open **Settings**"; +"Select **Default Browser App**" = "Select **Default Browser App**"; +"Choose **Ecosia**" = "Choose **Ecosia**"; +"Make default in settings" = "Make default in settings"; +"Close card button" = "Close card button"; + +// Feedback View +"Report an issue" = "Report an issue"; +"Suggestion or feedback" = "Suggestion or feedback"; +"What would you like to share?" = "What would you like to share?"; +"Add more detail about your feedback..." = "Add more detail about your feedback..."; +"Send" = "Send"; +"Close" = "Close"; +"Help Center" = "Help Center"; +"Thank you for your feedback!" = "Thank you for your feedback!"; + +// Error Messages +"Could not load seed counter" = "Could not load seed counter"; +"Something went wrong with displaying your seeds. Please try again later." = "Something went wrong with displaying your seeds. Please try again later."; +"Something went wrong. Please sign in again." = "Something went wrong. Please sign in again."; +"Something went wrong. Please sign out again." = "Something went wrong. Please sign out again."; + +// Rating Prompt Dialog +"Yes" = "Yes"; +"No" = "No"; +"Do you enjoy Ecosia?" = "Do you enjoy Ecosia?"; + +// Level System +"Level" = "Level"; + +// WebView Modal +"Failed to load page" = "Failed to load page"; +"Try Again" = "Try Again"; +"Error" = "Error"; + +// Level System +"Ecocurious" = "Ecocurious"; +"Green explorer" = "Green explorer"; +"Planet pal" = "Planet pal"; +"Seedling supporter" = "Seedling supporter"; +"Biodiversity bestie" = "Biodiversity bestie"; +"Forest friend" = "Forest friend"; +"Wildlife protector" = "Wildlife protector"; +"Eco explorer" = "Eco explorer"; +"Rainforest reviver" = "Rainforest reviver"; +"Planet protector" = "Planet protector"; +"Carbon neutralizer" = "Carbon neutralizer"; +"Seeker of sustainability" = "Seeker of sustainability"; +"Branch builder" = "Branch builder"; +"Eco enthusiast" = "Eco enthusiast"; +"Carbon cutter" = "Carbon cutter"; +"Seed sower" = "Seed sower"; +"Emission eliminator" = "Emission eliminator"; +"Sustainability sage" = "Sustainability sage"; +"Earth advocate" = "Earth advocate"; +"Seed superstar" = "Seed superstar"; + +// Account Impact View. "Sign up" already managed above in the Newsletter section +"Guest user" = "Guest user"; +"Seeds symbolize your own impact" = "Seeds symbolize your own impact"; +"Collect seeds every day you use Ecosia and see the climate impact you're having." = "Collect seeds every day you use Ecosia and see the climate impact you're having."; +"Learn more about seeds" = "Learn more about seeds"; +"Your Ecosia" = "Your Ecosia"; +"Your profile" = "Your profile"; +"Sign out" = "Sign out"; +"%d seeds" = "%d seeds"; +"User avatar" = "User avatar"; +"Default avatar" = "Default avatar"; +"Account: %@" = "Account: %@"; +"Opens your account and impact details" = "Opens your account and impact details"; +"Level: %@" = "Level: %@"; +"Opens your profile page" = "Opens your profile page"; +"Sign out of your account" = "Sign out of your account"; + +// Product Tour +"Go back" = "Go back"; +"Welcome to" = "Welcome to"; +"Real change\nat your fingertips" = "Real change\nat your fingertips"; +"Join 20 million people making a difference every day" = "Join 20 million people making a difference every day"; +"Next" = "Next"; +"Get started with Ecosia" = "Get started with Ecosia"; +"Try a search and discover how you're helping fight climate change by using Ecosia." = "Try a search and discover how you're helping fight climate change by using Ecosia."; +"You browse with 100% clean energy" = "You browse with 100% clean energy"; +"You're helping push dirty energy off the grid! We produce more solar and wind energy than your searches take." = "You're helping push dirty energy off the grid! We produce more solar and wind energy than your searches take."; +"We use profits for the planet" = "We use profits for the planet"; +"Unlike any other search engines, we use 100% of the profits that we make from ads for climate action!" = "Unlike any other search engines, we use 100% of the profits that we make from ads for climate action!"; +"We protect your data like the climate" = "We protect your data like the climate"; +"We block unnecessary trackers so you can browse privately, without worries about privacy." = "We block unnecessary trackers so you can browse privately, without worries about privacy."; diff --git a/firefox-ios/Ecosia/L10N/en.lproj/Plurals.stringsdict b/firefox-ios/Ecosia/L10N/en.lproj/Plurals.stringsdict new file mode 100644 index 0000000000000..57135203b3846 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/en.lproj/Plurals.stringsdict @@ -0,0 +1,102 @@ + + + + + %d tree(s) + + NSStringLocalizedFormatKey + %#@trees@ + trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d tree + other + %d trees + + + %d search(es) + + NSStringLocalizedFormatKey + %#@searches@ + searches + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d search + other + %d searches + + + %d referral(s) + + NSStringLocalizedFormatKey + %#@referrals@ + referrals + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d referral + other + %d referrals + + + %d friend(s) joined + + NSStringLocalizedFormatKey + %#@friends@ + friends + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d friend joined + other + %d friends joined + + + Tree(s) planted + + NSStringLocalizedFormatKey + %#@Trees@ + Trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Tree planted + other + Trees planted + + + accepted invite(s) + + NSStringLocalizedFormatKey + %#@invites@ + invites + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + accepted invite + other + accepted invites + + + + diff --git a/firefox-ios/Ecosia/L10N/es.lproj/Ecosia.strings b/firefox-ios/Ecosia/L10N/es.lproj/Ecosia.strings new file mode 100644 index 0000000000000..36ed6e8e5dd0a Binary files /dev/null and b/firefox-ios/Ecosia/L10N/es.lproj/Ecosia.strings differ diff --git a/firefox-ios/Ecosia/L10N/es.lproj/Plurals.stringsdict b/firefox-ios/Ecosia/L10N/es.lproj/Plurals.stringsdict new file mode 100644 index 0000000000000..c17d6c0c2cc91 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/es.lproj/Plurals.stringsdict @@ -0,0 +1,114 @@ + + + + + %d tree(s) + + NSStringLocalizedFormatKey + %#@trees@ + trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d árbol + many + %d árboles + other + %d árboles + + + %d search(es) + + NSStringLocalizedFormatKey + %#@searches@ + searches + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d búsqueda + many + %d búsquedas + other + %d búsquedas + + + %d referral(s) + + NSStringLocalizedFormatKey + %#@referrals@ + referrals + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d referencia + many + %d referencias + other + %d referencias + + + %d friend(s) joined + + NSStringLocalizedFormatKey + %#@friends@ + friends + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d amigo inscrito + many + %d amigos inscritos + other + %d amigos inscritos + + + Tree(s) planted + + NSStringLocalizedFormatKey + %#@Trees@ + Trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Árbol plantado + many + Árboles plantados + other + Árboles plantados + + + accepted invite(s) + + NSStringLocalizedFormatKey + %#@invites@ + invites + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + invitación aceptada + many + invitaciones aceptadas + other + invitaciones aceptadas + + + + diff --git a/firefox-ios/Ecosia/L10N/fr.lproj/Ecosia.strings b/firefox-ios/Ecosia/L10N/fr.lproj/Ecosia.strings new file mode 100644 index 0000000000000..f6cd48dd6ba4c Binary files /dev/null and b/firefox-ios/Ecosia/L10N/fr.lproj/Ecosia.strings differ diff --git a/firefox-ios/Ecosia/L10N/fr.lproj/Plurals.stringsdict b/firefox-ios/Ecosia/L10N/fr.lproj/Plurals.stringsdict new file mode 100644 index 0000000000000..3ddcc52575a96 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/fr.lproj/Plurals.stringsdict @@ -0,0 +1,114 @@ + + + + + %d tree(s) + + NSStringLocalizedFormatKey + %#@trees@ + trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d arbre + many + %d arbres + other + %d arbres + + + %d search(es) + + NSStringLocalizedFormatKey + %#@searches@ + searches + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d recherche + many + %d recherches + other + %d recherches + + + %d referral(s) + + NSStringLocalizedFormatKey + %#@referrals@ + referrals + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d référence + many + %d références + other + %d références + + + %d friend(s) joined + + NSStringLocalizedFormatKey + %#@friends@ + friends + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d ami présent + many + %d amis présents + other + %d amis présents + + + Tree(s) planted + + NSStringLocalizedFormatKey + %#@Trees@ + Trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Arbres plantés + many + Arbres plantés + other + Arbres plantés + + + accepted invite(s) + + NSStringLocalizedFormatKey + %#@invites@ + invites + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + invitation acceptée + many + invitations acceptées + other + invitation(s) acceptée(s) + + + + diff --git a/firefox-ios/Ecosia/L10N/it.lproj/Ecosia.strings b/firefox-ios/Ecosia/L10N/it.lproj/Ecosia.strings new file mode 100644 index 0000000000000..c06d6f6ca83e7 Binary files /dev/null and b/firefox-ios/Ecosia/L10N/it.lproj/Ecosia.strings differ diff --git a/firefox-ios/Ecosia/L10N/it.lproj/Plurals.stringsdict b/firefox-ios/Ecosia/L10N/it.lproj/Plurals.stringsdict new file mode 100644 index 0000000000000..144230ea35dc4 --- /dev/null +++ b/firefox-ios/Ecosia/L10N/it.lproj/Plurals.stringsdict @@ -0,0 +1,114 @@ + + + + + %d tree(s) + + NSStringLocalizedFormatKey + %#@trees@ + trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d albero + many + %d alberi + other + %d alberi + + + %d search(es) + + NSStringLocalizedFormatKey + %#@searches@ + searches + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d ricerca + many + %d ricerche + other + %d ricerche + + + %d referral(s) + + NSStringLocalizedFormatKey + %#@referrals@ + referrals + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d raccomandazione + many + %d raccomandazioni + other + %d raccomandazioni + + + %d friend(s) joined + + NSStringLocalizedFormatKey + %#@friends@ + friends + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d amico coinvolto + many + %d amici coinvolti + other + %d amici coinvolti + + + Tree(s) planted + + NSStringLocalizedFormatKey + %#@Trees@ + Trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Albero piantato + many + Alberi piantati + other + Alberi piantati + + + accepted invite(s) + + NSStringLocalizedFormatKey + %#@invites@ + invites + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + invito accettato + many + inviti accettati + other + inviti accettati + + + + diff --git a/firefox-ios/Ecosia/L10N/nl.lproj/Ecosia.strings b/firefox-ios/Ecosia/L10N/nl.lproj/Ecosia.strings new file mode 100644 index 0000000000000..da94030f3ab82 Binary files /dev/null and b/firefox-ios/Ecosia/L10N/nl.lproj/Ecosia.strings differ diff --git a/firefox-ios/Ecosia/L10N/nl.lproj/Plurals.stringsdict b/firefox-ios/Ecosia/L10N/nl.lproj/Plurals.stringsdict new file mode 100644 index 0000000000000..cce3872b146cb --- /dev/null +++ b/firefox-ios/Ecosia/L10N/nl.lproj/Plurals.stringsdict @@ -0,0 +1,102 @@ + + + + + %d tree(s) + + NSStringLocalizedFormatKey + %#@trees@ + trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d boom + other + %d bomen + + + %d search(es) + + NSStringLocalizedFormatKey + %#@searches@ + searches + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d zoekopdracht + other + %d zoekopdrachten + + + %d referral(s) + + NSStringLocalizedFormatKey + %#@referrals@ + referrals + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d verwijzing + other + %d verwijzingen + + + %d friend(s) joined + + NSStringLocalizedFormatKey + %#@friends@ + friends + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d vriend + other + %d vrienden + + + Tree(s) planted + + NSStringLocalizedFormatKey + %#@Trees@ + Trees + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Boom geplant + other + Bomen geplant + + + accepted invite(s) + + NSStringLocalizedFormatKey + %#@invites@ + invites + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + geaccepteerde uitnodiging + other + geaccepteerde uitnodigingen + + + + diff --git a/firefox-ios/Ecosia/UI/Account/BalanceIncrementAnimationView.swift b/firefox-ios/Ecosia/UI/Account/BalanceIncrementAnimationView.swift new file mode 100644 index 0000000000000..b9b5169d9cedc --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/BalanceIncrementAnimationView.swift @@ -0,0 +1,93 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A view that displays a balance increment with fade in/out animation +@available(iOS 16.0, *) +public struct BalanceIncrementAnimationView: View { + let increment: Int + let windowUUID: WindowUUID + @State private var opacity: Double = 0.0 + @State private var scale: CGFloat = 0.5 + @State private var theme = BalanceIncrementAnimationViewTheme() + + private struct UX { + static let springResponse: Double = 0.5 + static let springDampingFraction: Double = 0.6 + static let appearDelay: TimeInterval = 0.3 + static let holdDuration: TimeInterval = 2.0 + static let fadeOutDuration: TimeInterval = 0.5 + } + + public init(increment: Int, windowUUID: WindowUUID) { + self.increment = increment + self.windowUUID = windowUUID + } + + public var body: some View { + Text("+\(increment)") + .font(.caption.weight(.bold)) + .foregroundColor(theme.textColor) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background(theme.backgroundColor) + .clipShape(Circle()) + .scaleEffect(scale) + .opacity(opacity) + .onAppear { + triggerAnimation() + } + .ecosiaThemed(windowUUID, $theme) + } + + private func triggerAnimation() { + // Start small and invisible + scale = 0.5 + opacity = 0.0 + + // Pop in with spring + DispatchQueue.main.asyncAfter(deadline: .now() + UX.appearDelay) { + withOptionalAnimation(.spring(response: UX.springResponse, dampingFraction: UX.springDampingFraction)) { + scale = 1.0 + opacity = 1.0 + } + } + + // Fade out after holding + DispatchQueue.main.asyncAfter(deadline: .now() + UX.appearDelay + UX.holdDuration) { + withAnimation(.easeOut(duration: UX.fadeOutDuration)) { + opacity = 0.0 + } + } + } +} + +// MARK: - Theme +struct BalanceIncrementAnimationViewTheme: EcosiaThemeable { + var textColor = Color.primary + var backgroundColor = Color.secondary + + mutating func applyTheme(theme: Theme) { + textColor = Color(EcosiaColor.Peach700) + backgroundColor = Color(EcosiaColor.Peach100) + } +} + +#if DEBUG +// MARK: - Preview +@available(iOS 16.0, *) +struct BalanceIncrementAnimationView_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: 20) { + BalanceIncrementAnimationView(increment: 1, windowUUID: .XCTestDefaultUUID) + BalanceIncrementAnimationView(increment: 3, windowUUID: .XCTestDefaultUUID) + BalanceIncrementAnimationView(increment: 10, windowUUID: .XCTestDefaultUUID) + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountAvatarViewModel.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountAvatarViewModel.swift new file mode 100644 index 0000000000000..f2e4a16f9b6de --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountAvatarViewModel.swift @@ -0,0 +1,218 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Combine +import Common + +@available(iOS 16.0, *) +@MainActor +public final class EcosiaAccountAvatarViewModel: ObservableObject { + + @Published public var avatarURL: URL? + @Published public var progress: Double + @Published public var showSparkles = false + @Published public var currentLevelNumber: Int + @Published public var seedCount: Int = 0 + + private let authStateProvider: EcosiaAuthUIStateProvider + private var cancellables = Set() + private var progressObserver: NSObjectProtocol? + private var levelUpObserver: NSObjectProtocol? + private var previousSeedCount: Int = 0 + + private struct UX { + static let defaultProgress: Double = 0.25 + static let defaultLevel: Int = 1 + static let levelUpDuration: TimeInterval = 2.0 + } + + public init( + avatarURL: URL? = nil, + progress: Double = 0.25, + seedCount: Int = 0, + levelNumber: Int = 1, + authStateProvider: EcosiaAuthUIStateProvider = .shared + ) { + self.authStateProvider = authStateProvider + self.avatarURL = avatarURL + self.progress = max(0.0, min(1.0, progress)) + self.seedCount = seedCount + self.previousSeedCount = seedCount + self.currentLevelNumber = levelNumber + + setupInitialState() + setupObservers() + } + + deinit { + [progressObserver, levelUpObserver].forEach { + if let observer = $0 { + NotificationCenter.default.removeObserver(observer) + } + } + cancellables.removeAll() + } + + public func updateAvatarURL(_ url: URL?) { + avatarURL = url + } + + public func updateProgress(_ newProgress: Double) { + let clampedProgress = max(0.0, min(1.0, newProgress)) + progress = clampedProgress + } + +#if DEBUG + /// Manual level up for testing/previews only + public func levelUp() { + progress = 1.0 + triggerSparkles(duration: UX.levelUpDuration) + + Task { + try await Task.sleep(for: .seconds(UX.levelUpDuration)) + progress = 0.0 + } + } +#endif + + public func triggerSparkles(duration: TimeInterval = 4.0) { + showSparkles = true + + Task { + try await Task.sleep(for: .seconds(duration)) + showSparkles = false + } + } + + /// Updates avatar progress based on AccountVisitResponse + public func updateFromBalanceResponse(_ response: AccountVisitResponse) { + let newSeedCount = response.seeds.balanceAmount + let newLevelNumber = response.growthPoints.level.number + let newProgress = response.progressToNextLevel + + // Update seed count + previousSeedCount = seedCount + seedCount = newSeedCount + + // Update level and progress from API + currentLevelNumber = newLevelNumber + progress = newProgress + + // Check for level up using growth points + if response.didLevelUp { + triggerLevelUpAnimation(targetProgress: newProgress) + EcosiaLogger.accounts.info("User leveled up to level \(newLevelNumber) via growth points") + } + + EcosiaLogger.accounts.info("Avatar received balance update: seeds=\(newSeedCount), level=\(newLevelNumber), progress=\(newProgress)") + } + + /// Updates seed count manually (for local/offline scenarios - logged-out users) + /// Note: Logged-out users collect seeds but cannot level up. Leveling requires authentication. + public func updateSeedCount(_ newSeedCount: Int) { + previousSeedCount = seedCount + seedCount = newSeedCount + // No level calculation for logged-out users - they don't participate in the leveling system + EcosiaLogger.accounts.info("Seed count updated to \(newSeedCount) (logged-out, no leveling)") + } + + private func triggerLevelUpAnimation(targetProgress: Double) { + progress = 1.0 + triggerSparkles(duration: UX.levelUpDuration) + + Task { + try await Task.sleep(for: .seconds(UX.levelUpDuration)) + progress = targetProgress + } + } + + private func setupInitialState() { + avatarURL = authStateProvider.avatarURL + } + + private func setupObservers() { + // Subscribe to avatarURL changes from centralized provider + authStateProvider.$avatarURL + .receive(on: DispatchQueue.main) + .sink { [weak self] newAvatarURL in + self?.avatarURL = newAvatarURL + } + .store(in: &cancellables) + + // Subscribe to isLoggedIn changes to reset progress when logging out + authStateProvider.$isLoggedIn + .receive(on: DispatchQueue.main) + .sink { [weak self] isLoggedIn in + guard let self = self else { return } + if !isLoggedIn { + self.progress = UX.defaultProgress + } + } + .store(in: &cancellables) + + // Keep existing notification-based observers for progress and level-up + progressObserver = NotificationCenter.default.addObserver( + forName: .EcosiaAccountProgressUpdated, + object: nil, + queue: .main + ) { [weak self] notification in + self?.handleProgressUpdate(notification) + } + + levelUpObserver = NotificationCenter.default.addObserver( + forName: .EcosiaAccountLevelUp, + object: nil, + queue: .main + ) { [weak self] notification in + self?.handleLevelUp(notification) + } + } + + nonisolated private func handleProgressUpdate(_ notification: Notification) { + guard let userInfo = notification.userInfo, + let newProgress = userInfo[EcosiaAccountNotificationKeys.progress] as? Double else { + return + } + + Task { @MainActor in + updateProgress(newProgress) + } + } + + nonisolated private func handleLevelUp(_ notification: Notification) { + guard let userInfo = notification.userInfo, + let newLevel = userInfo[EcosiaAccountNotificationKeys.newLevel] as? Int, + let newProgress = userInfo[EcosiaAccountNotificationKeys.newProgress] as? Double else { + return + } + + Task { @MainActor in + currentLevelNumber = newLevel + triggerLevelUpAnimation(targetProgress: newProgress) + } + } +} + +#if DEBUG +@available(iOS 16.0, *) +extension EcosiaAccountAvatarViewModel { + static func preview( + avatarURL: URL? = URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + progress: Double = 0.75, + showSparkles: Bool = false + ) -> EcosiaAccountAvatarViewModel { + let viewModel = EcosiaAccountAvatarViewModel( + avatarURL: avatarURL, + progress: progress + ) + + if showSparkles { + viewModel.showSparkles = true + } + + return viewModel + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountImpactView.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountImpactView.swift new file mode 100644 index 0000000000000..9fea224bb51e3 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountImpactView.swift @@ -0,0 +1,234 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A SwiftUI view that displays account impact information for both logged-in and guest users +@available(iOS 16.0, *) +public struct EcosiaAccountImpactView: View { + @ObservedObject private var viewModel: EcosiaAccountImpactViewModel + @ObservedObject private var authStateProvider = EcosiaAuthUIStateProvider.shared + private let windowUUID: WindowUUID + + @State private var theme = EcosiaAccountImpactViewTheme() + @State private var showSeedsCounterInfoWebView = false + @State private var showProfileWebView = false + @State private var showSparkles = false + + /// Layout configuration optimized for account impact cards + private var impactCardLayout: NudgeCardLayout { + NudgeCardLayout( + imageSize: UX.imageImpactWidthHeight, + closeButtonSize: UX.closeButtonSize, + horizontalSpacing: .ecosia.space._m, + borderWidth: UX.borderWidth + ) + } + + public init( + viewModel: EcosiaAccountImpactViewModel, + windowUUID: WindowUUID + ) { + self.viewModel = viewModel + self.windowUUID = windowUUID + } + + public var body: some View { + VStack(spacing: .ecosia.space._l) { + // User info section with avatar (always present) + HStack(alignment: .center, spacing: .ecosia.space._m) { + EcosiaAccountProgressAvatar( + avatarURL: viewModel.avatarURL, + progress: viewModel.levelProgress, + showSparkles: showSparkles, + showProgress: !authStateProvider.hasRegisterVisitError, + windowUUID: windowUUID, + onLevelUpAnimationComplete: { + showSparkles = false + } + ) + + VStack(alignment: .leading, spacing: .ecosia.space._1s) { + Text(viewModel.userDisplayText) + .font(.ecosia(size: .ecosia.font._2l, weight: .bold)) + .foregroundColor(theme.textPrimaryColor) + .accessibilityIdentifier("account_impact_username") + .frame(minHeight: 25) + + Text(viewModel.levelDisplayText) + .font(.ecosia(size: .ecosia.font._s, weight: .medium)) + .foregroundColor(theme.levelTextColor) + .padding(.horizontal, 8) + .padding(.vertical, 2) + .background( + Capsule() + .fill(theme.levelBackgroundColor) + ) + .accessibilityLabel(String(format: .localized(.userLevelAccessibilityLabel), viewModel.levelDisplayText)) + .accessibilityIdentifier("account_impact_level") + } + } + .padding(.horizontal, .ecosia.space._m) + .frame(maxWidth: .infinity, alignment: .leading) + .accessibilityElement(children: .combine) + + // Show error view if register visit failed + if authStateProvider.hasRegisterVisitError { + EcosiaErrorView( + title: String.localized(.couldNotLoadSeedCounter), + subtitle: String.localized(.couldNotLoadSeedCounterMessage), + windowUUID: windowUUID + ) + .padding(.horizontal, .ecosia.space._m) + } + + // Conditional content based on login state + ZStack(alignment: .top) { + if !viewModel.isLoggedIn { + EcosiaAccountSignedOutView( + viewModel: viewModel, + windowUUID: windowUUID, + theme: theme, + onLearnMoreTap: { + showSeedsCounterInfoWebView = true + } + ) + .transition(.opacity) + .zIndex(0) + } + + if viewModel.isLoggedIn { + EcosiaAccountSignedInView( + viewModel: viewModel, + windowUUID: windowUUID, + theme: theme, + onProfileTap: { + showProfileWebView = true + }, + onSignOutTap: { + Task { + await viewModel.handleLogout() + } + } + ) + .transition(.opacity) + .zIndex(1) + } + } + .animation(.easeInOut(duration: 0.3), value: viewModel.isLoggedIn) + } + .ecosiaThemed(windowUUID, $theme) + .onChange(of: authStateProvider.currentLevelNumber) { _ in + let themeManager = AppContainer.shared.resolve() as ThemeManager + theme.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + } + .onReceive(NotificationCenter.default.publisher(for: .EcosiaAccountLevelUp)) { _ in + showSparkles = true + } + .sheet(isPresented: $showSeedsCounterInfoWebView) { + EcosiaWebViewModal( + url: EcosiaEnvironment.current.urlProvider.seedCounterInfo, + windowUUID: windowUUID + ) + } + .sheet(isPresented: $showProfileWebView) { + EcosiaWebViewModal( + url: EcosiaEnvironment.current.urlProvider.profileURL, + windowUUID: windowUUID, + onLoadComplete: { + Analytics.shared.accountProfileViewed() + }, + onDismiss: { + Analytics.shared.accountProfileDismissed() + } + ) + } + } + + // MARK: - UX Constants + private enum UX { + static let closeButtonSize: CGFloat = 15 + static let closeButtonBackgroundSize: CGFloat = 30 + static let imageImpactWidthHeight: CGFloat = 80 + static let borderWidth: CGFloat = 1 + } +} + +/// Theme configuration for EcosiaAccountImpactView +@available(iOS 16.0, *) +public struct EcosiaAccountImpactViewTheme: EcosiaThemeable { + public var backgroundColor = Color.white + public var cardBackgroundColor = Color.white + public var textPrimaryColor = Color.black + public var textSecondaryColor = Color.gray + public var cardActionButtonTextColor = Color.blue + public var cardCloseButtonColor = Color.gray.opacity(0.3) + public var signInButtonBackgroundColor = Color.green + public var signInButtonTextColor = Color.green + public var signOutImageTintColor = Color.green + public var signOutButtonTextColor = Color.green + public var borderColor = Color.gray.opacity(0.2) + public var avatarPlaceholderColor = Color.gray.opacity(0.3) + public var avatarIconColor = Color.gray + public var levelTextColor = Color.white + public var levelBackgroundColor = Color.black + + public init() {} + + public mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.ecosia.backgroundPrimary) + cardBackgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + cardActionButtonTextColor = Color(theme.colors.ecosia.buttonContentSecondary) + cardCloseButtonColor = Color(theme.colors.ecosia.buttonContentSecondary) + textPrimaryColor = Color(theme.colors.ecosia.textPrimary) + textSecondaryColor = Color(theme.colors.ecosia.textSecondary) + signInButtonBackgroundColor = Color(theme.colors.ecosia.buttonBackgroundFeatured) + signInButtonTextColor = Color(theme.colors.ecosia.buttonContentSecondaryStatic) + signOutImageTintColor = Color(theme.colors.ecosia.buttonContentSecondary) + signOutButtonTextColor = Color(theme.colors.ecosia.buttonContentSecondary) + borderColor = Color(theme.colors.ecosia.borderDecorative) + avatarPlaceholderColor = Color(theme.colors.ecosia.backgroundTertiary) + avatarIconColor = Color(theme.colors.ecosia.backgroundPrimary) + + if EcosiaAuthUIStateProvider.shared.currentLevelNumber > 1 { + levelTextColor = Color(theme.colors.ecosia.textStaticDark) + levelBackgroundColor = Color(theme.colors.ecosia.brandImpact) + } else { + levelTextColor = Color(theme.colors.ecosia.textInversePrimary) + levelBackgroundColor = Color(theme.colors.ecosia.backgroundNeutralInverse) + } + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaAccountImpactView_Previews: PreviewProvider { + static var previews: some View { + Group { + // Guest user state + EcosiaAccountImpactView( + viewModel: EcosiaAccountImpactViewModel( + onLogin: {}, + onDismiss: {} + ), + windowUUID: .XCTestDefaultUUID + ) + .previewDisplayName("Guest User") + + // Logged in user state + EcosiaAccountImpactView( + viewModel: EcosiaAccountImpactViewModel( + onLogin: {}, + onDismiss: {} + ), + windowUUID: .XCTestDefaultUUID + ) + .previewDisplayName("Logged In User") + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountImpactViewModel.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountImpactViewModel.swift new file mode 100644 index 0000000000000..bc17b79c0c2bd --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountImpactViewModel.swift @@ -0,0 +1,128 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common +import Combine + +/// ViewModel for the EcosiaAccountImpactView that uses centralized auth state +@available(iOS 16.0, *) +@MainActor +public class EcosiaAccountImpactViewModel: ObservableObject { + // MARK: - Published Properties + @Published public var isLoading: Bool = false + + // MARK: - Private Properties + private let onLoginAction: () -> Void + private let onDismissAction: () -> Void + private let authStateProvider: EcosiaAuthUIStateProvider + private var cancellables = Set() + + // MARK: - Initialization + public init( + onLogin: @escaping () -> Void, + onDismiss: @escaping () -> Void + ) { + self.onLoginAction = onLogin + self.onDismissAction = onDismiss + self.authStateProvider = EcosiaAuthUIStateProvider.shared + + // Forward objectWillChange notifications from authStateProvider + // This ensures SwiftUI knows to update the view when auth state changes + authStateProvider.objectWillChange + .sink { [weak self] _ in + self?.objectWillChange.send() + } + .store(in: &cancellables) + } + + // MARK: - Computed Properties + + /// Current authentication status from centralized provider + public var isLoggedIn: Bool { + authStateProvider.isLoggedIn + } + + /// Current username from centralized provider + public var username: String? { + authStateProvider.username + } + + /// Current avatar URL from centralized provider + public var avatarURL: URL? { + authStateProvider.avatarURL + } + + /// Current seed count from centralized provider + public var seedCount: Int { + authStateProvider.seedCount + } + + // MARK: - Public Methods + + /// Handles the main CTA button tap (login for guests, or custom action for logged-in users) + public func handleMainCTATap() { + Analytics.shared.accountImpactSignUpClicked() + + if isLoggedIn { + // For logged-in users, this could be a different action + // For now, we'll just dismiss + handleDismiss() + } else { + handleLogin() + } + } + + /// Handles the login action + public func handleLogin() { + isLoading = true + onLoginAction() + // Note: The loading state will be updated when the parent view updates the auth state + } + + /// Handles dismissing the view + public func handleDismiss() { + Analytics.shared.accountImpactCloseClicked() + onDismissAction() + } + + /// Handles the "Learn more about seeds" link tap + public func handleLearnMoreTap() { + Analytics.shared.accountImpactCardCtaClicked() + } + + /// Handles the logout action by delegating to Auth.shared + public func handleLogout() async { + do { + try await EcosiaAuthenticationService.shared.logout() + } catch { + EcosiaLogger.auth.error("Failed to logout: \(error)") + } + } +} + +// MARK: - Computed Properties +@available(iOS 16.0, *) +extension EcosiaAccountImpactViewModel { + + /// The text to display for the user section + public var userDisplayText: String { + authStateProvider.userDisplayText + } + + /// The text for the main CTA button + public var mainCTAText: String { + String.localized(.signUp) + } + + /// The level text to display - always shows the level based on seed count + public var levelDisplayText: String { + authStateProvider.levelDisplayText + } + + /// Progress for the avatar (0.0 to 1.0) + public var levelProgress: Double { + authStateProvider.levelProgress + } +} diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountNotifications.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountNotifications.swift new file mode 100644 index 0000000000000..29db100062cf5 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountNotifications.swift @@ -0,0 +1,66 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Notification names for account progress and level changes +extension Notification.Name { + /// Posted when account progress is updated + /// UserInfo may contain: progress (Double), level (Int) + public static let EcosiaAccountProgressUpdated = Notification.Name("EcosiaAccountProgressUpdated") + + /// Posted when user levels up + /// UserInfo may contain: newLevel (Int), newProgress (Double) + public static let EcosiaAccountLevelUp = Notification.Name("EcosiaAccountLevelUp") +} + +/// Keys for notification userInfo dictionaries +public struct EcosiaAccountNotificationKeys { + public static let progress = "progress" + public static let level = "level" + public static let newLevel = "newLevel" + public static let newProgress = "newProgress" +} + +/// Helper class for posting account progress notifications +public final class EcosiaAccountNotificationCenter { + + /// Posts a progress updated notification + /// - Parameters: + /// - progress: The new progress value (0.0 to 1.0) + /// - level: Optional level information + public static func postProgressUpdated(progress: Double, level: Int? = nil) { + var userInfo: [String: Any] = [EcosiaAccountNotificationKeys.progress: progress] + if let level = level { + userInfo[EcosiaAccountNotificationKeys.level] = level + } + + DispatchQueue.main.async { + NotificationCenter.default.post( + name: .EcosiaAccountProgressUpdated, + object: nil, + userInfo: userInfo + ) + } + } + + /// Posts a level up notification + /// - Parameters: + /// - newLevel: The new level reached + /// - newProgress: The new progress value (0.0 to 1.0) + public static func postLevelUp(newLevel: Int, newProgress: Double) { + let userInfo: [String: Any] = [ + EcosiaAccountNotificationKeys.newLevel: newLevel, + EcosiaAccountNotificationKeys.newProgress: newProgress + ] + + DispatchQueue.main.async { + NotificationCenter.default.post( + name: .EcosiaAccountLevelUp, + object: nil, + userInfo: userInfo + ) + } + } +} diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountProgressAvatar.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountProgressAvatar.swift new file mode 100644 index 0000000000000..e25224c4000c8 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountProgressAvatar.swift @@ -0,0 +1,294 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +@available(iOS 16.0, *) +public struct EcosiaAccountProgressAvatar: View { + private let avatarURL: URL? + private let progress: Double + private let showSparkles: Bool + private let showProgress: Bool + private let size: CGFloat + private let windowUUID: WindowUUID + private let onLevelUpAnimationComplete: (() -> Void)? + + public init( + avatarURL: URL?, + progress: Double, + showSparkles: Bool = false, + showProgress: Bool = false, + size: CGFloat = .ecosia.space._7l, + windowUUID: WindowUUID, + onLevelUpAnimationComplete: (() -> Void)? = nil + ) { + self.avatarURL = avatarURL + self.progress = max(0.0, min(1.0, progress)) + self.showSparkles = showSparkles + self.showProgress = showProgress + self.size = size + self.windowUUID = windowUUID + self.onLevelUpAnimationComplete = onLevelUpAnimationComplete + } + + public var body: some View { + ZStack { + // Progress ring (outermost layer) - only shown if showProgress is true + if showProgress { + EcosiaAccountProgressBar( + progress: progress, + size: progressRingSize, + strokeWidth: strokeWidth, + windowUUID: windowUUID + ) + } + + EcosiaAvatar( + avatarURL: avatarURL, + size: avatarSize + ) + + // Sparkles animation (overlay) - only shown if both showSparkles and showProgress are true + if showSparkles && showProgress { + EcosiaSparkleAnimation( + isVisible: showSparkles, + containerSize: sparkleContainerSize, + sparkleSize: sparkleSize, + onComplete: onLevelUpAnimationComplete + ) + } + } + .frame(width: progressRingSize, height: progressRingSize) + } + + private struct UX { + static let strokeWidthRatio: CGFloat = 0.06 + static let sparkleContainerPadding: CGFloat = .ecosia.space._s + static let sparkleSizeRatio: CGFloat = 0.2 + } + + private var strokeWidth: CGFloat { + size * UX.strokeWidthRatio + } + + private var avatarSize: CGFloat { + size - (strokeWidth * 2) + } + + private var progressRingSize: CGFloat { + size + } + + private var sparkleContainerSize: CGFloat { + size + UX.sparkleContainerPadding + } + + private var sparkleSize: CGFloat { + size * UX.sparkleSizeRatio + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaAccountProgressAvatar_Previews: PreviewProvider { + static var previews: some View { + EcosiaAccountProgressAvatarInteractivePreview() + } +} + +@available(iOS 16.0, *) +private struct EcosiaAccountProgressAvatarInteractivePreview: View { + @StateObject private var viewModel = EcosiaAccountAvatarViewModel(progress: 0.3) + let windowUUID = WindowUUID() + + var body: some View { + ScrollView { + VStack(spacing: .ecosia.space._2l) { + Text("Interactive Testing") + .font(.title2.bold()) + + EcosiaAccountProgressAvatar( + avatarURL: viewModel.avatarURL, + progress: viewModel.progress, + showSparkles: viewModel.showSparkles, + showProgress: true, // Enable progress in preview + size: .ecosia.space._7l, + windowUUID: windowUUID + ) + + VStack(spacing: .ecosia.space._s) { + Text("Level \(viewModel.currentLevelNumber)") + .font(.headline) + + Text("Seeds: \(viewModel.seedCount)") + .font(.subheadline) + + Text("Progress: \(Int(viewModel.progress * 100))%") + .font(.caption) + + Text("Levels from API (sign in to see real data)") + .font(.caption2) + .foregroundColor(.secondary) + } + + VStack(spacing: .ecosia.space._s) { + HStack { + Button("Add Seed") { + viewModel.updateSeedCount(viewModel.seedCount + 1) + } + .buttonStyle(.bordered) + + Button("Add 10 Seeds") { + viewModel.updateSeedCount(viewModel.seedCount + 10) + } + .buttonStyle(.bordered) + + Button("Add 100 Seeds") { + viewModel.updateSeedCount(viewModel.seedCount + 100) + } + .buttonStyle(.bordered) + + Button("Reset") { + viewModel.updateSeedCount(0) + } + .buttonStyle(.bordered) + } + + HStack { + Button("Add Avatar") { + viewModel.updateAvatarURL(URL(string: "https://avatars.githubusercontent.com/u/1?v=4")) + } + .buttonStyle(.bordered) + + Button("Remove Avatar") { + viewModel.updateAvatarURL(nil) + } + .buttonStyle(.bordered) + } + + HStack { + Button("Level 5") { + viewModel.updateSeedCount(30) + } + .buttonStyle(.bordered) + + Button("Level 10") { + viewModel.updateSeedCount(200) + } + .buttonStyle(.bordered) + + Button("Level 20") { + viewModel.updateSeedCount(1200) + } + .buttonStyle(.bordered) + } + + HStack { + Button("Test Sparkles") { + viewModel.triggerSparkles() + } + .buttonStyle(.bordered) + } + } + + Divider() + + Text("State Variations") + .font(.title2.bold()) + + LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 2), spacing: .ecosia.space._l) { + + VStack { + Text("Hidden Progress") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: nil, + progress: 0.25, + showProgress: false, + windowUUID: windowUUID + ) + } + + VStack { + Text("Signed In (75%)") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + progress: 0.75, + showProgress: true, + windowUUID: windowUUID + ) + } + + VStack { + Text("Complete (100%)") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: nil, + progress: 1.0, + showProgress: true, + windowUUID: windowUUID + ) + } + + VStack { + Text("Large Size") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + progress: 0.6, + showProgress: true, + size: .ecosia.space._8l, + windowUUID: windowUUID + ) + } + } + + Divider() + + Text("Size Variations") + .font(.title2.bold()) + + HStack(spacing: .ecosia.space._l) { + VStack { + Text("Small") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + progress: 0.6, + size: .ecosia.space._4l, + windowUUID: windowUUID + ) + } + + VStack { + Text("Medium") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + progress: 0.6, + size: .ecosia.space._6l, + windowUUID: windowUUID + ) + } + + VStack { + Text("Large") + .font(.caption) + EcosiaAccountProgressAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + progress: 0.6, + size: .ecosia.space._8l, + windowUUID: windowUUID + ) + } + } + } + .padding() + } + .navigationTitle("EcosiaAccountProgressAvatar") + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountProgressBar.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountProgressBar.swift new file mode 100644 index 0000000000000..f42ded4a98299 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountProgressBar.swift @@ -0,0 +1,153 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A circular progress bar that displays progress as a ring around content +@available(iOS 16.0, *) +public struct EcosiaAccountProgressBar: View { + private let progress: Double + private let size: CGFloat + private let strokeWidth: CGFloat + private let windowUUID: WindowUUID + @State private var theme = EcosiaAccountProgressBarTheme() + + private struct UX { + static let minimumVisibleProgress: Double = 0.1 // 10% minimum display + } + + public init( + progress: Double, + size: CGFloat = .ecosia.space._6l, + strokeWidth: CGFloat = 4, + windowUUID: WindowUUID + ) { + self.progress = max(0.0, min(1.0, progress)) // Clamp between 0.0 and 1.0 + self.size = size + self.strokeWidth = strokeWidth + self.windowUUID = windowUUID + } + + /// Ensures a minimum of 10% progress is shown when progress is at 0% + private var displayProgress: Double { + guard progress > 0.0 else { + return UX.minimumVisibleProgress + } + return progress + } + + public var body: some View { + ZStack { + // Background ring (unfilled) + Circle() + .stroke( + theme.backgroundColor, + style: StrokeStyle( + lineWidth: strokeWidth, + lineCap: .round + ) + ) + + // Progress ring (filled) + Circle() + .trim(from: 0.0, to: displayProgress) + .stroke( + theme.progressColor, + style: StrokeStyle( + lineWidth: strokeWidth, + lineCap: .round + ) + ) + .rotationEffect(.degrees(90)) + .animation(.easeInOut(duration: 0.5), value: displayProgress) + } + .frame(width: size, height: size) + .ecosiaThemed(windowUUID, $theme) + } +} + +// MARK: - Theme +struct EcosiaAccountProgressBarTheme: EcosiaThemeable { + var progressColor = Color.primary + var backgroundColor = Color.secondary + + mutating func applyTheme(theme: Theme) { + progressColor = Color(theme.colors.ecosia.brandImpact) + backgroundColor = Color(theme.colors.ecosia.borderDecorative) + } +} + +#if DEBUG +// MARK: - Preview +@available(iOS 16.0, *) +struct EcosiaAccountProgressBar_Previews: PreviewProvider { + static var previews: some View { + let windowUUID = WindowUUID() + + VStack(spacing: .ecosia.space._l) { + // 0% progress - Shows minimum 10% display + VStack(spacing: .ecosia.space._2s) { + EcosiaAccountProgressBar( + progress: 0.0, + windowUUID: windowUUID + ) + Text("0% (shows 10% minimum)") + .font(.caption) + .foregroundColor(.secondary) + } + + // 25% progress + VStack(spacing: .ecosia.space._2s) { + EcosiaAccountProgressBar( + progress: 0.25, + windowUUID: windowUUID + ) + Text("25% progress") + .font(.caption) + .foregroundColor(.secondary) + } + + // 75% progress + VStack(spacing: .ecosia.space._2s) { + EcosiaAccountProgressBar( + progress: 0.75, + windowUUID: windowUUID + ) + Text("75% progress") + .font(.caption) + .foregroundColor(.secondary) + } + + // 100% progress (complete) + VStack(spacing: .ecosia.space._2s) { + EcosiaAccountProgressBar( + progress: 1.0, + windowUUID: windowUUID + ) + Text("100% (complete)") + .font(.caption) + .foregroundColor(.secondary) + } + + Divider() + + // Large size example + VStack(spacing: .ecosia.space._2s) { + EcosiaAccountProgressBar( + progress: 0.5, + size: .ecosia.space._8l, + strokeWidth: 6, + windowUUID: windowUUID + ) + Text("50% (larger size)") + .font(.caption) + .foregroundColor(.secondary) + } + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountSignedInView.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountSignedInView.swift new file mode 100644 index 0000000000000..ab34b70ca028e --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountSignedInView.swift @@ -0,0 +1,158 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A SwiftUI view that displays the signed-in state of the account impact view +@available(iOS 16.0, *) +public struct EcosiaAccountSignedInView: View { + @ObservedObject private var viewModel: EcosiaAccountImpactViewModel + @State private var theme = EcosiaAccountSignedInViewTheme() + private let windowUUID: WindowUUID + private let onProfileTap: () -> Void + private let onSignOutTap: () -> Void + + public init( + viewModel: EcosiaAccountImpactViewModel, + windowUUID: WindowUUID, + theme: EcosiaAccountImpactViewTheme? = nil, + onProfileTap: @escaping () -> Void, + onSignOutTap: @escaping () -> Void + ) { + self.viewModel = viewModel + self.windowUUID = windowUUID + self.onProfileTap = onProfileTap + self.onSignOutTap = onSignOutTap + + if let theme = theme { + self._theme = State(initialValue: EcosiaAccountSignedInViewTheme(from: theme)) + } + } + + public var body: some View { + VStack(alignment: .leading, spacing: .ecosia.space._m) { + VStack(alignment: .leading, spacing: 0) { + // "Your Ecosia" section title + ZStack { + Rectangle() + .fill(.clear) + .frame(height: UX.yourEcosiaSectionHeight) + Text(String.localized(.yourEcosia)) + .font(.footnote) + .foregroundColor(theme.yourEcosiaTextColor) + .padding(.leading, .ecosia.space._s) + .frame(maxWidth: .infinity, alignment: .leading) + .accessibilityIdentifier("account_signed_in_title") + } + // "Your profile" section title + Button(action: { + Analytics.shared.accountProfileClicked() + onProfileTap() + }) { + ZStack { + RoundedRectangle(cornerRadius: .ecosia.borderRadius._l) + .fill(theme.yourProfileBackground) + .frame(height: UX.yourProfileButtonHeight) + Text(String.localized(.yourProfile)) + .font(.subheadline) + .foregroundColor(theme.yourProfileTextColor) + .padding(.leading, .ecosia.space._s) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .accessibilityIdentifier("account_profile_button") + .accessibilityLabel(String.localized(.yourProfile)) + .accessibilityHint(String.localized(.profileButtonAccessibilityHint)) + .accessibilityAddTraits(.isButton) + } + + // Sign Out button + Button(action: { + Analytics.shared.accountSignOutClicked() + onSignOutTap() + }) { + HStack(alignment: .center, spacing: .ecosia.space._2s) { + Image("sign-out", bundle: .ecosia) + .renderingMode(.template) + .resizable() + .frame(width: UX.ctaImageSize, height: UX.ctaImageSize) + .foregroundColor(theme.ctaButtonImageTintColor) + + Text(String.localized(.signOut)) + .font(.subheadline) + .foregroundColor(theme.ctaButtonTextColor) + .frame(height: UX.ctaButtonHeight) + } + .frame(maxWidth: .infinity) + } + .accessibilityIdentifier("account_sign_out_button") + .accessibilityLabel(String.localized(.signOut)) + .accessibilityHint(String.localized(.signOutButtonAccessibilityHint)) + .accessibilityAddTraits(.isButton) + } + .padding(.horizontal, .ecosia.space._m) + .frame(maxWidth: .infinity, alignment: .leading) + .ecosiaThemed(windowUUID, $theme) + } + + // MARK: - UX Constants + private enum UX { + static let ctaImageSize: CGFloat = 16 + static let yourEcosiaSectionHeight: CGFloat = 40 + static let yourProfileButtonHeight: CGFloat = 40 + static let ctaButtonHeight: CGFloat = 40 + } +} + +/// Theme configuration for EcosiaAccountSignedInView +@available(iOS 16.0, *) +public struct EcosiaAccountSignedInViewTheme: EcosiaThemeable { + public var yourProfileTextColor = Color.black + public var yourEcosiaTextColor = Color.gray + public var yourProfileBackground = Color.white + public var ctaButtonTextColor = Color.blue + public var ctaButtonImageTintColor = Color.blue + + public init() {} + + public mutating func applyTheme(theme: Theme) { + yourProfileTextColor = Color(theme.colors.ecosia.textPrimary) + yourEcosiaTextColor = Color(theme.colors.ecosia.textSecondary) + yourProfileBackground = Color(theme.colors.ecosia.backgroundElevation1) + ctaButtonTextColor = Color(theme.colors.ecosia.buttonContentSecondary) + ctaButtonImageTintColor = Color(theme.colors.ecosia.buttonContentSecondary) + } +} + +@available(iOS 16.0, *) +extension EcosiaAccountSignedInViewTheme { + + public init(from parentTheme: EcosiaAccountImpactViewTheme) { + self.yourProfileTextColor = parentTheme.textPrimaryColor + self.yourEcosiaTextColor = parentTheme.textSecondaryColor + self.yourProfileBackground = parentTheme.cardBackgroundColor + self.ctaButtonTextColor = parentTheme.cardActionButtonTextColor + self.ctaButtonImageTintColor = parentTheme.cardActionButtonTextColor + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaAccountSignedInView_Previews: PreviewProvider { + static var previews: some View { + EcosiaAccountSignedInView( + viewModel: EcosiaAccountImpactViewModel( + onLogin: {}, + onDismiss: {} + ), + windowUUID: .XCTestDefaultUUID, + onProfileTap: { print("Profile tapped") }, + onSignOutTap: { print("Sign out tapped") } + ) + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAccountSignedOutView.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAccountSignedOutView.swift new file mode 100644 index 0000000000000..3534bcc1ec160 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAccountSignedOutView.swift @@ -0,0 +1,213 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A SwiftUI view that displays the signed-out state of the account impact view +@available(iOS 16.0, *) +public struct EcosiaAccountSignedOutView: View { + @ObservedObject private var viewModel: EcosiaAccountImpactViewModel + private let windowUUID: WindowUUID + private let onLearnMoreTap: () -> Void + @State private var theme = EcosiaAccountSignedOutViewTheme() + @State private var isCardDismissed: Bool + @State private var cardHeight: CGFloat? + @State private var opacity: Double = 1 + @StateObject private var nudgeCardDelegate = NudgeCardActionHandler() + + /// Layout configuration optimized for account impact cards + private var impactCardLayout: NudgeCardLayout { + NudgeCardLayout( + imageSize: UX.imageImpactWidthHeight, + closeButtonSize: UX.closeButtonSize, + closeButtonPaddingTop: .ecosia.space._s, + closeButtonPaddingLeading: .ecosia.space._s, + closeButtonPaddingBottom: .ecosia.space._s, + closeButtonPaddingTrailing: .ecosia.space._s, + horizontalSpacing: .ecosia.space._m, + borderWidth: UX.borderWidth + ) + } + + public init( + viewModel: EcosiaAccountImpactViewModel, + windowUUID: WindowUUID, + theme: EcosiaAccountImpactViewTheme? = nil, + onLearnMoreTap: @escaping () -> Void + ) { + self.viewModel = viewModel + self.windowUUID = windowUUID + self.onLearnMoreTap = onLearnMoreTap + self._isCardDismissed = State(initialValue: !User.shared.shouldShowAccountImpactNudgeCard) + + if let theme = theme { + self._theme = State(initialValue: EcosiaAccountSignedOutViewTheme(from: theme)) + } + } + + public var body: some View { + VStack(alignment: .leading, spacing: .ecosia.space._l) { + // Impact card - with height animation to make sure the dismissal is as smooth as possible + ConfigurableNudgeCardView( + viewModel: NudgeCardViewModel( + title: String.localized(.seedsSymbolizeYourOwnImpact), + description: String.localized(.collectSeedsEveryDayYouUse), + buttonText: String.localized(.learnMoreAboutSeeds), + image: UIImage(named: "account-menu-impact-flag", in: .ecosia, with: nil), + showsCloseButton: true, + style: NudgeCardStyle( + backgroundColor: theme.cardBackgroundColor, + textPrimaryColor: theme.cardTextPrimaryColor, + textSecondaryColor: theme.cardTextSecondaryColor, + closeButtonTextColor: theme.cardCloseButtonColor, + actionButtonTextColor: theme.cardActionButtonTextColor + ), + layout: impactCardLayout + ), + delegate: nudgeCardDelegate + ) + .background( + GeometryReader { geometry in + Color.clear.preference( + key: CardHeightPreferenceKey.self, + value: geometry.size.height + ) + } + ) + .onPreferenceChange(CardHeightPreferenceKey.self) { height in + if cardHeight == nil && height > 0 { + cardHeight = height + } + } + .frame(height: isCardDismissed ? 0 : cardHeight) + .opacity(isCardDismissed ? 0 : 1) + .clipped() + .opacity(opacity) + + // Sign Up CTA button + Button(action: viewModel.handleMainCTATap) { + Text(viewModel.mainCTAText) + .font(.subheadline) + .padding(.ecosia.space._m) + .frame(maxWidth: .infinity) + .frame(height: UX.ctaButtonHeight) + .cornerRadius(.ecosia.borderRadius._m) + .background(theme.ctaButtonBackgroundColor) + .foregroundColor(theme.ctaButtonTextColor) + } + .clipShape(Capsule()) + .accessibilityIdentifier("account_impact_cta_button") + .accessibilityLabel(viewModel.mainCTAText) + .accessibilityAddTraits(.isButton) + } + .ecosiaThemed(windowUUID, $theme) + .onAppear { + nudgeCardDelegate.onActionTap = { + viewModel.handleLearnMoreTap() + onLearnMoreTap() + } + nudgeCardDelegate.onDismissTap = { + User.shared.hideAccountImpactNudgeCard() + Analytics.shared.accountImpactCardDismissClicked() + isCardDismissed = true + } + } + } + + // MARK: - UX Constants + private enum UX { + static let closeButtonSize: CGFloat = 15 + static let imageImpactWidthHeight: CGFloat = 80 + static let borderWidth: CGFloat = 1 + static let ctaButtonHeight: CGFloat = 40 + } +} + +/// Theme configuration for EcosiaAccountSignedOutView +@available(iOS 16.0, *) +public struct EcosiaAccountSignedOutViewTheme: EcosiaThemeable { + public var cardBackgroundColor = Color.white + public var cardTextPrimaryColor = Color.black + public var cardTextSecondaryColor = Color.gray + public var cardCloseButtonColor = Color.black + public var cardActionButtonTextColor = Color.blue + public var ctaButtonTextColor = Color.green + public var ctaButtonBackgroundColor = Color.green + + public init() {} + + public mutating func applyTheme(theme: Theme) { + cardBackgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + cardTextPrimaryColor = Color(theme.colors.ecosia.textPrimary) + cardTextSecondaryColor = Color(theme.colors.ecosia.textSecondary) + cardActionButtonTextColor = Color(theme.colors.ecosia.buttonContentSecondary) + cardCloseButtonColor = Color(theme.colors.ecosia.buttonContentSecondary) + ctaButtonTextColor = Color(theme.colors.ecosia.buttonContentSecondaryStatic) + ctaButtonBackgroundColor = Color(theme.colors.ecosia.buttonBackgroundFeatured) + } +} + +@available(iOS 16.0, *) +extension EcosiaAccountSignedOutViewTheme { + + public init(from parentTheme: EcosiaAccountImpactViewTheme) { + self.cardBackgroundColor = parentTheme.cardBackgroundColor + self.cardTextPrimaryColor = parentTheme.textPrimaryColor + self.cardTextSecondaryColor = parentTheme.textSecondaryColor + self.cardCloseButtonColor = parentTheme.cardCloseButtonColor + self.cardActionButtonTextColor = parentTheme.cardActionButtonTextColor + self.ctaButtonTextColor = parentTheme.signInButtonTextColor + self.ctaButtonBackgroundColor = parentTheme.signInButtonBackgroundColor + } +} + +// MARK: - Card Height Preference Key + +@available(iOS 16.0, *) +private struct CardHeightPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +// MARK: - Nudge Card Action Handler + +@available(iOS 16.0, *) +private class NudgeCardActionHandler: ObservableObject, ConfigurableNudgeCardActionDelegate { + var onActionTap: (() -> Void)? + var onDismissTap: (() -> Void)? + + func nudgeCardRequestToPerformAction() { + onActionTap?() + } + + func nudgeCardRequestToDimiss() { + onDismissTap?() + } + + func nudgeCardTapped() { + onActionTap?() + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaAccountSignedOutView_Previews: PreviewProvider { + static var previews: some View { + EcosiaAccountSignedOutView( + viewModel: EcosiaAccountImpactViewModel( + onLogin: {}, + onDismiss: {} + ), + windowUUID: .XCTestDefaultUUID, + onLearnMoreTap: { print("Learn more tapped") } + ) + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAuthUIStateProvider.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAuthUIStateProvider.swift new file mode 100644 index 0000000000000..c1bafa9cc4d7b --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAuthUIStateProvider.swift @@ -0,0 +1,365 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Combine +import Common + +/// Centralized, reactive authentication state provider for consistent UI state across all components +/// This eliminates the need for individual components to manage their own auth state observers +public class EcosiaAuthUIStateProvider: ObservableObject { + + /// Auth0 gives us back a Gravatar URL when no profile picture URL is provided from a resource (e.g. Sign In with Apple) + /// We want to strip it, therefore we track the URL + private let gravatarURL = URL(string: "https://s.gravatar.com/avatar/") + + // MARK: - Published Properties + + /// Current authentication status + @Published public private(set) var isLoggedIn: Bool = false + + /// Current user profile information + @Published public private(set) var userProfile: UserProfile? + + /// Current seed count (server-based for logged in users, local for guests) + /// Initialized with local storage value to prevent flickering on app launch + @Published public private(set) var seedCount: Int = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + + /// Current user avatar URL + @Published public private(set) var avatarURL: URL? + + /// Current username for display + @Published public private(set) var username: String? + + /// Balance increment for animations (temporary state) + @Published public private(set) var balanceIncrement: Int? + + /// Current level number (from API for logged-in users, 1 for logged-out) + @Published public private(set) var currentLevelNumber: Int = 1 + + /// Current progress towards next level (from API for logged-in users, default 0.25 for initial state) + @Published public private(set) var currentProgress: Double = 0.25 + + /// Error state for register visit failures (read-only externally, set only by this class) + @Published public private(set) var hasRegisterVisitError: Bool = false + + // MARK: - Private Properties + + private var authStateObserver: NSObjectProtocol? + private var userProfileObserver: NSObjectProtocol? + private var seedProgressObserver: NSObjectProtocol? + private let accountsProvider: AccountsProviderProtocol + private let authenticationService: EcosiaAuthenticationService + /// Normalizing the avatar to match Web's Product behaviour. + /// Our Auth Provider (Auth0) sends us a Gravatar URL when no profile image is retrieved from a user + /// (e.g. Apple Sign In). As of now, we replace it with our tree-image in `EcosiaAvatar` by not setting any URL + private var normalizedAvatarURL: URL? { + guard userProfile?.pictureURL?.baseDomain != gravatarURL?.baseDomain else { return nil } + return userProfile?.pictureURL + } + private static var seedProgressManagerType: SeedProgressManagerProtocol.Type = UserDefaultsSeedProgressManager.self + + // MARK: - Singleton + + /// Factory for creating accounts provider - can be configured before first access + public static var accountsProviderFactory: () -> AccountsProviderProtocol = { AccountsProvider() } + + /// Shared instance for app-wide auth state + public static let shared = EcosiaAuthUIStateProvider(accountsProvider: accountsProviderFactory()) + + public init(accountsProvider: AccountsProviderProtocol, authenticationService: EcosiaAuthenticationService = .shared) { + self.accountsProvider = accountsProvider + self.authenticationService = authenticationService + + // Initialize state synchronously to prevent flickering + self.isLoggedIn = authenticationService.isLoggedIn + self.userProfile = authenticationService.userProfile + self.avatarURL = normalizedAvatarURL + self.username = userProfile?.name + + // If logged out, ensure seed count is loaded (already done in property initializer) + // If logged in, seed count will be updated from API when the NTP header appears + + setupAuthStateMonitoring() + } + + deinit { + if let observer = authStateObserver { + NotificationCenter.default.removeObserver(observer) + } + if let observer = userProfileObserver { + NotificationCenter.default.removeObserver(observer) + } + if let observer = seedProgressObserver { + NotificationCenter.default.removeObserver(observer) + } + } + + // MARK: - Public Interface + + /// Computed property for user display text + public var userDisplayText: String { + username ?? String.localized(.guestUser) + } + + /// Computed property for level display text + /// Returns level number and name for logged-in users, empty string for logged-out users + public var levelDisplayText: String { + let levelNumber = currentLevelNumber + let levelName = GrowthPointsLevelSystem.levelName(for: levelNumber) + return "\(String.localized(.level)) \(levelNumber) - \(levelName)" + } + + /// Computed property for level progress (0.0 to 1.0) + /// Returns progress from API for logged-in users, 0.25 default for initial/logged-out state + public var levelProgress: Double { + guard isLoggedIn else { + return 0.25 // Default progress for logged-out users + } + return currentProgress + } + + // MARK: - Private Methods + + private func setupAuthStateMonitoring() { + // Listen for auth state changes + authStateObserver = NotificationCenter.default.addObserver( + forName: .EcosiaAuthStateChanged, + object: nil, + queue: .main + ) { [weak self] notification in + Task { + await self?.handleAuthStateChange(notification) + } + } + + // Listen for user profile updates + userProfileObserver = NotificationCenter.default.addObserver( + forName: .EcosiaUserProfileUpdated, + object: nil, + queue: .main + ) { [weak self] _ in + Task { + await self?.handleUserProfileUpdate() + } + } + + // Listen for seed progress updates (for logged-out users) + seedProgressObserver = NotificationCenter.default.addObserver( + forName: UserDefaultsSeedProgressManager.progressUpdatedNotification, + object: nil, + queue: .main + ) { [weak self] _ in + Task { + await self?.handleSeedProgressUpdate() + } + } + } + + private func handleAuthStateChange(_ notification: Notification) async { + // Handle specific auth actions (business logic can be nonisolated) + if let actionType = notification.userInfo?["actionType"] as? EcosiaAuthActionType { + switch actionType { + case .userLoggedIn: + EcosiaLogger.accounts.info("User logged in - registering visit") + registerVisitIfNeeded() + case .userLoggedOut: + EcosiaLogger.accounts.info("User logged out - resetting to local seed collection") + await resetToLocalSeedCollection() + await handleLocalSeedCollection() + case .authStateLoaded: + break // State already updated above + } + } + } + + @MainActor + private func handleUserProfileUpdate() { + Task { @MainActor in + isLoggedIn = authenticationService.isLoggedIn + } + userProfile = authenticationService.userProfile + username = userProfile?.name + avatarURL = normalizedAvatarURL + } + + @MainActor + private func handleSeedProgressUpdate() { + // Only handle for logged-out users + guard !isLoggedIn else { return } + + let newSeedCount = Self.seedProgressManagerType.loadTotalSeedsCollected() + + // If seed count increased, show animation + if newSeedCount > seedCount { + let increment = newSeedCount - seedCount + EcosiaLogger.accounts.info("Seed progress updated for logged-out user: \(seedCount) → \(newSeedCount) (+\(increment))") + animateBalanceChange(from: seedCount, to: newSeedCount, increment: increment) + } else { + seedCount = newSeedCount + } + } + + // MARK: - Seed Count Management + + /// Registers a user visit to fetch the latest balance from the backend. + /// + /// Only proceeds if a valid access token is available (user is logged in). + /// Updates the balance and level information on success. + /// Sets `hasRegisterVisitError` to `true` on failure. + private func registerVisitIfNeeded() { + Task { + do { + // Get fresh access token from Auth0 - automatically handles expiry and refresh + let accessToken = try await authenticationService.getFreshAccessToken() + + EcosiaLogger.accounts.info("Registering user visit for balance update") + let response = try await accountsProvider.registerVisit(accessToken: accessToken) + await updateBalance(response) + + // Clear error on success + await MainActor.run { + hasRegisterVisitError = false + } + } catch { + EcosiaLogger.accounts.debug("Could not register visit: \(error.localizedDescription)") + + // Set error state + await MainActor.run { + hasRegisterVisitError = true + } + } + } + } + + @MainActor + private func updateBalance(_ response: AccountVisitResponse) { + let newSeedCount = response.seeds.balanceAmount + let newLevelNumber = response.growthPoints.level.number + let newProgress = response.progressToNextLevel + + // Update level and progress from API + currentLevelNumber = newLevelNumber + currentProgress = newProgress + + // Trigger level-up animation if user leveled up + if response.didLevelUp { + EcosiaLogger.accounts.info("Level up detected: triggering animation for level \(newLevelNumber)") + triggerLevelUpAnimation() + } + + if let increment = response.seedsIncrement { + EcosiaLogger.accounts.info("Balance updated with animation: \(seedCount) → \(newSeedCount) (+\(increment)), level=\(newLevelNumber), progress=\(newProgress)") + animateBalanceChange(from: seedCount, to: newSeedCount, increment: increment) + } else { + EcosiaLogger.accounts.info("Balance updated without animation: \(seedCount) → \(newSeedCount), level=\(newLevelNumber), progress=\(newProgress)") + seedCount = newSeedCount + } + } + + @MainActor + private func animateBalanceChange(from oldValue: Int, to newValue: Int, increment: Int) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.balanceIncrement = increment + + withAnimation(.easeIn(duration: 0.3)) { + self.seedCount = newValue + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { + withAnimation(.linear(duration: 0.57)) { + self.balanceIncrement = nil + } + } + } + } + + @MainActor + private func triggerLevelUpAnimation() { + EcosiaAccountNotificationCenter.postLevelUp( + newLevel: currentLevelNumber, + newProgress: currentProgress + ) + } + + /// Resets to local seed collection system after logout. + /// + /// Resets seeds to 0, level to 1, and clears lastAppOpenDate to allow immediate seed collection. + @MainActor + private func resetToLocalSeedCollection() { + EcosiaLogger.accounts.info("Resetting to local seed collection system") + + Self.seedProgressManagerType.resetLocalSeedProgress() + + seedCount = Self.seedProgressManagerType.loadTotalSeedsCollected() + currentLevelNumber = 1 + currentProgress = 0.25 + } + + /// Handles daily seed collection for logged-out users. + /// + /// Collects one seed per day and animates the increment if a new seed was collected. + @MainActor + private func handleLocalSeedCollection() { + EcosiaLogger.accounts.info("Handling local seed collection for logged-out user") + Self.seedProgressManagerType.collectDailySeed() + let newSeedCount = Self.seedProgressManagerType.loadTotalSeedsCollected() + + if newSeedCount > seedCount { + let increment = newSeedCount - seedCount + animateBalanceChange(from: seedCount, to: newSeedCount, increment: increment) + } else { + seedCount = newSeedCount + } + } + + // MARK: - Public Methods + + /// Refreshes seed state based on authentication status. + /// + /// Should be called when the NTP appears or app returns from background. + /// - For logged-in users: Registers a visit to fetch latest balance from server + /// - For logged-out users: Checks and collects daily seed + @MainActor + public func refreshSeedState() { + if isLoggedIn { + EcosiaLogger.accounts.debug("Refreshing seed state for logged-in user (server fetch)") + registerVisitIfNeeded() + } else { + EcosiaLogger.accounts.debug("Refreshing seed state for logged-out user (daily seed check)") + handleLocalSeedCollection() + } + } + + // MARK: - Debug Methods + + /// Debug method to simulate balance updates for testing animations + /// This allows QA to test seed addition and level-up animations without server calls + /// Available in all builds, accessible through hidden debug menu + @MainActor + public func debugUpdateBalance(_ response: AccountVisitResponse) { + updateBalance(response) + EcosiaLogger.accounts.info("Debug: Balance updated via debug method") + } + + /// Debug method to directly trigger level-up animation without mock data + /// This allows QA to test the level-up sparkle animation independently + /// Available in all builds, accessible through hidden debug menu + @MainActor + public func debugTriggerLevelUpAnimation() { + triggerLevelUpAnimation() + EcosiaLogger.accounts.info("Debug: Level-up animation triggered directly") + } + + /// Debug method to directly add seeds with animation for logged-in users + /// This allows QA to test seed increment animations without mock responses + /// Available in all builds, accessible through hidden debug menu + @MainActor + public func debugAddSeeds(_ count: Int) { + let currentSeeds = seedCount + let newSeeds = currentSeeds + count + animateBalanceChange(from: currentSeeds, to: newSeeds, increment: count) + EcosiaLogger.accounts.info("Debug: Added \(count) seeds for logged-in user (\(currentSeeds) → \(newSeeds))") + } +} diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaAvatar.swift b/firefox-ios/Ecosia/UI/Account/EcosiaAvatar.swift new file mode 100644 index 0000000000000..1070c0ea51ccb --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaAvatar.swift @@ -0,0 +1,85 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +/// A reusable avatar component that displays user avatars with cached remote image loading +@available(iOS 16.0, *) +public struct EcosiaAvatar: View { + private let avatarURL: URL? + private let size: CGFloat + private let signedOutPlaceholderImageName: String + private let signedInPlaceholderImageName: String + @ObservedObject private var authStateProvider = EcosiaAuthUIStateProvider.shared + + public init(avatarURL: URL?, + size: CGFloat = .ecosia.space._2l, + signedOutPlaceholderImageName: String = "avatar", + signedInPlaceholderImageName: String = "placeholder-trees") { + self.avatarURL = avatarURL + self.size = size + self.signedOutPlaceholderImageName = signedOutPlaceholderImageName + self.signedInPlaceholderImageName = signedInPlaceholderImageName + } + + public var body: some View { + Group { + EcosiaCachedAsyncImage(url: avatarURL) { image in + image + .resizable() + .scaledToFill() + } placeholder: { + let shouldShowSignedInPlaceholder: Bool = authStateProvider.isLoggedIn && avatarURL == nil + placeholderView(imageName: shouldShowSignedInPlaceholder ? signedInPlaceholderImageName : signedOutPlaceholderImageName) + .transition(.opacity) + } + .frame(width: size, height: size) + .clipShape(Circle()) + .accessibilityLabel(String.localized(.userAvatarAccessibilityLabel)) + .accessibilityIdentifier("user_avatar") + .transition(.opacity) + } + .animation(.easeInOut(duration: 0.3), value: avatarURL) + } + + private func placeholderView(imageName: String) -> some View { + Image(imageName, bundle: .ecosia) + .resizable() + .scaledToFit() + .frame(width: size, height: size) + .clipShape(Circle()) + .accessibilityLabel(String.localized(.defaultAvatarAccessibilityLabel)) + .accessibilityIdentifier("default_avatar") + } +} + +#if DEBUG +// MARK: - Preview +@available(iOS 16.0, *) +struct EcosiaAvatar_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: .ecosia.space._l) { + // With remote URL + EcosiaAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + size: .ecosia.space._6l + ) + + // Without URL (placeholder) + EcosiaAvatar( + avatarURL: nil, + size: .ecosia.space._6l + ) + + // Small size + EcosiaAvatar( + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + size: .ecosia.space._2l + ) + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaCachedAsyncImage.swift b/firefox-ios/Ecosia/UI/Account/EcosiaCachedAsyncImage.swift new file mode 100644 index 0000000000000..e9d52909b0d66 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaCachedAsyncImage.swift @@ -0,0 +1,145 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A SwiftUI view that loads and caches remote images +/// Provides smooth transitions and persistent caching across view reloads +@available(iOS 16.0, *) +struct EcosiaCachedAsyncImage: View { + private let url: URL? + private let content: (Image) -> Content + private let placeholder: () -> Placeholder + private let transition: AnyTransition + + @StateObject private var loader: ImageCacheLoader + + init( + url: URL?, + transition: AnyTransition = .opacity, + @ViewBuilder content: @escaping (Image) -> Content, + @ViewBuilder placeholder: @escaping () -> Placeholder + ) { + self.url = url + self.transition = transition + self.content = content + self.placeholder = placeholder + + // Initialize loader with cached image if available + _loader = StateObject(wrappedValue: ImageCacheLoader(url: url)) + } + + var body: some View { + ZStack { + // Placeholder layer + if url == nil { + placeholder() + .transition(transition) + } else if let uiImage = loader.image { + /* + We disabled swiftlint here to let each single + copy of this struct define their own accessibility label + */ + // swiftlint:disable accessibility_label_for_image + content(Image(uiImage: uiImage)) + .transition(transition) + // swiftlint:enable accessibility_label_for_image + } + } + .animation(.easeInOut(duration: 0.3), value: loader.image != nil) + .task(id: url) { + if let url = url { + await loader.loadImage(from: url) + } + } + } +} + +/// Image cache loader with URLCache-based persistent caching +@MainActor +public final class ImageCacheLoader: ObservableObject { + @Published var image: UIImage? + + private static let cache: URLCache = { + // 100MB memory, 500MB disk - persists across app restarts + URLCache(memoryCapacity: 100_000_000, diskCapacity: 500_000_000) + }() + + private var currentTask: Task? + + /// Initialize with URL and immediately check cache to prevent flicker + init(url: URL?) { + // Check cache synchronously during initialization + if let url = url, + let cachedResponse = Self.cache.cachedResponse(for: URLRequest(url: url)), + let cachedImage = UIImage(data: cachedResponse.data) { + self.image = cachedImage + } + } + + deinit { + currentTask?.cancel() + } + + func loadImage(from url: URL) async { + // Cancel any existing task + currentTask?.cancel() + + let request = URLRequest(url: url) + + // Check URLCache first (disk + memory) + if let cachedResponse = Self.cache.cachedResponse(for: request), + let cachedImage = UIImage(data: cachedResponse.data) { + self.image = cachedImage + return + } + + // Load from network with retry on cancellation + await loadImageWithRetry(from: url, request: request) + } + + private func loadImageWithRetry(from url: URL, request: URLRequest, isRetry: Bool = false) async { + currentTask = Task { + do { + let (data, response) = try await URLSession.shared.data(from: url) + + // Check if task was cancelled + guard !Task.isCancelled else { return } + + if let loadedImage = UIImage(data: data) { + // Cache the response with URLCache (persists to disk) + if let httpResponse = response as? HTTPURLResponse { + let cachedResponse = CachedURLResponse(response: httpResponse, data: data) + Self.cache.storeCachedResponse(cachedResponse, for: request) + } + self.image = loadedImage + } + } catch { + let nsError = error as NSError + + /* + Workaround for iOS AsyncImage bug (error -999 cancellation). + Retry once on cancellation: https://developer.apple.com/forums/thread/682498 + */ + if nsError.code == NSURLErrorCancelled && !isRetry { + EcosiaLogger.accounts.debug("Image load cancelled, retrying once") + await loadImageWithRetry(from: url, request: request, isRetry: true) + } else if !Task.isCancelled { + EcosiaLogger.accounts.debug("Failed to load avatar image: \(error.localizedDescription)") + } + } + } + + await currentTask?.value + } +} + +extension ImageCacheLoader { + + public static func clearCache(for url: URL?) async throws { + guard let url else { return } + ImageCacheLoader.cache.removeCachedResponse(for: URLRequest(url: url)) + } +} diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaSeedView.swift b/firefox-ios/Ecosia/UI/Account/EcosiaSeedView.swift new file mode 100644 index 0000000000000..79433b5639688 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaSeedView.swift @@ -0,0 +1,225 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A reusable seed count component that displays the seed icon and animated count. +/// Displays counts 0-999 normally, and "999+" for counts of 1000 or more. +/// Screen readers always receive the actual count for accessibility. +@available(iOS 16.0, *) +public struct EcosiaSeedView: View { + private let seedCount: Int + private let seedIconSize: CGFloat + private let spacing: CGFloat + private let enableAnimation: Bool + private let showSparkles: Bool + private let showLock: Bool + private let windowUUID: WindowUUID + @State private var animationScale: CGFloat = 1.0 + @State private var animationOffsetY: CGFloat = 0 + @State private var animationRotation: Double = 0 + @State private var animationOffsetX: CGFloat = 0 + @State private var previousSeedCount: Int = 0 + @State private var theme = EcosiaSeedViewTheme() + + private struct UX { + static let springResponse: Double = 0.6 + static let springDampingFraction: Double = 0.5 + static let squeezeScale: CGFloat = 0.5 + static let squeezeOffsetX: CGFloat = -5 + static let squeezeOffsetY: CGFloat = -5 + static let squeezeRotation: Double = -10.0 + static let squeezeDuration: TimeInterval = 0.2 + static let lockSize: CGFloat = 16 + static let bounceDelay: TimeInterval = 0.15 + } + + public init( + seedCount: Int, + seedIconSize: CGFloat = .ecosia.space._1l, + spacing: CGFloat = .ecosia.space._1s, + enableAnimation: Bool = true, + showSparkles: Bool = false, + showLock: Bool = false, + windowUUID: WindowUUID + ) { + self.seedCount = seedCount + self.seedIconSize = seedIconSize + self.spacing = spacing + self.enableAnimation = enableAnimation + self.showSparkles = showSparkles + self.showLock = showLock + self.windowUUID = windowUUID + } + + private var displayedSeedCount: String { + if seedCount > 999 { + return String(format: .localized(.numberAsStringWithPlusSymbol), "999") + } + return "\(seedCount)" + } + + private var accessibilityLabel: String { + String(format: .localized(.seedCountAccessibilityLabel), seedCount) + } + + public var body: some View { + HStack(alignment: .center, spacing: spacing) { + Image("seed", bundle: .ecosia) + .resizable() + .frame(width: seedIconSize, height: seedIconSize) + .scaleEffect(enableAnimation ? animationScale : 1.0) + .rotationEffect(.degrees(enableAnimation ? animationRotation : 0)) + .offset(x: enableAnimation ? animationOffsetX : 0, y: enableAnimation ? animationOffsetY : 0) + .accessibilityHidden(true) + .overlay( + GeometryReader { geometry in + if showSparkles { + EcosiaSparkleAnimation( + isVisible: showSparkles, + containerSize: seedIconSize * 2.5, + sparkleSize: seedIconSize * 0.4 + ) + .frame(width: seedIconSize * 2.5, height: seedIconSize * 2.5) + .position(x: geometry.size.width / 2, y: geometry.size.height / 2) + .allowsHitTesting(false) + } + } + ) + + Text(displayedSeedCount) + .font(.headline) + .foregroundColor(theme.textColor) + .animatedText(numericValue: seedCount, reduceMotionEnabled: !enableAnimation) + + if showLock { + Image("lock", bundle: .ecosia) + .renderingMode(.template) + .resizable() + .frame(width: UX.lockSize, height: UX.lockSize) + .foregroundColor(theme.lockColor) + } + } + .accessibilityElement(children: .combine) + .accessibilityLabel(accessibilityLabel) + .accessibilityIdentifier("seed_count_view") + .onChange(of: seedCount) { newValue in + if enableAnimation && newValue > previousSeedCount { + triggerSeedAnimation() + } + previousSeedCount = newValue + } + .ecosiaThemed(windowUUID, $theme) + } + + private func triggerSeedAnimation() { + animationScale = 1.0 + animationRotation = 0 + animationOffsetY = 0 + animationOffsetX = 0 + + withOptionalAnimation(.easeIn(duration: UX.squeezeDuration)) { + animationScale = UX.squeezeScale + animationRotation = UX.squeezeRotation + animationOffsetX = UX.squeezeOffsetX + animationOffsetY = UX.squeezeOffsetY + } + + DispatchQueue.main.asyncAfter(deadline: .now() + UX.squeezeDuration + UX.bounceDelay) { + withOptionalAnimation(.spring(response: UX.springResponse, dampingFraction: UX.springDampingFraction)) { + animationScale = 1.0 + animationRotation = 0 + animationOffsetX = 0 + animationOffsetY = 0 + } + } + } +} + +// MARK: - Theme +struct EcosiaSeedViewTheme: EcosiaThemeable { + var textColor = Color.primary + var lockColor = Color.primary + + mutating func applyTheme(theme: Theme) { + textColor = Color(theme.colors.ecosia.textPrimary) + lockColor = Color(theme.colors.ecosia.iconDecorative) + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaSeedView_Previews: PreviewProvider { + static var previews: some View { + EcosiaSeedViewInteractivePreview() + } +} + +@available(iOS 16.0, *) +private struct EcosiaSeedViewInteractivePreview: View { + @State private var seedCount = 42 + + var body: some View { + VStack(spacing: .ecosia.space._l) { + Text("Interactive Seed Animation Test") + .font(.title2.bold()) + + EcosiaSeedView(seedCount: seedCount, windowUUID: .XCTestDefaultUUID) + + HStack { + Button("Add Seeds") { + seedCount += Int.random(in: 1...5) + } + .buttonStyle(.bordered) + + Button("Add 100") { + seedCount += 100 + } + .buttonStyle(.bordered) + + Button("Reset") { + seedCount = 42 + } + .buttonStyle(.bordered) + } + + Divider() + + Text("Static Examples") + .font(.title3.bold()) + + VStack(spacing: .ecosia.space._l) { + EcosiaSeedView( + seedCount: 999, + seedIconSize: .ecosia.space._2l, + spacing: .ecosia.space._s, + windowUUID: .XCTestDefaultUUID + ) + EcosiaSeedView( + seedCount: 1000, + windowUUID: .XCTestDefaultUUID + ) + EcosiaSeedView( + seedCount: 5432, + windowUUID: .XCTestDefaultUUID + ) + EcosiaSeedView( + seedCount: 5, + enableAnimation: false, + windowUUID: .XCTestDefaultUUID + ) + + EcosiaSeedView( + seedCount: 3, + enableAnimation: false, + showLock: true, + windowUUID: .XCTestDefaultUUID + ) + } + } + .padding() + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaSparkleAnimation.swift b/firefox-ios/Ecosia/UI/Account/EcosiaSparkleAnimation.swift new file mode 100644 index 0000000000000..fee4c9316f57a --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaSparkleAnimation.swift @@ -0,0 +1,265 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +@available(iOS 16.0, *) +public struct EcosiaSparkleAnimation: View { + private let isVisible: Bool + private let containerSize: CGFloat + private let sparkleSize: CGFloat + private let animationDuration: Double + private let onComplete: (() -> Void)? + + @State private var sparkles: [SparkleState] = [] + + /// Choreography phase definition + private struct SparklePhase { + let positionX: CGFloat + let positionY: CGFloat + let startSize: CGFloat + let endSize: CGFloat + let startRotation: Double + let endRotation: Double + let startOpacity: Double + let endOpacity: Double + let duration: TimeInterval + let delay: TimeInterval + let timing: AnimationTiming + + enum AnimationTiming { + case easeIn + case easeOut + case linear + } + } + + /// Corner position for sparkles + private enum SparkleCorner { + case topLeft + case bottomLeft + case topRight + + func offset(for containerSize: CGFloat) -> (x: CGFloat, y: CGFloat) { + let cornerOffset = (containerSize / 2) * 0.5 // 50% of the way to the corner + switch self { + case .topLeft: + return (-cornerOffset, -cornerOffset) + case .bottomLeft: + return (-cornerOffset, cornerOffset) + case .topRight: + return (cornerOffset, -cornerOffset) + } + } + } + + /// Generates sparkle phases for a given corner position + private func createSparklePhases( + corner: SparkleCorner, + containerSize: CGFloat, + appearSize: CGFloat, + appearDelay: TimeInterval, + appearTiming: SparklePhase.AnimationTiming, + disappearRotation: Double + ) -> [SparklePhase] { + let position = corner.offset(for: containerSize) + // swiftlint:disable multiline_arguments + return [ + SparklePhase( + positionX: position.x, positionY: position.y, + startSize: 8, endSize: appearSize, + startRotation: -45, endRotation: 0, + startOpacity: 0, endOpacity: 1, + duration: 0.2, delay: appearDelay, + timing: appearTiming + ), + SparklePhase( + positionX: position.x, positionY: position.y, + startSize: appearSize, endSize: 8, + startRotation: 0, endRotation: disappearRotation, + startOpacity: 1, endOpacity: 0, + duration: 0.2, delay: appearDelay + 0.2, + timing: .linear + ) + ] + // swiftlint:enable multiline_arguments + } + + public init( + isVisible: Bool, + containerSize: CGFloat = .ecosia.space._6l, + sparkleSize: CGFloat = 24, + animationDuration: Double = 6.0, + onComplete: (() -> Void)? = nil + ) { + self.isVisible = isVisible + self.containerSize = containerSize + self.sparkleSize = sparkleSize + self.animationDuration = animationDuration + self.onComplete = onComplete + } + + public var body: some View { + ZStack { + if isVisible { + ForEach(sparkles) { sparkle in + Image("highlight-star", bundle: .ecosia) + .resizable() + .frame(width: sparkle.size, height: sparkle.size) + .rotationEffect(.degrees(sparkle.rotation)) + .opacity(sparkle.opacity) + .offset(x: sparkle.offsetX, y: sparkle.offsetY) + .accessibilityHidden(true) + } + } + } + .frame(width: containerSize, height: containerSize) + .onChange(of: isVisible) { visible in + if visible { + triggerAnimation() + } else { + sparkles.removeAll() + } + } + .onAppear { + if isVisible { + triggerAnimation() + } + } + } + + private func triggerAnimation() { + runBurst(burstIndex: 0) + + DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { + self.runBurst(burstIndex: 1) + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { + self.sparkles.removeAll() + self.onComplete?() + } + } + + private func runBurst(burstIndex: Int) { + let offset = burstIndex * 3 + + animateSparkle( + id: offset + 0, + phases: createSparklePhases( + corner: .topLeft, + containerSize: containerSize, + appearSize: 28, + appearDelay: 0.0, + appearTiming: .easeIn, + disappearRotation: 45 + ) + ) + + animateSparkle( + id: offset + 1, + phases: createSparklePhases( + corner: .bottomLeft, + containerSize: containerSize, + appearSize: 20, + appearDelay: 0.4, + appearTiming: .linear, + disappearRotation: -45 + ) + ) + + animateSparkle( + id: offset + 2, + phases: createSparklePhases( + corner: .topRight, + containerSize: containerSize, + appearSize: 20, + appearDelay: 0.8, + appearTiming: .linear, + disappearRotation: 45 + ) + ) + } + + private func animateSparkle(id: Int, phases: [SparklePhase]) { + guard let firstPhase = phases.first else { return } + + let sparkle = SparkleState( + id: UUID(), + sparkleID: id, + offsetX: firstPhase.positionX, + offsetY: firstPhase.positionY, + size: firstPhase.startSize, + rotation: firstPhase.startRotation, + opacity: firstPhase.startOpacity + ) + sparkles.append(sparkle) + + for phase in phases { + DispatchQueue.main.asyncAfter(deadline: .now() + phase.delay) { + guard let index = self.sparkles.firstIndex(where: { $0.sparkleID == id }) else { return } + + let animation: Animation = { + switch phase.timing { + case .easeIn: + return .easeIn(duration: phase.duration) + case .easeOut: + return .easeOut(duration: phase.duration) + case .linear: + return .linear(duration: phase.duration) + } + }() + + withAnimation(animation) { + self.sparkles[index].size = phase.endSize + self.sparkles[index].rotation = phase.endRotation + self.sparkles[index].opacity = phase.endOpacity + } + } + } + } +} + +// MARK: - Supporting Types +private struct SparkleState: Identifiable { + let id: UUID + let sparkleID: Int + let offsetX: CGFloat + let offsetY: CGFloat + var size: CGFloat + var rotation: Double + var opacity: Double +} + +#if DEBUG +// MARK: - Preview +@available(iOS 16.0, *) +struct EcosiaSparkleAnimation_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: .ecosia.space._2l) { + ZStack { + Circle() + .fill(Color.gray.opacity(0.3)) + .frame(width: .ecosia.space._6l, height: .ecosia.space._6l) + + EcosiaSparkleAnimation(isVisible: true) + } + + ZStack { + Circle() + .fill(Color.blue.opacity(0.3)) + .frame(width: .ecosia.space._8l, height: .ecosia.space._8l) + + EcosiaSparkleAnimation( + isVisible: true, + containerSize: .ecosia.space._8l, + sparkleSize: .ecosia.space._1l + ) + } + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/EcosiaWebViewModal.swift b/firefox-ios/Ecosia/UI/Account/EcosiaWebViewModal.swift new file mode 100644 index 0000000000000..06fb146efa37d --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/EcosiaWebViewModal.swift @@ -0,0 +1,288 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import WebKit +import Common + +/// A modal view that displays web content in a WebView with navigation controls +@available(iOS 16.0, *) +public struct EcosiaWebViewModal: View { + private let url: URL + private let windowUUID: WindowUUID + private let userAgent: String? + private let onLoadComplete: (() -> Void)? + private let onDismiss: (() -> Void)? + private let redirectURLString: String? + private let retryCount: Int + @SwiftUI.Environment(\.dismiss) private var dismiss: DismissAction + @State private var theme = EcosiaWebViewModalTheme() + @State private var webView: WKWebView? + @State private var isLoading = true + @State private var pageTitle = "" + @State private var hasError = false + @State private var errorMessage = "" + + public init( + url: URL, + windowUUID: WindowUUID, + userAgent: String? = nil, + onLoadComplete: (() -> Void)? = nil, + onDismiss: (() -> Void)? = nil, + retryCount: Int = 1 + ) { + self.url = url + self.windowUUID = windowUUID + self.userAgent = userAgent + self.onLoadComplete = onLoadComplete + self.onDismiss = onDismiss + self.redirectURLString = url.absoluteString + self.retryCount = retryCount + } + + public var body: some View { + NavigationView { + ZStack { + theme.backgroundColor.ignoresSafeArea() + + VStack(spacing: 0) { + ZStack { + if hasError { + VStack(spacing: 16) { + Image(systemName: "exclamationmark.triangle") + .font(.system(size: 48)) + .foregroundColor(theme.brandPrimaryColor) + .accessibilityLabel(String.localized(.errorIcon)) + + Text(String.localized(.failedToLoadPage)) + .font(.headline) + .foregroundColor(theme.brandPrimaryColor) + + Text(errorMessage) + .font(.body) + .foregroundColor(.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal) + + Button(String.localized(.tryAgain)) { + hasError = false + isLoading = true + let request = URLRequest(url: url) + webView?.load(request) + } + .foregroundColor(theme.brandPrimaryColor) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + WebViewRepresentable( + url: url, + webView: $webView, + isLoading: $isLoading, + pageTitle: $pageTitle, + hasError: $hasError, + errorMessage: $errorMessage, + userAgent: userAgent, + onLoadComplete: onLoadComplete, + redirectURLString: redirectURLString, + retryCount: retryCount + ) + + if isLoading { + theme.backgroundColor + .frame(maxWidth: .infinity, maxHeight: .infinity) + ProgressView() + .scaleEffect(1.2) + } + } + } + } + } + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button(String.localized(.close)) { + dismiss() + } + .foregroundColor(theme.brandPrimaryColor) + .accessibilityIdentifier("close_webview_modal_button") + } + } + } + .ecosiaThemed(windowUUID, $theme) + .onDisappear { + onDismiss?() + } + } +} + +/// UIViewRepresentable wrapper for WKWebView +@available(iOS 16.0, *) +private struct WebViewRepresentable: UIViewRepresentable { + let url: URL + @Binding var webView: WKWebView? + @Binding var isLoading: Bool + @Binding var pageTitle: String + @Binding var hasError: Bool + @Binding var errorMessage: String + let userAgent: String? + let onLoadComplete: (() -> Void)? + let redirectURLString: String? + let retryCount: Int + + func makeUIView(context: Context) -> WKWebView { + let configuration = WKWebViewConfiguration() + configuration.allowsInlineMediaPlayback = true + configuration.mediaTypesRequiringUserActionForPlayback = [] + + let webView = WKWebView(frame: .zero, configuration: configuration) + webView.navigationDelegate = context.coordinator + webView.uiDelegate = context.coordinator + webView.allowsBackForwardNavigationGestures = true + + if let userAgent = userAgent { + webView.customUserAgent = userAgent + } + + self.webView = webView + + var request = URLRequest(url: url) + request.timeoutInterval = 30.0 // 30 second timeout + webView.load(request) + + return webView + } + + func updateUIView(_ uiView: WKWebView, context: Context) { + // No-op: WebView state is managed through bindings and coordinator + } + + func makeCoordinator() -> Coordinator { + Coordinator(parent: self) + } + + class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate { + let parent: WebViewRepresentable + private let retryCount: Int + private var remainingRetries = 0 + private var blankTargetURLs: Set = [] + + init(parent: WebViewRepresentable) { + self.parent = parent + self.retryCount = parent.retryCount + self.remainingRetries = parent.retryCount + } + + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + parent.isLoading = true + parent.hasError = false + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + parent.isLoading = false + parent.pageTitle = webView.title ?? "" + parent.onLoadComplete?() + } + + func webView(_ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + guard let url = navigationAction.request.url else { + decisionHandler(.allow) + return + } + + // If this is a back navigation to a page we loaded from a target="_blank" link, + // prevent it and go back further (to the page before the blank link was clicked) + if navigationAction.navigationType == .backForward, + blankTargetURLs.contains(url.absoluteString) { + EcosiaLogger.auth.debug("🔐 [WEBVIEW] Preventing navigation back to target='_blank' origin: \(url)") + decisionHandler(.cancel) + + // Keep going back to escape the blank-target page + if webView.canGoBack { + webView.goBack() + } else { + // If we can't go back, reload the initial URL (root) + webView.load(URLRequest(url: parent.url)) + blankTargetURLs.removeAll() + } + return + } + + decisionHandler(.allow) + } + + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { + handleFailure(error) + } + + func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + handleFailure(error) + } + + private func handleFailure(_ error: Error) { + guard remainingRetries == 0 else { + remainingRetries -= 1 + return + } + parent.isLoading = false + parent.hasError = true + parent.errorMessage = error.localizedDescription + } + + // MARK: - WKUIDelegate + + func webView( + _ webView: WKWebView, + createWebViewWith configuration: WKWebViewConfiguration, + for navigationAction: WKNavigationAction, + windowFeatures: WKWindowFeatures + ) -> WKWebView? { + // When targetFrame is nil, it means the link should open in a new tab/window + // (e.g., target="_blank" or window.open()) + guard navigationAction.targetFrame == nil, + let url = navigationAction.request.url else { + return nil + } + + // Record the current page before loading the blank target + if let currentURL = webView.url { + blankTargetURLs.insert(currentURL.absoluteString) + EcosiaLogger.auth.info("🔐 [WEBVIEW] Recorded blank-target origin: \(currentURL)") + } + + EcosiaLogger.auth.info("🔐 [WEBVIEW] Loading target='_blank' URL in modal: \(url)") + webView.load(URLRequest(url: url)) + + // Return nil to prevent creating a new window + return nil + } + } +} + +/// Theme configuration for EcosiaWebViewModal +@available(iOS 16.0, *) +public struct EcosiaWebViewModalTheme: EcosiaThemeable { + public var backgroundColor = Color.white + public var brandPrimaryColor = Color.blue + + public init() {} + + public mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.layer1) + brandPrimaryColor = Color(theme.colors.ecosia.brandPrimary) + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaWebViewModal_Previews: PreviewProvider { + static var previews: some View { + EcosiaWebViewModal( + url: URL(string: "https://support.ecosia.org/article/844-seed-counter")!, + windowUUID: .XCTestDefaultUUID + ) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Account/LevelSystem/GrowthPointsLevelSystem.swift b/firefox-ios/Ecosia/UI/Account/LevelSystem/GrowthPointsLevelSystem.swift new file mode 100644 index 0000000000000..07eb058643850 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/LevelSystem/GrowthPointsLevelSystem.swift @@ -0,0 +1,86 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Growth Points levelling system based on API data +/// Provides convenient access to level information and progress calculations +public struct GrowthPointsLevelSystem { + + // MARK: - Level Information + + /// Gets the current level number from API response + public static func currentLevel(from response: AccountVisitResponse) -> Int { + return response.growthPoints.level.number + } + + /// Gets the localized name for a given level number + public static func levelName(for levelNumber: Int) -> String { + switch levelNumber { + case 1: return String.localized(.ecocurious) + case 2: return String.localized(.greenExplorer) + case 3: return String.localized(.planetPal) + case 4: return String.localized(.seedlingSupporter) + case 5: return String.localized(.biodiversityBestie) + case 6: return String.localized(.forestFriend) + case 7: return String.localized(.wildlifeProtector) + case 8: return String.localized(.ecoExplorer) + case 9: return String.localized(.rainforestReviver) + case 10: return String.localized(.planetProtector) + case 11: return String.localized(.carbonNeutralizer) + case 12: return String.localized(.seekerOfSustainability) + case 13: return String.localized(.branchBuilder) + case 14: return String.localized(.ecoEnthusiast) + case 15: return String.localized(.carbonCutter) + case 16: return String.localized(.seedSower) + case 17: return String.localized(.emissionEliminator) + case 18: return String.localized(.sustainabilitySage) + case 19: return String.localized(.earthAdvocate) + case 20: return String.localized(.seedSuperstar) + default: return String.localized(.ecocurious) // Fallback to level 1 + } + } + + /// Gets the current level name from API response + public static func currentLevelName(from response: AccountVisitResponse) -> String { + return levelName(for: response.growthPoints.level.number) + } + + /// Gets the previous level number from API response + public static func previousLevel(from response: AccountVisitResponse) -> Int { + return response.growthPoints.previousLevel.number + } + + // MARK: - Progress Information + + /// Calculates progress towards next level from API response (0.0 to 1.0) + public static func progressToNextLevel(from response: AccountVisitResponse) -> Double { + return response.progressToNextLevel + } + + /// Checks if the user leveled up from the API response + public static func didLevelUp(from response: AccountVisitResponse) -> Bool { + return response.didLevelUp + } + + /// Gets growth points earned towards next level + public static func growthPointsEarned(from response: AccountVisitResponse) -> Int { + return response.growthPoints.level.growthPointsEarnedTowardsNextLevel + } + + /// Gets growth points required to unlock next level + public static func growthPointsRequired(from response: AccountVisitResponse) -> Int { + return response.growthPoints.level.growthPointsToUnlockNextLevel + } + + /// Gets seeds rewarded for reaching current level + public static func seedsRewardedForLevelUp(from response: AccountVisitResponse) -> Int { + return response.growthPoints.level.seedsRewardedForLevelUp + } + + /// Gets total growth points required for current level + public static func totalGrowthPointsRequired(from response: AccountVisitResponse) -> Int { + return response.growthPoints.level.totalGrowthPointsRequired + } +} diff --git a/firefox-ios/Ecosia/UI/Account/SeedProgressManager/SeedCounterConfig.swift b/firefox-ios/Ecosia/UI/Account/SeedProgressManager/SeedCounterConfig.swift new file mode 100644 index 0000000000000..797ddb072d816 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/SeedProgressManager/SeedCounterConfig.swift @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public struct SeedCounterConfig { + let sparklesAnimationDuration: Double + let maxCappedLevel: Int? // Optional field to cap the level as part of the experiment + let maxCappedSeeds: Int? // Optional field to cap the total seeds as part of the experiment + let levels: [SeedLevel] + + struct SeedLevel: Codable { + let level: Int + let requiredSeeds: Int + } +} + +extension SeedCounterConfig: Decodable {} diff --git a/firefox-ios/Ecosia/UI/Account/SeedProgressManager/SeedProgressManagerProtocol.swift b/firefox-ios/Ecosia/UI/Account/SeedProgressManager/SeedProgressManagerProtocol.swift new file mode 100644 index 0000000000000..92db17aa051aa --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/SeedProgressManager/SeedProgressManagerProtocol.swift @@ -0,0 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +public protocol SeedProgressManagerProtocol { + static var progressUpdatedNotification: Notification.Name { get } + static var levelUpNotification: Notification.Name { get } + static var seedCounterConfig: SeedCounterConfig? { get set } + + static func loadCurrentLevel() -> Int + static func loadTotalSeedsCollected() -> Int + static func loadLastAppOpenDate() -> Date? + + static func saveProgress(totalSeeds: Int, currentLevel: Int, lastAppOpenDate: Date) + + static func addSeeds(_ count: Int, relativeToDate date: Date) + static func resetLocalSeedProgress() + + static func calculateInnerProgress() -> CGFloat + static func collectDailySeed() +} diff --git a/firefox-ios/Ecosia/UI/Account/SeedProgressManager/UserDefaultsSeedProgressManager.swift b/firefox-ios/Ecosia/UI/Account/SeedProgressManager/UserDefaultsSeedProgressManager.swift new file mode 100644 index 0000000000000..cd39a14d1c9fe --- /dev/null +++ b/firefox-ios/Ecosia/UI/Account/SeedProgressManager/UserDefaultsSeedProgressManager.swift @@ -0,0 +1,194 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Manages local seed collection and progression for logged-out users. +/// +/// This manager persists seed progress locally using `UserDefaults` and is intended +/// exclusively for tracking seeds collected by users who are not logged in. When users +/// log in, their seed progress is managed server-side through the account system. +/// +/// ## Local Storage +/// +/// The manager stores three key values in `UserDefaults`: +/// - Total seeds collected since first app launch +/// - Current level based on seed thresholds +/// - Last app open date for daily seed collection +/// +/// ## Level Progression +/// +/// Seed levels and thresholds are configured via `SeedCounterConfig`, which can be +/// remotely configured. The manager automatically handles level-up transitions when +/// seed thresholds are met and posts notifications for UI updates. +/// +/// ## Important +/// +/// This manager should only be used for logged-out users. Server-based seed management +/// takes precedence for authenticated users. +public final class UserDefaultsSeedProgressManager: SeedProgressManagerProtocol { + + private static let className = String(describing: UserDefaultsSeedProgressManager.self) + public static let maxSeedsForLoggedOutUsers = 3 + public static var progressUpdatedNotification: Notification.Name { .init("\(className).SeedProgressUpdated") } + public static var levelUpNotification: Notification.Name { .init("\(className).SeedLevelUp") } + + // UserDefaults keys + private static let totalSeedsCollectedKey = "TotalSeedsCollected" + private static let currentLevelKey = "CurrentLevel" + private static let lastAppOpenDateKey = "LastAppOpenDate" + + public static var seedCounterConfig: SeedCounterConfig? + private static var seedLevels: [SeedCounterConfig.SeedLevel] { seedCounterConfig?.levels.compactMap { $0 } ?? [] } + + // Fetch max level and max seeds from remote configuration if provided + private static let maxCappedLevel = seedCounterConfig?.maxCappedLevel + private static let maxCappedSeeds = seedCounterConfig?.maxCappedSeeds + + private init() {} + + // MARK: - Static Methods + + /// Loads the current level from UserDefaults. + /// + /// - Returns: The current level, defaulting to 1 if not set. + public static func loadCurrentLevel() -> Int { + let currentLevel = UserDefaults.standard.integer(forKey: currentLevelKey) + return currentLevel == 0 ? 1 : currentLevel + } + + /// Loads the total seeds collected from UserDefaults. + /// + /// - Returns: The total number of seeds collected. Returns 0 for first-time users. + public static func loadTotalSeedsCollected() -> Int { + return UserDefaults.standard.integer(forKey: totalSeedsCollectedKey) + } + + /// Loads the last app open date from UserDefaults. + /// + /// - Returns: The last date the app was opened and a seed was collected, or `nil` if this is the first launch. + public static func loadLastAppOpenDate() -> Date? { + return UserDefaults.standard.object(forKey: lastAppOpenDateKey) as? Date + } + + /// Saves the seed progress and level to UserDefaults. + /// + /// Posts a `progressUpdatedNotification` after saving. + /// + /// - Parameters: + /// - totalSeeds: The total number of seeds to save. + /// - currentLevel: The current level to save. + /// - lastAppOpenDate: The date to record as the last app open. + public static func saveProgress(totalSeeds: Int, currentLevel: Int, lastAppOpenDate: Date) { + let defaults = UserDefaults.standard + defaults.set(totalSeeds, forKey: totalSeedsCollectedKey) + defaults.set(currentLevel, forKey: currentLevelKey) + defaults.set(lastAppOpenDate, forKey: lastAppOpenDateKey) + NotificationCenter.default.post(name: progressUpdatedNotification, object: nil) + } + + /// Returns the seed threshold required for a specific level. + /// + /// - Parameter level: The level to query. + /// - Returns: The number of seeds required for the level, or 0 if not found. + private static func requiredSeedsForLevel(_ level: Int) -> Int { + if let seedLevel = seedLevels.first(where: { $0.level == level }) { + return seedLevel.requiredSeeds + } + return seedLevels.first?.requiredSeeds ?? 0 // If the is no level matching, use the first one + } + + /// Calculates the inner progress for the current level as a fraction from 0 to 1. + /// + /// - Returns: A value between 0.0 and 1.0 representing progress within the current level. + public static func calculateInnerProgress() -> CGFloat { + let totalSeeds = loadTotalSeedsCollected() + + // Find the level config where total seeds fall in the range of the current level and the next level + guard let currentLevelConfig = seedLevels.first(where: { level in + let previousLevelSeeds = level.level > 1 ? requiredSeedsForLevel(level.level - 1) : 0 + let nextLevelSeeds = requiredSeedsForLevel(level.level) + return totalSeeds > previousLevelSeeds && totalSeeds <= nextLevelSeeds + }) else { + return 0.0 // Default to 0 if no valid level is found + } + + let previousLevelSeeds = currentLevelConfig.level > 1 ? requiredSeedsForLevel(currentLevelConfig.level - 1) : 0 + let progressInCurrentLevel = totalSeeds - previousLevelSeeds + let requiredSeedsForCurrentLevel = currentLevelConfig.requiredSeeds - previousLevelSeeds + + // Return progress as a fraction (between 0 and 1) + return CGFloat(progressInCurrentLevel) / CGFloat(requiredSeedsForCurrentLevel) + } + + /// Adds seeds to the counter, using the current date. + /// + /// Enforces the maximum seed cap for logged-out users. + /// + /// - Parameter count: The number of seeds to add. + public static func addSeeds(_ count: Int) { + addSeeds(count, relativeToDate: .now) + } + + /// Adds seeds to the counter with a specific date. + /// + /// Enforces the maximum seed cap for logged-out users (3 seeds). + /// Posts a `progressUpdatedNotification` after adding seeds. + /// + /// - Parameters: + /// - count: The number of seeds to add. + /// - date: The date to record as the last app open. + public static func addSeeds(_ count: Int, relativeToDate date: Date) { + // Load total seeds + var totalSeeds = loadTotalSeedsCollected() + + EcosiaLogger.accounts.info("Seed cap enforced for logged-out user: max \(maxSeedsForLoggedOutUsers) seeds") + + if totalSeeds >= maxSeedsForLoggedOutUsers { + return + } + + totalSeeds += count + + if totalSeeds >= maxSeedsForLoggedOutUsers { + totalSeeds = maxSeedsForLoggedOutUsers + } + + saveProgress(totalSeeds: totalSeeds, currentLevel: 1, lastAppOpenDate: date) + NotificationCenter.default.post(name: progressUpdatedNotification, object: nil) + } + + /// Resets the local seed progress to initial state (0 seeds, level 1, no last open date). + /// + /// Clears the last app open date to allow immediate seed collection. + /// Used on logout to prepare for fresh local seed collection. + public static func resetLocalSeedProgress() { + UserDefaults.standard.set(0, forKey: totalSeedsCollectedKey) + UserDefaults.standard.set(1, forKey: currentLevelKey) + UserDefaults.standard.removeObject(forKey: lastAppOpenDateKey) + NotificationCenter.default.post(name: progressUpdatedNotification, object: nil) + } + + /// Collects a seed once per day for logged-out users. + /// + /// On first launch (no previous date saved), always collects a seed. + /// On subsequent launches, only collects if a new day has started since the last collection. + public static func collectDailySeed() { + let currentDate = Date() + let lastOpenDate = loadLastAppOpenDate() + let calendar = Calendar.current + + guard let lastOpenDate = lastOpenDate else { + EcosiaLogger.accounts.info("First seed collection for new user") + addSeeds(1, relativeToDate: currentDate) + return + } + + if calendar.isDateInToday(lastOpenDate) { + return // Seed already collected today + } + + addSeeds(1, relativeToDate: currentDate) + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/AI/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/AI/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/AI/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/AI/ai-sparkle.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/AI/ai-sparkle.imageset/Contents.json new file mode 100644 index 0000000000000..a479d3a45f807 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/AI/ai-sparkle.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ai-sparkle.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/AI/ai-sparkle.imageset/ai-sparkle.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/AI/ai-sparkle.imageset/ai-sparkle.pdf new file mode 100644 index 0000000000000..add81096e566b Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/AI/ai-sparkle.imageset/ai-sparkle.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/account-menu-impact-flag.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/account-menu-impact-flag.imageset/Contents.json new file mode 100644 index 0000000000000..c0d314626fa90 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/account-menu-impact-flag.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "impact-flag.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/account-menu-impact-flag.imageset/impact-flag.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/Account/account-menu-impact-flag.imageset/impact-flag.pdf new file mode 100644 index 0000000000000..87268f171f2da Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/Account/account-menu-impact-flag.imageset/impact-flag.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/avatar.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/avatar.imageset/Contents.json new file mode 100644 index 0000000000000..c9b4dfeda3329 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/avatar.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "avatar.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/avatar.imageset/avatar.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/Account/avatar.imageset/avatar.pdf new file mode 100644 index 0000000000000..7457cadfa94af Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/Account/avatar.imageset/avatar.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/highlight-star.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/highlight-star.imageset/Contents.json new file mode 100644 index 0000000000000..78a7f33ad3bb1 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/highlight-star.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "highlight-star.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/highlight-star.imageset/highlight-star.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/Account/highlight-star.imageset/highlight-star.pdf new file mode 100644 index 0000000000000..664e54a57b40e Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/Account/highlight-star.imageset/highlight-star.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/placeholder-trees.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/placeholder-trees.imageset/Contents.json new file mode 100644 index 0000000000000..f9486723799fb --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/placeholder-trees.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "placeholder-trees.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/placeholder-trees.imageset/placeholder-trees.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/Account/placeholder-trees.imageset/placeholder-trees.pdf new file mode 100644 index 0000000000000..e06ef7b50cdcc Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/Account/placeholder-trees.imageset/placeholder-trees.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/seed.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/seed.imageset/Contents.json new file mode 100644 index 0000000000000..66fec5485487e --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/seed.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "seed.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/seed.imageset/seed.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/Account/seed.imageset/seed.pdf new file mode 100644 index 0000000000000..9e86bcd19e1c2 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/Account/seed.imageset/seed.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/sign-out.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Account/sign-out.imageset/Contents.json new file mode 100644 index 0000000000000..237f54f382391 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Account/sign-out.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sing-out.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Account/sign-out.imageset/sing-out.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/Account/sign-out.imageset/sing-out.pdf new file mode 100644 index 0000000000000..615aedc937c16 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/Account/sign-out.imageset/sing-out.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/Contents.json new file mode 100644 index 0000000000000..a170702f87a12 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "banknote.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "banknote-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/banknote-dark.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/banknote-dark.pdf new file mode 100644 index 0000000000000..bf37a4c839d67 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/banknote-dark.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/banknote.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/banknote.pdf new file mode 100644 index 0000000000000..2caba7b43e32f Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/banknote.imageset/banknote.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/close.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/close.imageset/Contents.json new file mode 100644 index 0000000000000..3aeefbd81d47b --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/close.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "close.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/close.imageset/close.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/close.imageset/close.pdf new file mode 100644 index 0000000000000..edc3ad3b47211 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/close.imageset/close.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/default-browser-card-side-image-koto-illustrations.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/default-browser-card-side-image-koto-illustrations.imageset/Contents.json new file mode 100644 index 0000000000000..a4be096035981 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/default-browser-card-side-image-koto-illustrations.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "default-browser-card-side-image-koto-illustrations.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/default-browser-card-side-image-koto-illustrations.imageset/default-browser-card-side-image-koto-illustrations.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/default-browser-card-side-image-koto-illustrations.imageset/default-browser-card-side-image-koto-illustrations.pdf new file mode 100644 index 0000000000000..bb589d188bd64 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/default-browser-card-side-image-koto-illustrations.imageset/default-browser-card-side-image-koto-illustrations.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/financialReports.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/financialReports.imageset/Contents.json new file mode 100644 index 0000000000000..380d8b01a8e38 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/financialReports.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "financialReports.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/financialReports.imageset/financialReports.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/financialReports.imageset/financialReports.pdf new file mode 100644 index 0000000000000..88e67e1e8d36f Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/financialReports.imageset/financialReports.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/Contents.json new file mode 100644 index 0000000000000..44ef7dccb9641 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/Contents.json @@ -0,0 +1,30 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "1x" + }, + { + "filename" : "forest@2x.jpg", + "idiom" : "iphone", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "scale" : "1x" + }, + { + "filename" : "forest_ipad.jpg", + "idiom" : "ipad", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/forest@2x.jpg b/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/forest@2x.jpg new file mode 100644 index 0000000000000..dfbc029e5913e Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/forest@2x.jpg differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/forest_ipad.jpg b/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/forest_ipad.jpg new file mode 100644 index 0000000000000..1d5f2681a5aa5 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/forest.imageset/forest_ipad.jpg differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/hand.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/hand.imageset/Contents.json new file mode 100644 index 0000000000000..1bd9e5b31148d --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/hand.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Illustrations.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/hand.imageset/Illustrations.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/hand.imageset/Illustrations.pdf new file mode 100644 index 0000000000000..ee762d6396974 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/hand.imageset/Illustrations.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/iconLogo.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/iconLogo.imageset/Contents.json new file mode 100644 index 0000000000000..9b0a59747dd4d --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/iconLogo.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Type=E + Tree Circle, Size=hdpi (36).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/iconLogo.imageset/Type=E + Tree Circle, Size=hdpi (36).pdf b/firefox-ios/Ecosia/UI/Common.xcassets/iconLogo.imageset/Type=E + Tree Circle, Size=hdpi (36).pdf new file mode 100644 index 0000000000000..24ba9b7ce7e17 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/iconLogo.imageset/Type=E + Tree Circle, Size=hdpi (36).pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/lock.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/lock.imageset/Contents.json new file mode 100644 index 0000000000000..a875b347a2ed2 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/lock.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "secureLock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/lock.imageset/secureLock.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/lock.imageset/secureLock.pdf new file mode 100644 index 0000000000000..2a5528431ff62 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/lock.imageset/secureLock.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/pins.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/pins.imageset/Contents.json new file mode 100644 index 0000000000000..44e3c45d0892f --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/pins.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "pins.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/pins.imageset/pins.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/pins.imageset/pins.pdf new file mode 100644 index 0000000000000..d03b750e8cbb7 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/pins.imageset/pins.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/problem.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/problem.imageset/Contents.json new file mode 100644 index 0000000000000..a988e6c1f0215 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/problem.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "problem.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/problem.imageset/problem.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/problem.imageset/problem.pdf new file mode 100644 index 0000000000000..b0374fae4407d Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/problem.imageset/problem.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/Contents.json new file mode 100644 index 0000000000000..ac506c0f74fd0 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "referral.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "referral-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/referral-dark.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/referral-dark.pdf new file mode 100644 index 0000000000000..d11ba337dfdd8 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/referral-dark.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/referral.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/referral.pdf new file mode 100644 index 0000000000000..cc0798aae7c52 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/referral.imageset/referral.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/sign-in.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/sign-in.imageset/Contents.json new file mode 100644 index 0000000000000..0024837e34d35 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/sign-in.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sign-in.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/sign-in.imageset/sign-in.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/sign-in.imageset/sign-in.pdf new file mode 100644 index 0000000000000..0afa802a85599 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/sign-in.imageset/sign-in.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/Contents.json new file mode 100644 index 0000000000000..f35c3fb1a1a54 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "tree.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "tree-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/tree-dark.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/tree-dark.pdf new file mode 100644 index 0000000000000..8929c0694e2b6 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/tree-dark.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/tree.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/tree.pdf new file mode 100644 index 0000000000000..2282ec9c68c17 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/tree.imageset/tree.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/trees.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/trees.imageset/Contents.json new file mode 100644 index 0000000000000..dfce1fcfa8a7c --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/trees.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "trees.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/trees.imageset/trees.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/trees.imageset/trees.pdf new file mode 100644 index 0000000000000..d7644cbb81468 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/trees.imageset/trees.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/treesUpdate.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/treesUpdate.imageset/Contents.json new file mode 100644 index 0000000000000..e4c002e5b9bbe --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/treesUpdate.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "treesUpdate.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "original" + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/treesUpdate.imageset/treesUpdate.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/treesUpdate.imageset/treesUpdate.pdf new file mode 100644 index 0000000000000..1080590586ed1 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/treesUpdate.imageset/treesUpdate.pdf differ diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/wave-forms-horizontal-1.imageset/Contents.json b/firefox-ios/Ecosia/UI/Common.xcassets/wave-forms-horizontal-1.imageset/Contents.json new file mode 100644 index 0000000000000..61e9f63aa7434 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Common.xcassets/wave-forms-horizontal-1.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "wave-forms-horizontal.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/Common.xcassets/wave-forms-horizontal-1.imageset/wave-forms-horizontal.pdf b/firefox-ios/Ecosia/UI/Common.xcassets/wave-forms-horizontal-1.imageset/wave-forms-horizontal.pdf new file mode 100644 index 0000000000000..1651645dc3841 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Common.xcassets/wave-forms-horizontal-1.imageset/wave-forms-horizontal.pdf differ diff --git a/firefox-ios/Ecosia/UI/Components/EcosiaButtonStyle.swift b/firefox-ios/Ecosia/UI/Components/EcosiaButtonStyle.swift new file mode 100644 index 0000000000000..26d281e2a39d9 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Components/EcosiaButtonStyle.swift @@ -0,0 +1,51 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// Button style that shows highlight state during tap +public struct EcosiaButtonStyle: ButtonStyle { + public enum Style { + case bare + case outline + } + + let theme: Theme + let style: Style + let cornerRadius: CGFloat + let borderWidth: CGFloat + + public init(theme: Theme, style: Style = .outline, cornerRadius: CGFloat = 22, borderWidth: CGFloat = 1) { + self.theme = theme + self.style = style + self.cornerRadius = cornerRadius + self.borderWidth = borderWidth + } + + public func makeBody(configuration: Configuration) -> some View { + switch style { + case .bare: + configuration.label + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(Color(configuration.isPressed + ? theme.colors.ecosia.buttonBackgroundTransparentActive + : .clear)) + ) + case .outline: + configuration.label + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .stroke(Color(theme.colors.ecosia.borderDecorative), lineWidth: borderWidth) + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(Color(configuration.isPressed + ? theme.colors.ecosia.buttonBackgroundSecondaryActive + : .clear)) + ) + ) + } + } +} diff --git a/firefox-ios/Ecosia/UI/Components/EcosiaErrorView.swift b/firefox-ios/Ecosia/UI/Components/EcosiaErrorView.swift new file mode 100644 index 0000000000000..32ba4a115bc66 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Components/EcosiaErrorView.swift @@ -0,0 +1,163 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A generic error view component that displays error messages with optional title and subtitle +/// Optionally includes a close button and dismissal callback +@available(iOS 16.0, *) +public struct EcosiaErrorView: View { + private let title: String? + private let subtitle: String + private let windowUUID: WindowUUID + private let onCloseTapped: (() -> Void)? + private let onDismiss: (() -> Void)? + + @State private var theme = EcosiaErrorViewTheme() + + private struct UX { + static let borderWidth: CGFloat = 1 + static let closeButtonSize: CGFloat = 16 + } + + /// Initialize error view with title and subtitle + /// - Parameters: + /// - title: Optional bold title text. If nil, only subtitle is shown + /// - subtitle: Main error message text + /// - windowUUID: Window UUID for theming + /// - onCloseTapped: Optional closure called when close button is tapped. If nil, close button is hidden. + /// This closure should handle initiating dismissal (e.g., starting animations) + /// - onDismiss: Optional closure called when view dismissal is complete (e.g., after animations finish). + /// The caller is responsible for calling this at the appropriate time. + public init( + title: String? = nil, + subtitle: String, + windowUUID: WindowUUID, + onCloseTapped: (() -> Void)? = nil, + onDismiss: (() -> Void)? = nil + ) { + self.title = title + self.subtitle = subtitle + self.windowUUID = windowUUID + self.onCloseTapped = onCloseTapped + self.onDismiss = onDismiss + } + + public var body: some View { + HStack(alignment: .center, spacing: .ecosia.space._s) { + // Error icon + Image("problem", bundle: .ecosia) + .resizable() + .frame(width: .ecosia.space._1l, height: .ecosia.space._1l) + .foregroundColor(theme.iconColor) + .accessibilityLabel(String.localized(.ecosiaErrorViewAccessibilityImageLabel)) + .accessibilityIdentifier("error_view_image") + + // Text content + VStack(alignment: .leading, spacing: .ecosia.space._1s) { + if let title = title { + Text(title) + .font(.subheadline.bold()) + .foregroundColor(theme.textPrimaryColor) + } + + Text(subtitle) + .font(.subheadline) + .foregroundColor(title != nil ? theme.textSecondaryColor : theme.textPrimaryColor) + } + .frame(maxWidth: .infinity, alignment: .leading) + + // Close button (conditionally shown) + if let onCloseTapped = onCloseTapped { + Button(action: { + onCloseTapped() + }) { + Image("close", bundle: .ecosia) + .renderingMode(.template) + .resizable() + .frame(width: UX.closeButtonSize, height: UX.closeButtonSize) + .foregroundColor(theme.closeButtonColor) + } + .accessibilityLabel(String.localized(.close)) + .accessibilityAddTraits(.isButton) + } + } + .padding(.horizontal, .ecosia.space._s) + .padding(.vertical, .ecosia.space._1s) + .background( + RoundedRectangle(cornerRadius: .ecosia.borderRadius._m) + .fill(theme.backgroundColor) + .overlay( + RoundedRectangle(cornerRadius: .ecosia.borderRadius._m) + .stroke(theme.borderColor, lineWidth: UX.borderWidth) + ) + ) + .ecosiaThemed(windowUUID, $theme) + } +} + +// MARK: - Theme +@available(iOS 16.0, *) +struct EcosiaErrorViewTheme: EcosiaThemeable { + var backgroundColor = Color.white + var borderColor = Color.pink + var textPrimaryColor = Color.black + var textSecondaryColor = Color.gray + var iconColor = Color.red + var closeButtonColor = Color.gray + + mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.ecosia.backgroundRoleNegative) + borderColor = Color(theme.colors.ecosia.borderNegative) + textPrimaryColor = Color(theme.colors.ecosia.textPrimary) + textSecondaryColor = Color(theme.colors.ecosia.textSecondary) + iconColor = Color(theme.colors.ecosia.stateError) + closeButtonColor = Color(theme.colors.ecosia.buttonContentSecondary) + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaErrorView_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: .ecosia.space._l) { + // With title, subtitle, and close button + EcosiaErrorView( + title: String.localized(.couldNotLoadSeedCounter), + subtitle: String.localized(.couldNotLoadSeedCounterMessage), + windowUUID: .XCTestDefaultUUID, + onCloseTapped: { print("Close tapped") }, + onDismiss: { print("Dismissed") } + ) + + // Subtitle only with close button + EcosiaErrorView( + subtitle: String.localized(.signInErrorMessage), + windowUUID: .XCTestDefaultUUID, + onCloseTapped: { print("Close tapped") } + ) + + // Without close button but with dismiss callback + EcosiaErrorView( + title: "Error Title", + subtitle: "This error view has no close button", + windowUUID: .XCTestDefaultUUID, + onDismiss: { print("Dismissed externally") } + ) + + // Long text example with close button + EcosiaErrorView( + title: "Error Title", + subtitle: "This is a longer error message that should wrap to multiple lines to show how the component handles longer text content.", + windowUUID: .XCTestDefaultUUID, + onCloseTapped: { print("Close tapped") }, + onDismiss: { print("Dismissed") } + ) + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/Components/EcosiaThemedSwitch.swift b/firefox-ios/Ecosia/UI/Components/EcosiaThemedSwitch.swift new file mode 100644 index 0000000000000..9ad7313792c15 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Components/EcosiaThemedSwitch.swift @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit + +public class EcosiaThemedSwitch: UISwitch, ThemeApplicable { + private var enabledThumbColor: UIColor? + private var disabledThumbColor: UIColor? + private var enabledBackgroundColor: UIColor? + private var disabledBackgroundColor: UIColor? + + override public init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + private func setup() { + addTarget(self, action: #selector(valueChanged), for: .valueChanged) + } + + @objc + func valueChanged(_ control: UISwitch) { + thumbTintColor = isOn ? enabledThumbColor : disabledThumbColor + } + + public func applyTheme(theme: Theme) { + enabledThumbColor = theme.colors.ecosia.switchKnobActive + disabledThumbColor = theme.colors.ecosia.switchKnobDisabled + enabledBackgroundColor = theme.colors.ecosia.buttonBackgroundPrimary + disabledBackgroundColor = theme.colors.ecosia.stateDisabled + onTintColor = enabledBackgroundColor + tintColor = disabledBackgroundColor + thumbTintColor = isOn ? enabledThumbColor : disabledThumbColor + } +} diff --git a/firefox-ios/Ecosia/UI/ConfigurableNudgeCardView.swift b/firefox-ios/Ecosia/UI/ConfigurableNudgeCardView.swift new file mode 100644 index 0000000000000..ad2b82ecbd5c6 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ConfigurableNudgeCardView.swift @@ -0,0 +1,234 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A protocol defining actions that can be triggered from a Nudge Card. +public protocol ConfigurableNudgeCardActionDelegate: AnyObject { + func nudgeCardRequestToPerformAction() + func nudgeCardRequestToDimiss() + func nudgeCardTapped() +} + +/// A style configuration object for `ConfigurableNudgeCardView`, defining color values for rendering. +public struct NudgeCardStyle { + let backgroundColor: Color + let textPrimaryColor: Color + let textSecondaryColor: Color + let closeButtonTextColor: Color + let actionButtonTextColor: Color + + public init(backgroundColor: Color, + textPrimaryColor: Color, + textSecondaryColor: Color, + closeButtonTextColor: Color, + actionButtonTextColor: Color) { + self.backgroundColor = backgroundColor + self.textPrimaryColor = textPrimaryColor + self.textSecondaryColor = textSecondaryColor + self.closeButtonTextColor = closeButtonTextColor + self.actionButtonTextColor = actionButtonTextColor + } +} + +/// Layout configuration for `ConfigurableNudgeCardView`. +public struct NudgeCardLayout { + let imageSize: CGFloat + let closeButtonSize: CGFloat + let closeButtonPaddingTop: CGFloat + let closeButtonPaddingLeading: CGFloat + let closeButtonPaddingBottom: CGFloat + let closeButtonPaddingTrailing: CGFloat + let horizontalSpacing: CGFloat + let verticalSpacing: CGFloat + let contentPadding: CGFloat + let cornerRadius: CGFloat + let borderWidth: CGFloat + let titleFont: Font + let descriptionFont: Font + let buttonFont: Font + let buttonTopPadding: CGFloat + + public init(imageSize: CGFloat = 48, + closeButtonSize: CGFloat = 15, + closeButtonPaddingTop: CGFloat = 0, + closeButtonPaddingLeading: CGFloat = 0, + closeButtonPaddingBottom: CGFloat = 0, + closeButtonPaddingTrailing: CGFloat = 0, + horizontalSpacing: CGFloat = .ecosia.space._2s, + verticalSpacing: CGFloat = .ecosia.space._2s, + contentPadding: CGFloat = .ecosia.space._m, + cornerRadius: CGFloat = .ecosia.borderRadius._l, + borderWidth: CGFloat = 1, + titleFont: Font = .ecosia(size: .ecosia.font._l, weight: .bold), + descriptionFont: Font = .ecosia(size: .ecosia.font._m), + buttonFont: Font = .ecosia(size: .ecosia.font._m), + buttonTopPadding: CGFloat = .ecosia.space._1s) { + self.imageSize = imageSize + self.closeButtonSize = closeButtonSize + self.closeButtonPaddingTop = closeButtonPaddingTop + self.closeButtonPaddingLeading = closeButtonPaddingLeading + self.closeButtonPaddingBottom = closeButtonPaddingBottom + self.closeButtonPaddingTrailing = closeButtonPaddingTrailing + self.horizontalSpacing = horizontalSpacing + self.verticalSpacing = verticalSpacing + self.contentPadding = contentPadding + self.cornerRadius = cornerRadius + self.borderWidth = borderWidth + self.titleFont = titleFont + self.descriptionFont = descriptionFont + self.buttonFont = buttonFont + self.buttonTopPadding = buttonTopPadding + } + + /// Default layout for standard nudge cards + public static let `default` = NudgeCardLayout() +} + +/// A view model containing the content and style information used to render a `ConfigurableNudgeCardView`. +public struct NudgeCardViewModel { + /// A card must have a title. + let title: String + /// Pass `nil` to hide the description text. + let description: String? + /// Pass `nil` to hide the bottom action button. + let buttonText: String? + /// Pass `nil` to hide the image. + let image: UIImage? + let showsCloseButton: Bool + var style: NudgeCardStyle + let layout: NudgeCardLayout + + public init(title: String, + description: String? = nil, + buttonText: String? = nil, + image: UIImage? = nil, + showsCloseButton: Bool = true, + style: NudgeCardStyle, + layout: NudgeCardLayout = .default) { + self.title = title + self.description = description + self.buttonText = buttonText + self.image = image + self.showsCloseButton = showsCloseButton + self.style = style + self.layout = layout + } +} + +/// A SwiftUI view representing a configurable card with optional image, text, action button, and close button. +/// Used in collection view cells like NTP Cards or the Default Browser Card. +public struct ConfigurableNudgeCardView: View { + var viewModel: NudgeCardViewModel? + weak var delegate: ConfigurableNudgeCardActionDelegate? + + public init(viewModel: NudgeCardViewModel? = nil, + delegate: ConfigurableNudgeCardActionDelegate? = nil) { + self.viewModel = viewModel + self.delegate = delegate + } + + public var body: some View { + HStack(alignment: .top, spacing: viewModel?.layout.horizontalSpacing ?? .ecosia.space._2s) { + // Image + if let image = viewModel?.image { + Image(uiImage: image) + .resizable() + .frame(width: viewModel?.layout.imageSize ?? 48, + height: viewModel?.layout.imageSize ?? 48) + .accessibilityHidden(true) + } + + // Text and Action Stack + VStack(alignment: .leading, + spacing: viewModel?.layout.verticalSpacing ?? .ecosia.space._2s) { + if let title = viewModel?.title { + Text(title) + .font(viewModel?.layout.titleFont ?? .headline.bold()) + .foregroundColor(viewModel?.style.textPrimaryColor) + .multilineTextAlignment(.leading) + .accessibilityLabel(title) + .accessibilityIdentifier("nudge_card_title") + } + + if let description = viewModel?.description { + Text(description) + .font(viewModel?.layout.descriptionFont ?? .subheadline) + .foregroundColor(viewModel?.style.textSecondaryColor) + .multilineTextAlignment(.leading) + .accessibilityLabel(description) + .accessibilityIdentifier("nudge_card_description") + } + + if let buttonText = viewModel?.buttonText { + Button(action: { + delegate?.nudgeCardRequestToPerformAction() + }) { + Text(buttonText) + .font(viewModel?.layout.buttonFont ?? .subheadline) + .foregroundColor(viewModel?.style.actionButtonTextColor) + } + .padding(.top, viewModel?.layout.buttonTopPadding ?? .ecosia.space._1s) + .accessibilityLabel(buttonText) + .accessibilityIdentifier("nudge_card_cta_button") + .accessibilityAddTraits(.isButton) + } + } + + // Close button + if viewModel?.showsCloseButton == true { + Button(action: { + delegate?.nudgeCardRequestToDimiss() + }) { + Image("close", bundle: .ecosia) + .renderingMode(.template) + .resizable() + .frame(width: viewModel?.layout.closeButtonSize ?? 15, + height: viewModel?.layout.closeButtonSize ?? 15) + .foregroundStyle(viewModel?.style.closeButtonTextColor ?? .primaryText) + .accessibilityLabel(String.localized(.configurableNudgeCardCloseButtonAccessibilityLabel)) + .accessibilityIdentifier("nudge_card_close_button") + .accessibilityAddTraits(.isButton) + } + .padding(.top, viewModel?.layout.closeButtonPaddingTop ?? 0) + .padding(.leading, viewModel?.layout.closeButtonPaddingLeading ?? 0) + .padding(.bottom, viewModel?.layout.closeButtonPaddingBottom ?? 0) + .padding(.trailing, viewModel?.layout.closeButtonPaddingTrailing ?? 0) + } + } + .onTapGesture { + delegate?.nudgeCardTapped() + } + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.isButton) + .padding(viewModel?.layout.contentPadding ?? .ecosia.space._m) + .background(viewModel?.style.backgroundColor) + .clipShape(RoundedRectangle(cornerRadius: viewModel?.layout.cornerRadius ?? .ecosia.borderRadius._l)) + .overlay( + RoundedRectangle(cornerRadius: viewModel?.layout.cornerRadius ?? .ecosia.borderRadius._l) + .stroke(.border, lineWidth: viewModel?.layout.borderWidth ?? 1) + ) + } +} + +#Preview{ + let mockViewModel = NudgeCardViewModel( + title: "Make ecosia your default browser app", + description: "Safely open all links from other apps in Ecosia", + buttonText: "Take Action", + image: .init(named: "default-browser-card-side-image-koto-illustrations", + in: .ecosia, + with: nil), + style: NudgeCardStyle(backgroundColor: .primaryBackground, + textPrimaryColor: .primaryText, + textSecondaryColor: .primaryText, + closeButtonTextColor: .primaryText, + actionButtonTextColor: .primaryBrand), + layout: .default + ) + + ConfigurableNudgeCardView(viewModel: mockViewModel, delegate: nil) + .padding() +} diff --git a/firefox-ios/Ecosia/UI/DesignSystem/CGFloat+DesignSystem.swift b/firefox-ios/Ecosia/UI/DesignSystem/CGFloat+DesignSystem.swift new file mode 100644 index 0000000000000..f95159f748f2a --- /dev/null +++ b/firefox-ios/Ecosia/UI/DesignSystem/CGFloat+DesignSystem.swift @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +public extension CGFloat { + static let ecosia = EcosiaFloatDesignSystemFoundations() +} + +public struct EcosiaFloatDesignSystemFoundations { + public let font = EcosiaTypography() + public let space = EcosiaSpacing() + public let borderRadius = EcosiaBorders() +} diff --git a/firefox-ios/Ecosia/UI/DesignSystem/EcosiaBorders.swift b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaBorders.swift new file mode 100644 index 0000000000000..25136c5b9b6b8 --- /dev/null +++ b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaBorders.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +// This class contains all of Ecosia official border radies system values referenced on the link below. +// https://www.figma.com/design/8T2rTBVwynJKSdY6MQo5PQ/%E2%9A%9B%EF%B8%8F--Foundations?node-id=71-588 +// Only values present on the above link should be added here and you should avoid using any border radius values outside these. +public struct EcosiaBorders { + public let _s: CGFloat = 4 + public let _m: CGFloat = 6 + public let _l: CGFloat = 10 + public let _1l: CGFloat = 20 +} diff --git a/firefox-ios/Ecosia/UI/DesignSystem/EcosiaColor.swift b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaColor.swift new file mode 100644 index 0000000000000..f411add169fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaColor.swift @@ -0,0 +1,89 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +// This class contains all of Ecosia official primitive color tokens referenced in the link below. +// https://www.figma.com/design/8T2rTBVwynJKSdY6MQo5PQ/%E2%9A%9B%EF%B8%8F--Foundations?node-id=1239-9385&t=UKHtrxcc9UtOihsm-4 +// You should never call those colors directly, they should only be called from a theme within the theme manager. +// This is the equivalent to Firefox's `FXColors`. +public struct EcosiaColor { + // MARK: - Black & White + public static let Black = UIColor(rgb: 0x000000) + public static let White = UIColor(rgb: 0xFFFFFF) + + // MARK: - Neutral + public static let Gray10 = UIColor(rgb: 0xF8F8F6) + public static let Gray20 = UIColor(rgb: 0xF0F0EB) + public static let Gray30 = UIColor(rgb: 0xDEDED9) + public static let Gray40 = UIColor(rgb: 0xBEBEB9) + public static let Gray50 = UIColor(rgb: 0x6C6C6C) + public static let Gray60 = UIColor(rgb: 0x4C4C4C) + public static let Gray70 = UIColor(rgb: 0x333333) + public static let Gray80 = UIColor(rgb: 0x252525) + public static let Gray90 = UIColor(rgb: 0x1A1A1A) + + // MARK: - Grellow + public static let Grellow100 = UIColor(rgb: 0xD7EB80) + public static let Grellow200 = UIColor(rgb: 0xBBCF65) + public static let Grellow300 = UIColor(rgb: 0xA1B353) + public static let Grellow400 = UIColor(rgb: 0x889745) + public static let Grellow500 = UIColor(rgb: 0x6F7D38) + public static let Grellow600 = UIColor(rgb: 0x58632B) + public static let Grellow700 = UIColor(rgb: 0x424A1E) + public static let Grellow800 = UIColor(rgb: 0x2D3315) + public static let Grellow900 = UIColor(rgb: 0x1B1D0F) + + // MARK: - Green + public static let Green10 = UIColor(rgb: 0xCFF2D0) + public static let Green20 = UIColor(rgb: 0xAFE9B0) + public static let Green30 = UIColor(rgb: 0x5DD25E) + public static let Green40 = UIColor(rgb: 0xA4D24F) + public static let Green50 = UIColor(rgb: 0x008009) + public static let Green60 = UIColor(rgb: 0x007508) + public static let Green70 = UIColor(rgb: 0x006600) + + // MARK: - Dark Green + public static let DarkGreen30 = UIColor(rgb: 0x668A7A) + public static let DarkGreen50 = UIColor(rgb: 0x275243) + public static let DarkGreen70 = UIColor(rgb: 0x09281D) + public static let DarkGreen800 = UIColor(rgb: 0x18362B) + + // MARK: - Light Green + public static let LightGreen20 = UIColor(rgb: 0xBACC80) + public static let LightGreen30 = UIColor(rgb: 0xD8FF80) + public static let LightGreen40 = UIColor(rgb: 0xA4D24F) + public static let LightGreen50 = UIColor(rgb: 0x72A11A) + public static let LightGreen60 = UIColor(rgb: 0x40521F) + + // MARK: - Red + public static let Red20 = UIColor(rgb: 0xFFE8DA) + public static let Red30 = UIColor(rgb: 0xFF8A8C) + public static let Red40 = UIColor(rgb: 0xFD4256) + public static let Red50 = UIColor(rgb: 0xAF1731) + + // MARK: - Yellow + public static let Yellow40 = UIColor(rgb: 0xF7BC00) + public static let Yellow50 = UIColor(rgb: 0xD6A300) + + // MARK: - Blue + public static let Blue30 = UIColor(rgb: 0x96D6F8) + public static let Blue40 = UIColor(rgb: 0x0094C7) + public static let Blue50 = UIColor(rgb: 0x007EA8) + public static let Blue60 = UIColor(rgb: 0x005D87) + public static let Blue70 = UIColor(rgb: 0x004687) + public static let Blue80 = UIColor(rgb: 0x002A3D) + + // MARK: - Peach + public static let Peach30 = UIColor(rgb: 0xFFE6BF) + public static let Peach40 = UIColor(rgb: 0xFFAF87) + public static let Peach50 = UIColor(rgb: 0xCA8461) + public static let Peach100 = UIColor(rgb: 0xFCDBCC) + public static let Peach700 = UIColor(rgb: 0x77300A) + + // MARK: - Claret + public static let Claret300 = UIColor(rgb: 0xD89AA6) + public static let Claret600 = UIColor(rgb: 0x8F4759) + public static let Claret800 = UIColor(rgb: 0x4C232D) +} diff --git a/firefox-ios/Ecosia/UI/DesignSystem/EcosiaSpacing.swift b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaSpacing.swift new file mode 100644 index 0000000000000..520d5b2647479 --- /dev/null +++ b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaSpacing.swift @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +// This class contains all of Ecosia official spacing system values referenced on the link below. +// https://www.figma.com/design/8T2rTBVwynJKSdY6MQo5PQ/%E2%9A%9B%EF%B8%8F--Foundations?node-id=13-83 +// Only values present on the above link should be added here and you should avoid using any spacing values outside these. +public struct EcosiaSpacing { + public let _2s: CGFloat = 4 + public let _1s: CGFloat = 8 + public let _s: CGFloat = 12 + public let _m: CGFloat = 16 + public let _l: CGFloat = 20 + public let _1l: CGFloat = 24 + public let _2l: CGFloat = 32 + public let _3l: CGFloat = 40 + public let _4l: CGFloat = 48 + public let _5l: CGFloat = 56 + public let _6l: CGFloat = 64 + public let _7l: CGFloat = 80 + public let _8l: CGFloat = 100 +} diff --git a/firefox-ios/Ecosia/UI/DesignSystem/EcosiaTypography.swift b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaTypography.swift new file mode 100644 index 0000000000000..e8918b11bbc48 --- /dev/null +++ b/firefox-ios/Ecosia/UI/DesignSystem/EcosiaTypography.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +public struct EcosiaTypography { + public let _1s: CGFloat = 10 + public let _s: CGFloat = 12 + public let _m: CGFloat = 14 + public let _l: CGFloat = 16 + public let _1l: CGFloat = 18 + public let _2l: CGFloat = 20 + public let _3l: CGFloat = 24 + public let _4l: CGFloat = 36 + public let _5l: CGFloat = 48 + public let _6l: CGFloat = 54 + public let _7l: CGFloat = 72 +} diff --git a/firefox-ios/Ecosia/UI/DesignSystem/UIFont+DesignSystem.swift b/firefox-ios/Ecosia/UI/DesignSystem/UIFont+DesignSystem.swift new file mode 100644 index 0000000000000..a0184b3bf02c1 --- /dev/null +++ b/firefox-ios/Ecosia/UI/DesignSystem/UIFont+DesignSystem.swift @@ -0,0 +1,30 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SwiftUI + +private let familyBrandFontName = "FoundersGroteskCond-SmBd" + +extension UIFont { + + public static func ecosiaFamilyBrand(size: CGFloat) -> UIFont { + return UIFont(name: familyBrandFontName, size: size) ?? systemFont(ofSize: size, weight: .semibold) + } + + public static func ecosia(size: CGFloat, weight: UIFont.Weight = .regular) -> UIFont { + return UIFont.systemFont(ofSize: size, weight: weight) + } +} + +extension Font { + + public static func ecosiaFamilyBrand(size: CGFloat) -> Font { + Font.custom(familyBrandFontName, size: size) + } + + public static func ecosia(size: CGFloat, weight: Font.Weight = .regular) -> Font { + Font.system(size: size, weight: weight) + } +} diff --git a/firefox-ios/Ecosia/UI/DynamicPresentationDetentModifier.swift b/firefox-ios/Ecosia/UI/DynamicPresentationDetentModifier.swift new file mode 100644 index 0000000000000..f7886d63a3929 --- /dev/null +++ b/firefox-ios/Ecosia/UI/DynamicPresentationDetentModifier.swift @@ -0,0 +1,70 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +/// A view modifier that provides dynamic presentation detents based on content height +@available(iOS 16.0, *) +public struct DynamicPresentationDetentModifier: ViewModifier { + @State private var contentHeight: CGFloat = 0 + let minHeight: CGFloat + let padding: CGFloat + + public init(minHeight: CGFloat, padding: CGFloat) { + self.minHeight = minHeight + self.padding = padding + } + + public func body(content: Content) -> some View { + let calculatedHeight = contentHeight > 0 ? max(contentHeight + padding, minHeight) : minHeight + + content + .overlay( + GeometryReader { geometry in + Color.clear.preference( + key: HeightPreferenceKey.self, + value: geometry.size.height + ) + } + ) + .onPreferenceChange(HeightPreferenceKey.self) { height in + if height > 0 { + contentHeight = height + } + } + .presentationDetents([.height(calculatedHeight)]) + .presentationDragIndicator(.automatic) + } +} + +// MARK: - Height Preference Key + +@available(iOS 16.0, *) +private struct HeightPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = nextValue() + } +} + +// MARK: - View Extension + +@available(iOS 16.0, *) +public extension View { + /// Applies dynamic height presentation detent that adjusts to fit content + /// - Parameters: + /// - minHeight: Minimum height for the presentation (default: 2 times the `.ecosia.space._8l`) + /// - padding: Additional padding to add to content height (default: `.ecosia.space._3l`) + /// - Returns: A view with dynamic height presentation detent + func dynamicHeightPresentationDetent( + minHeight: CGFloat = .ecosia.space._8l * 2, + padding: CGFloat = .ecosia.space._3l + ) -> some View { + modifier(DynamicPresentationDetentModifier( + minHeight: minHeight, + padding: padding + )) + } +} diff --git a/firefox-ios/Ecosia/UI/EcosiaAccessibilityIdentifiers.swift b/firefox-ios/Ecosia/UI/EcosiaAccessibilityIdentifiers.swift new file mode 100644 index 0000000000000..5a1099d087948 --- /dev/null +++ b/firefox-ios/Ecosia/UI/EcosiaAccessibilityIdentifiers.swift @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +/// Accessibility identifiers for Ecosia-specific UI elements +public struct EcosiaAccessibilityIdentifiers { + public static let logo = "ecosia-logo" + + public struct TabToolbar { + public static let circleButton = "TabToolbar.circleButton" + } + + public struct FindInPage { + public static let searchField = "FindInPage.searchField" + public static let matchCount = "FindInPage.matchCount" + public static let findPrevious = "FindInPage.find_previous" + public static let findNext = "FindInPage.find_next" + public static let findClose = "FindInPage.close" + } +} diff --git a/firefox-ios/Ecosia/UI/EcosiaText.swift b/firefox-ios/Ecosia/UI/EcosiaText.swift new file mode 100644 index 0000000000000..e900734bca43c --- /dev/null +++ b/firefox-ios/Ecosia/UI/EcosiaText.swift @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +/// A view that displays localized text based on a given key. +struct EcosiaText: View { + /// The key used to retrieve the localized string. + let key: String.Key + /// An optional comment that provides additional context for the localization. + let comment: String + + /// Initializes a new instance of `EcosiaText` with the specified localization key and optional comment. + /// + /// - Parameters: + /// - key: The key used to retrieve the localized string. + /// - comment: An optional comment that provides additional context for the localization. Default is an empty string. + init(_ key: String.Key, comment: String = "") { + self.key = key + self.comment = comment + } + + var body: some View { + if let parsed = try? AttributedString(markdown: .localized(key)) { + Text(parsed) + } else { + Text(verbatim: .localized(key)) + } + } +} diff --git a/firefox-ios/Ecosia/UI/Extensions/Color+Ecosia.swift b/firefox-ios/Ecosia/UI/Extensions/Color+Ecosia.swift new file mode 100644 index 0000000000000..2458b901f644c --- /dev/null +++ b/firefox-ios/Ecosia/UI/Extensions/Color+Ecosia.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +extension Color { + + /// Returns a `Color` from the Ecosia bundle for a given name. + /// + /// This method retrieves a color from the Ecosia resources within the app bundle using the specified name. + /// + /// - Parameter name: The name of the color to retrieve. + /// - Returns: A `Color` object corresponding to the specified color name from the Ecosia bundle. + public static func ecosiaBundledColorWithName(_ name: String) -> Color { + Color(name, bundle: Bundle.ecosia) + } +} diff --git a/firefox-ios/Ecosia/UI/Extensions/SwiftUI+AccessibleAnimation.swift b/firefox-ios/Ecosia/UI/Extensions/SwiftUI+AccessibleAnimation.swift new file mode 100644 index 0000000000000..7ac79554ff257 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Extensions/SwiftUI+AccessibleAnimation.swift @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +public func withOptionalAnimation(_ animation: Animation? = .default, _ body: () throws -> Result) rethrows -> Result { + if UIAccessibility.isReduceMotionEnabled { + return try body() + } else { + return try withAnimation(animation, body) + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackContentView.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackContentView.swift new file mode 100644 index 0000000000000..2c3bd321a58d9 --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackContentView.swift @@ -0,0 +1,87 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +struct FeedbackContentView: View { + @State private var theme = FeedbackContentViewTheme() + let windowUUID: WindowUUID? + + @Binding var selectedFeedbackType: FeedbackType? + @Binding var feedbackText: String + @Binding var isButtonEnabled: Bool + let updateButtonState: () -> Void + let sendFeedback: () -> Void + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: .ecosia.space._1l) { + Text(String.localized(.whatWouldYouLikeToShare)) + .font(.title3) + .foregroundColor(theme.textPrimaryColor) + .padding(.horizontal, .ecosia.space._m) + .padding(.top, .ecosia.space._m) + .accessibilityIdentifier("feedback_title") + + FeedbackTypeSection( + windowUUID: windowUUID, + selectedFeedbackType: $selectedFeedbackType, + updateButtonState: updateButtonState + ) + VStack(spacing: .ecosia.space._m) { + ZStack(alignment: .topLeading) { + theme.backgroundColor + + TextEditor(text: $feedbackText) + .font(.body) + .transparentScrolling() + .foregroundColor(theme.textPrimaryColor) + .padding(.horizontal, .ecosia.space._s) + .padding(.vertical, .ecosia.space._m) + .border(theme.borderColor, width: theme.borderWidth) + .onChange(of: feedbackText) { _ in + updateButtonState() + } + + if feedbackText.isEmpty { + Text(String.localized(.addMoreDetailAboutYourFeedback)) + .font(.body) + .foregroundColor(theme.textSecondaryColor) + .padding(.horizontal, .ecosia.space._m) + .padding(.vertical, .ecosia.space._1l) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + .frame(height: FeedbackView.UX.textEditorHeight) + .cornerRadius(.ecosia.borderRadius._l) + .padding(.top, .ecosia.space._m) + .padding(.horizontal, .ecosia.space._m) + + Button(action: sendFeedback) { + Text(String.localized(.send)) + .font(.body.bold()) + .frame(maxWidth: .infinity) + .padding(.ecosia.space._m) + .foregroundColor(.white) + .background(isButtonEnabled ? theme.buttonBackgroundColor : theme.buttonDisabledBackgroundColor) + .cornerRadius(.ecosia.borderRadius._m) + } + .disabled(!isButtonEnabled) + .clipShape(Capsule()) + .padding(.horizontal, .ecosia.space._m) + .padding(.bottom, .ecosia.space._m) + .accessibilityIdentifier("feedback_cta_button") + .accessibilityLabel(Text("Send feedback")) + .accessibilityAddTraits(.isButton) + } + .background(theme.sectionBackgroundColor) + .cornerRadius(.ecosia.borderRadius._l) + .padding(.horizontal, .ecosia.space._m) + } + .background(theme.backgroundColor) + .ecosiaThemed(windowUUID, $theme) + } + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackContentViewTheme.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackContentViewTheme.swift new file mode 100644 index 0000000000000..5402d20e188b7 --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackContentViewTheme.swift @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +public struct FeedbackContentViewTheme: EcosiaThemeable { + public var backgroundColor = Color.white + public var sectionBackgroundColor = Color.white + public var feedbackTypeListItemBackgroundColor = Color(UIColor.systemBackground) + public var textPrimaryColor = Color.black + public var textSecondaryColor = Color.gray + public var buttonBackgroundColor = Color.blue + public var buttonDisabledBackgroundColor = Color.gray + public var brandPrimaryColor = Color.blue + public var borderColor = Color.gray.opacity(0.2) + public var borderWidth: CGFloat = 1 + + public init() {} + + public init(from feedbackTheme: FeedbackTheme) { + self.backgroundColor = feedbackTheme.backgroundColor + self.sectionBackgroundColor = feedbackTheme.sectionBackgroundColor + self.feedbackTypeListItemBackgroundColor = feedbackTheme.feedbackTypeListItemBackgroundColor + self.textPrimaryColor = feedbackTheme.textPrimaryColor + self.textSecondaryColor = feedbackTheme.textSecondaryColor + self.buttonBackgroundColor = feedbackTheme.buttonBackgroundColor + self.buttonDisabledBackgroundColor = feedbackTheme.buttonDisabledBackgroundColor + self.brandPrimaryColor = feedbackTheme.brandPrimaryColor + self.borderColor = feedbackTheme.borderColor + self.borderWidth = feedbackTheme.borderWidth + } + + public mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.ecosia.backgroundPrimaryDecorative) + sectionBackgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + feedbackTypeListItemBackgroundColor = Color(theme.colors.ecosia.backgroundPrimary) + textPrimaryColor = Color(theme.colors.ecosia.textPrimary) + textSecondaryColor = Color(theme.colors.ecosia.textSecondary) + buttonBackgroundColor = Color(theme.colors.ecosia.buttonBackgroundPrimaryActive) + buttonDisabledBackgroundColor = Color(theme.colors.ecosia.stateDisabled) + brandPrimaryColor = Color(theme.colors.ecosia.brandPrimary) + borderColor = Color(theme.colors.ecosia.borderDecorative) + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTheme.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTheme.swift new file mode 100644 index 0000000000000..603d03f7671d7 --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTheme.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +public struct FeedbackTheme: EcosiaThemeable { + public var backgroundColor = Color.white + public var sectionBackgroundColor = Color.white + public var feedbackTypeListItemBackgroundColor = Color(UIColor.systemBackground) + public var textPrimaryColor = Color.black + public var textSecondaryColor = Color.gray + public var buttonBackgroundColor = Color.blue + public var buttonDisabledBackgroundColor = Color.gray + public var brandPrimaryColor = Color.blue + public var borderColor = Color.gray.opacity(0.2) + public var borderWidth: CGFloat = 1 + + public init() {} + + public mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.ecosia.backgroundPrimaryDecorative) + sectionBackgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + feedbackTypeListItemBackgroundColor = Color(theme.colors.ecosia.backgroundPrimary) + textPrimaryColor = Color(theme.colors.ecosia.textPrimary) + textSecondaryColor = Color(theme.colors.ecosia.textSecondary) + buttonBackgroundColor = Color(theme.colors.ecosia.buttonBackgroundPrimaryActive) + buttonDisabledBackgroundColor = Color(theme.colors.ecosia.stateDisabled) + brandPrimaryColor = Color(theme.colors.ecosia.brandPrimary) + borderColor = Color(theme.colors.ecosia.borderDecorative) + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackType.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackType.swift new file mode 100644 index 0000000000000..5325edc6a7f6b --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackType.swift @@ -0,0 +1,32 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +/// FeedbackType represents the type of feedback a user can submit +public enum FeedbackType: String, CaseIterable, Identifiable { + case reportIssue = "Report an issue" + case suggestionOrFeedback = "Suggestion or feedback" + + public var id: String { self.rawValue } + + var analyticsIdentfier: String { + switch self { + case .reportIssue: + return "report_issue" + case .suggestionOrFeedback: + return "suggestion_or_feedback" + } + } + + var localizedString: String { + switch self { + case .reportIssue: + return String.localized(.reportIssue) + case .suggestionOrFeedback: + return String.localized(.suggestionOrFeedback) + } + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTypeSection.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTypeSection.swift new file mode 100644 index 0000000000000..f628930771ab2 --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTypeSection.swift @@ -0,0 +1,52 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +struct FeedbackTypeSection: View { + @State private var theme = FeedbackTypeSectionTheme() + let windowUUID: WindowUUID? + @Binding var selectedFeedbackType: FeedbackType? + let updateButtonState: () -> Void + + var body: some View { + VStack { + VStack(spacing: 0) { + ForEach(FeedbackType.allCases) { type in + Button(action: { + selectedFeedbackType = type + updateButtonState() + }) { + HStack { + Text(type.localizedString) + .font(.body) + .foregroundColor(theme.textPrimaryColor) + + Spacer() + + if selectedFeedbackType == type { + Image(systemName: "checkmark") + .foregroundColor(theme.brandPrimaryColor) + .accessibility(label: Text("Selected")) + } + } + .padding(.ecosia.space._m) + .background(theme.sectionBackgroundColor) + } + + if type != FeedbackType.allCases.last { + Divider() + .padding(.leading, .ecosia.space._m) + } + } + } + .frame(minHeight: 44 * CGFloat(FeedbackType.allCases.count)) + } + .background(theme.sectionBackgroundColor) + .cornerRadius(.ecosia.borderRadius._l) + .padding(.horizontal, .ecosia.space._m) + .ecosiaThemed(windowUUID, $theme) + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTypeSectionTheme.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTypeSectionTheme.swift new file mode 100644 index 0000000000000..4e7696046aa50 --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackTypeSectionTheme.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +public struct FeedbackTypeSectionTheme: EcosiaThemeable { + public var sectionBackgroundColor = Color.white + public var textPrimaryColor = Color.black + public var brandPrimaryColor = Color.blue + + public init() {} + + public init(from contentTheme: FeedbackContentViewTheme) { + self.sectionBackgroundColor = contentTheme.sectionBackgroundColor + self.textPrimaryColor = contentTheme.textPrimaryColor + self.brandPrimaryColor = contentTheme.brandPrimaryColor + } + + public mutating func applyTheme(theme: Theme) { + sectionBackgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + textPrimaryColor = Color(theme.colors.ecosia.textPrimary) + brandPrimaryColor = Color(theme.colors.ecosia.brandPrimary) + } +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackView.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackView.swift new file mode 100644 index 0000000000000..006cc501092bf --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackView.swift @@ -0,0 +1,97 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import UIKit +import Common + +/// The SwiftUI view for collecting user feedback +public struct FeedbackView: View { + // User input state + @State private var selectedFeedbackType: FeedbackType? + @State private var feedbackText: String = "" + @State private var isButtonEnabled: Bool = false + + // Theme handling + @State private var theme = FeedbackTheme() + let windowUUID: WindowUUID? + + // Define a dismiss callback that will be injected by the hosting controller + var onDismiss: (() -> Void)? + // Callback for notifying when feedback was submitted + var onFeedbackSubmitted: (() -> Void)? + + // Layout constants + struct UX { + static let cornerRadius: CGFloat = .ecosia.borderRadius._l + static let buttonCornerRadius: CGFloat = 25 + static let textEditorHeight: CGFloat = 200 + } + + public init(windowUUID: WindowUUID? = nil) { + self.windowUUID = windowUUID + } + + public var body: some View { + NavigationView { + ZStack { + + theme.backgroundColor.ignoresSafeArea() + + let feedbackContent = FeedbackContentView( + windowUUID: windowUUID, + selectedFeedbackType: $selectedFeedbackType, + feedbackText: $feedbackText, + isButtonEnabled: $isButtonEnabled, + updateButtonState: updateButtonState, + sendFeedback: sendFeedback + ) + .navigationTitle(String.localized(.sendFeedback)) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button(String.localized(.close)) { + dismiss() + } + .foregroundColor(theme.brandPrimaryColor) + .accessibilityIdentifier("close_feedback_button") + } + } + + Group { + if #available(iOS 16.0, *) { + feedbackContent.scrollDismissesKeyboard(.interactively) + } else { + feedbackContent + } + } + } + .ecosiaThemed(windowUUID, $theme) + } + } + + /// Update the state of the send button based on user input + private func updateButtonState() { + isButtonEnabled = selectedFeedbackType != nil && !feedbackText.isEmpty + } + + /// Dismiss the view + private func dismiss() { + onDismiss?() + } + + /// Send the feedback to analytics and dismiss the view + private func sendFeedback() { + guard let selectedFeedbackType else { return } + + Analytics.shared.sendFeedback(feedbackText, + withType: selectedFeedbackType) + onFeedbackSubmitted?() + dismiss() + } +} + +#Preview { + FeedbackView(windowUUID: .XCTestDefaultUUID) +} diff --git a/firefox-ios/Ecosia/UI/FeedbackView/FeedbackViewController.swift b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackViewController.swift new file mode 100644 index 0000000000000..474067497c971 --- /dev/null +++ b/firefox-ios/Ecosia/UI/FeedbackView/FeedbackViewController.swift @@ -0,0 +1,35 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import UIKit +import Common + +/// UIKit wrapper for the SwiftUI FeedbackView +public class FeedbackViewController: UIHostingController { + /// Completion handler to be called when feedback is submitted + public var onFeedbackSubmitted: (() -> Void)? + + public init(windowUUID: WindowUUID? = nil) { + var feedbackView = FeedbackView(windowUUID: windowUUID) + + super.init(rootView: feedbackView) + + feedbackView.onDismiss = { [weak self] in + self?.dismiss(animated: true) + } + + feedbackView.onFeedbackSubmitted = { [weak self] in + self?.onFeedbackSubmitted?() + } + + self.rootView = feedbackView + self.modalPresentationStyle = .formSheet + self.isModalInPresentation = true + } + + @MainActor dynamic required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/firefox-ios/Ecosia/UI/InstructionStepsView.swift b/firefox-ios/Ecosia/UI/InstructionStepsView.swift new file mode 100644 index 0000000000000..c8c0822e4c686 --- /dev/null +++ b/firefox-ios/Ecosia/UI/InstructionStepsView.swift @@ -0,0 +1,215 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Lottie + +private struct InstructionStepsViewLayout { + static let stepNumberWidthHeight: CGFloat = 24 + static let stepsContainerCornerRadius: CGFloat = 10 + static let wavesHeight: CGFloat = 11 +} + +public struct InstructionStepsViewStyle { + let backgroundPrimaryColor: Color + let topContentBackgroundColor: Color + let stepsBackgroundColor: Color + let textPrimaryColor: Color + let textSecondaryColor: Color + let buttonBackgroundColor: Color + let buttonTextColor: Color + let stepRowStyle: StepRowStyle + + public init(backgroundPrimaryColor: Color, + topContentBackgroundColor: Color, + stepsBackgroundColor: Color, + textPrimaryColor: Color, + textSecondaryColor: Color, + buttonBackgroundColor: Color, + buttonTextColor: Color, + stepRowStyle: StepRowStyle) { + self.backgroundPrimaryColor = backgroundPrimaryColor + self.topContentBackgroundColor = topContentBackgroundColor + self.stepsBackgroundColor = stepsBackgroundColor + self.textPrimaryColor = textPrimaryColor + self.textSecondaryColor = textSecondaryColor + self.buttonBackgroundColor = buttonBackgroundColor + self.buttonTextColor = buttonTextColor + self.stepRowStyle = stepRowStyle + } +} + +/// A reusable instruction screen with a title, steps, and a CTA button. +struct InstructionStepsView: View { + let title: String.Key + let topContentView: TopContentView + let steps: [InstructionStep] + let buttonTitle: String.Key + let onButtonTap: () -> Void + let style: InstructionStepsViewStyle + + init(title: String.Key, + steps: [InstructionStep], + buttonTitle: String.Key, + onButtonTap: @escaping () -> Void, + style: InstructionStepsViewStyle, + @ViewBuilder topContentView: () -> TopContentView) { + self.title = title + self.steps = steps + self.buttonTitle = buttonTitle + self.onButtonTap = onButtonTap + self.style = style + self.topContentView = topContentView() + } + + var body: some View { + ZStack { + style.backgroundPrimaryColor + .ignoresSafeArea() + VStack(spacing: .ecosia.space._1l) { + ZStack(alignment: .bottom) { + style.topContentBackgroundColor + .ignoresSafeArea(edges: .top) + topContentView + Image("wave-forms-horizontal-1", bundle: .ecosia) + .resizable() + .renderingMode(.template) + .frame(height: InstructionStepsViewLayout.wavesHeight) + .foregroundStyle(style.backgroundPrimaryColor) + .accessibilityHidden(true) + } + + VStack(spacing: .ecosia.space._1l) { + VStack(alignment: .leading, + spacing: .ecosia.space._s) { + EcosiaText(title) + .font(.title2.bold()) + .foregroundColor(style.textPrimaryColor) + .accessibilityIdentifier("instruction_title") + .frame(maxWidth: .infinity, alignment: .leading) + + VStack(alignment: .leading, + spacing: .ecosia.space._s) { + renderedSteps + } + } + .frame(maxWidth: .infinity) + .padding(.ecosia.space._m) + .background(style.stepsBackgroundColor) + .cornerRadius(.ecosia.borderRadius._l) + + Button(action: onButtonTap) { + EcosiaText(buttonTitle) + .font(.body) + .foregroundColor(style.buttonTextColor) + .frame(maxWidth: .infinity) + .padding() + .background(style.buttonBackgroundColor) + } + .clipShape(Capsule()) + .accessibilityIdentifier("instruction_cta_button") + .accessibilityLabel(Text(buttonTitle.rawValue)) + .accessibilityAddTraits(.isButton) + } + .padding([.bottom, .leading, .trailing], .ecosia.space._1l) + } + } + } + + private var renderedSteps: some View { + ForEach(Array(steps.enumerated()), id: \.offset) { pair in + let index = pair.offset + let step = pair.element + StepRow(index: index, step: step, style: style.stepRowStyle) + } + } +} + +public struct StepRowStyle { + let stepNumberColor: Color + let stepNumberBackgroundColor: Color + let stepTextColor: Color + + public init(stepNumberColor: Color, + stepNumberBackgroundColor: Color = .clear, + stepTextColor: Color) { + self.stepNumberColor = stepNumberColor + self.stepNumberBackgroundColor = stepNumberBackgroundColor + self.stepTextColor = stepTextColor + } +} + +private struct StepRow: View { + let index: Int + let step: InstructionStep + let style: StepRowStyle + + var body: some View { + HStack(alignment: .center, + spacing: .ecosia.space._s) { + Text("\(index + 1)") + .font(.subheadline.bold()) + .foregroundColor(style.stepNumberColor) + .frame(width: InstructionStepsViewLayout.stepNumberWidthHeight, + height: InstructionStepsViewLayout.stepNumberWidthHeight) + .background(style.stepNumberBackgroundColor) + .clipShape(Circle()) + .accessibilityIdentifier("instruction_step_number") + + EcosiaText(step.text) + .font(.subheadline) + .foregroundColor(style.stepTextColor) + .multilineTextAlignment(.leading) + .accessibilityIdentifier("instruction_step_\(index + 1)_text") + } + .accessibilityElement(children: .combine) + } +} + +/// A single instruction step with its text. +struct InstructionStep { + let text: String.Key +} + +// MARK: - Preview + +#Preview { + InstructionStepsView( + title: .defaultBrowserCardDetailTitle, + steps: [ + InstructionStep(text: .defaultBrowserCardDetailInstructionStep1), + InstructionStep(text: .defaultBrowserCardDetailInstructionStep2), + InstructionStep(text: .defaultBrowserCardDetailInstructionStep3) + ], + buttonTitle: .defaultBrowserCardDetailButton, + onButtonTap: {}, + style: InstructionStepsViewStyle( + backgroundPrimaryColor: .tertiaryBackground, + topContentBackgroundColor: Color(UIColor(rgb: 0x275243)), + stepsBackgroundColor: .primaryBackground, + textPrimaryColor: .primaryText, + textSecondaryColor: .primaryText, + buttonBackgroundColor: .primaryBrand, + buttonTextColor: .primaryBackground, + stepRowStyle: StepRowStyle(stepNumberColor: .primary, + stepNumberBackgroundColor: .secondary, + stepTextColor: .primaryText) + ) + ) { + GeometryReader { geometry in + VStack { + Spacer() + LottieView { + try await DotLottieFile.named("default_browser_setup_animation", bundle: .ecosia) + } + .configuration(LottieConfiguration(renderingEngine: .mainThread)) + .looping() + .offset(y: UIDevice.current.userInterfaceIdiom == .pad ? 40 : 18) + .aspectRatio(contentMode: .fit) + .frame(width: geometry.size.width) + .clipped() + } + } + } +} diff --git a/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButton.swift b/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButton.swift new file mode 100644 index 0000000000000..ce85f1058a2d8 --- /dev/null +++ b/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButton.swift @@ -0,0 +1,58 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +@available(iOS 16.0, *) +public struct EcosiaAISearchButton: View { + private let windowUUID: WindowUUID + private let onTap: () -> Void + + @State private var theme = EcosiaAISearchButtonTheme() + + public init( + windowUUID: WindowUUID, + onTap: @escaping () -> Void + ) { + self.windowUUID = windowUUID + self.onTap = onTap + } + + public var body: some View { + Button(action: onTap) { + Image("ai-sparkle", bundle: .ecosia) + .resizable() + .renderingMode(.template) + .foregroundColor(theme.iconColor) + .frame(width: .ecosia.space._1l, height: .ecosia.space._1l) + .padding(.ecosia.space._2s) + .frame(width: .ecosia.space._3l, height: .ecosia.space._3l) + .background(theme.backgroundColor) + .cornerRadius(.ecosia.borderRadius._1l) + } + .buttonStyle(PlainButtonStyle()) + .accessibilityLabel("AI Search") + .accessibilityHint("Opens AI search functionality") + .ecosiaThemed(windowUUID, $theme) + } +} + +#if DEBUG +// MARK: - Preview +@available(iOS 16.0, *) +struct EcosiaAISearchButton_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: .ecosia.space._l) { + + EcosiaAISearchButton( + windowUUID: .XCTestDefaultUUID, + onTap: {} + ) + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButtonTheme.swift b/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButtonTheme.swift new file mode 100644 index 0000000000000..a4a00c1018405 --- /dev/null +++ b/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButtonTheme.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +public struct EcosiaAISearchButtonTheme: EcosiaThemeable { + public var backgroundColor = Color.gray.opacity(0.2) + public var iconColor = Color.primary + + public init() {} + + public mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + iconColor = Color(theme.colors.ecosia.textPrimary) + } +} diff --git a/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAccountNavButton.swift b/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAccountNavButton.swift new file mode 100644 index 0000000000000..eeaab6a1899e0 --- /dev/null +++ b/firefox-ios/Ecosia/UI/NTP/Header/EcosiaAccountNavButton.swift @@ -0,0 +1,131 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A complete account navigation button component that combines seed view and avatar +@available(iOS 16.0, *) +public struct EcosiaAccountNavButton: View { + private let seedCount: Int + private let avatarURL: URL? + private let onTap: () -> Void + private let enableAnimation: Bool + private let showSeedSparkles: Bool + private var showSeedCoundLock: Bool { + !authStateProvider.isLoggedIn && + seedCount == UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers + } + private let windowUUID: WindowUUID + @State private var theme = EcosiaAccountNavButtonTheme() + @ObservedObject private var authStateProvider = EcosiaAuthUIStateProvider.shared + + public init( + seedCount: Int, + avatarURL: URL? = nil, + enableAnimation: Bool = true, + showSeedSparkles: Bool = false, + windowUUID: WindowUUID, + onTap: @escaping () -> Void + ) { + self.seedCount = seedCount + self.avatarURL = avatarURL + self.enableAnimation = enableAnimation + self.showSeedSparkles = showSeedSparkles + self.windowUUID = windowUUID + self.onTap = onTap + } + + private var accessibilityLabel: String { + let seedCountLabel = String(format: .localized(.seedCountAccessibilityLabel), seedCount) + return String(format: .localized(.accountButtonAccessibilityLabel), seedCountLabel) + } + + public var body: some View { + Button(action: onTap) { + HStack(spacing: .ecosia.space._1s) { + if !authStateProvider.hasRegisterVisitError { + EcosiaSeedView( + seedCount: seedCount, + seedIconSize: .ecosia.space._1l, + spacing: .ecosia.space._2s, + enableAnimation: enableAnimation, + showSparkles: showSeedSparkles, + showLock: showSeedCoundLock, + windowUUID: windowUUID + ) + } + + EcosiaAvatar( + avatarURL: avatarURL, + size: .ecosia.space._2l + ) + } + .padding(.top, .ecosia.space._2s) + .padding(.bottom, .ecosia.space._2s) + .padding(.leading, authStateProvider.hasRegisterVisitError ? .ecosia.space._2s : .ecosia.space._1s) + .padding(.trailing, .ecosia.space._2s) + .frame(minHeight: .ecosia.space._3l, maxHeight: .ecosia.space._3l) + .background( + theme.backgroundColor + .cornerRadius(.ecosia.borderRadius._1l) + ) + .animation(.easeInOut(duration: 0.3), value: authStateProvider.hasRegisterVisitError) + } + .buttonStyle(PlainButtonStyle()) + .accessibilityElement(children: .combine) + .accessibilityLabel(accessibilityLabel) + .accessibilityHint(String.localized(.accountButtonAccessibilityHint)) + .accessibilityIdentifier("account_nav_button") + .accessibilityAddTraits(.isButton) + .ecosiaThemed(windowUUID, $theme) + } +} + +// MARK: - Theme +struct EcosiaAccountNavButtonTheme: EcosiaThemeable { + var backgroundColor = Color.gray.opacity(0.2) + + mutating func applyTheme(theme: Theme) { + backgroundColor = Color(theme.colors.ecosia.backgroundElevation1) + } +} + +#if DEBUG +// MARK: - Preview +@available(iOS 16.0, *) +struct EcosiaAccountNavButton_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: .ecosia.space._l) { + EcosiaAccountNavButton( + seedCount: 1, + windowUUID: .XCTestDefaultUUID, + onTap: {} + ) + + EcosiaAccountNavButton( + seedCount: 42, + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + windowUUID: .XCTestDefaultUUID, + onTap: {} + ) + + EcosiaAccountNavButton( + seedCount: 999, + windowUUID: .XCTestDefaultUUID, + onTap: {} + ) + + EcosiaAccountNavButton( + seedCount: 25, + avatarURL: URL(string: "https://avatars.githubusercontent.com/u/1?v=4"), + windowUUID: .XCTestDefaultUUID, + onTap: {} + ) + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/ProductTour/FadeTransitionDelegate.swift b/firefox-ios/Ecosia/UI/ProductTour/FadeTransitionDelegate.swift new file mode 100644 index 0000000000000..8839c8aab594b --- /dev/null +++ b/firefox-ios/Ecosia/UI/ProductTour/FadeTransitionDelegate.swift @@ -0,0 +1,75 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +/// A custom transition delegate that provides a fade animation for modal presentation and dismissal +public final class FadeTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate { + + /// The background color to apply to the underlying view when dismissing. Defaults to nil (no change). + public var dismissalBackgroundColor: UIColor? + + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return FadeAnimator(isPresenting: false, dismissalBackgroundColor: dismissalBackgroundColor) + } + + public func animationController(forPresented presented: UIViewController, + presenting: UIViewController, + source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return FadeAnimator(isPresenting: true, dismissalBackgroundColor: nil) + } +} + +private final class FadeAnimator: NSObject, UIViewControllerAnimatedTransitioning { + private let isPresenting: Bool + private let dismissalBackgroundColor: UIColor? + + init(isPresenting: Bool, dismissalBackgroundColor: UIColor?) { + self.isPresenting = isPresenting + self.dismissalBackgroundColor = dismissalBackgroundColor + super.init() + } + + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return 0.2 + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard let toView = transitionContext.view(forKey: .to), + let fromView = transitionContext.view(forKey: .from) else { + transitionContext.completeTransition(false) + return + } + + let containerView = transitionContext.containerView + let duration = transitionDuration(using: transitionContext) + + if isPresenting { + containerView.addSubview(toView) + toView.alpha = 0 + + UIView.animate(withDuration: duration, + animations: { + toView.alpha = 1 + }, completion: { finished in + transitionContext.completeTransition(finished) + }) + } else { + containerView.insertSubview(toView, at: 0) + toView.alpha = 1 + + if let dismissalBackgroundColor = dismissalBackgroundColor { + toView.backgroundColor = dismissalBackgroundColor + } + + UIView.animate(withDuration: duration, + animations: { + fromView.alpha = 0 + }, completion: { finished in + fromView.removeFromSuperview() + transitionContext.completeTransition(finished) + }) + } + } +} diff --git a/firefox-ios/Ecosia/UI/ProductTour/ProductTourManager.swift b/firefox-ios/Ecosia/UI/ProductTour/ProductTourManager.swift new file mode 100644 index 0000000000000..28f2eed8bda67 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ProductTour/ProductTourManager.swift @@ -0,0 +1,273 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import Foundation + +/// Events broadcast to observers when the product tour progresses +public enum ProductTourEvent { + /// The tour has started; the homepage should show first-search content + case tourStarted + /// The user completed their first search; show the search spotlight + case searchCompleted + /// The entire search track (first search + spotlight) is done; move to the external website track + case searchTrackCompleted + /// The user visited an external website; show the external website spotlight + case externalWebsiteVisited + /// All milestones are done; the tour is finished + case tourCompleted + /// The sign-in flow has started; observers should hold off on tour-related UI changes + case signInFlowStarted + /// The sign-in flow has ended; observers can resume normal tour behaviour + case signInFlowEnded +} + +/// Tracks which product tour milestones have been completed +public struct ProductTourMilestones: OptionSet, Equatable { + public let rawValue: Int + public init(rawValue: Int) { self.rawValue = rawValue } + + /// The user has performed their first search (pre-spotlight) + public static let firstSearchDone = ProductTourMilestones(rawValue: 1 << 0) + /// The search spotlight flow has been completed/skipped/dismissed + public static let searchSpotlightDone = ProductTourMilestones(rawValue: 1 << 1) + /// The user has visited an external website (pre-spotlight) + public static let externalWebsiteVisitDone = ProductTourMilestones(rawValue: 1 << 2) + /// The external website spotlight has been completed/dismissed + public static let externalWebsiteSpotlightDone = ProductTourMilestones(rawValue: 1 << 3) + + /// All milestones required to finish the tour + public static let all: ProductTourMilestones = [ + .firstSearchDone, + .searchSpotlightDone, + .externalWebsiteVisitDone, + .externalWebsiteSpotlightDone + ] +} + +/// Protocol for observing product tour events +public protocol ProductTourObserver: AnyObject { + func productTour(didReceiveEvent event: ProductTourEvent) +} + +/// Manager responsible for controlling the product tour across the app. +/// +/// The tour has two independent tracks that can happen in any order: +/// 1. **Search track**: first search → search spotlight +/// 2. **External website track**: first external website visit → external website spotlight +/// +/// The tour completes automatically once both tracks are finished. +/// +/// The manager automatically listens to authentication state changes. When a user logs in, +/// the ``AccountOrigin`` determines which tour variant to start (new account vs. existing account). +public final class ProductTourManager { + public static let shared = ProductTourManager() + private static let milestonesKey = "ProductTourMilestones" + + private let userDefaults: UserDefaults + private let authManager: EcosiaBrowserWindowAuthManager + private let isExperimentEnabled: () -> Bool + + // Observers for event notifications + private var observers: [WeakReference] = [] + + /// When `true`, the sign-in flow is in progress and the first-search + /// card should be hidden until the flow resolves. + public private(set) var isSignInFlowActive: Bool = false + + /// Tracks which independent milestones have been completed + public private(set) var completedMilestones: ProductTourMilestones { + didSet { + guard oldValue != completedMilestones else { return } + saveMilestones() + } + } + + public init(userDefaults: UserDefaults = .standard, + authManager: EcosiaBrowserWindowAuthManager = .shared, + isExperimentEnabled: @escaping () -> Bool = { OnboardingProductTourExperiment.isEnabled }) { + self.userDefaults = userDefaults + self.authManager = authManager + self.isExperimentEnabled = isExperimentEnabled + self.completedMilestones = Self.loadMilestones(from: userDefaults, isExperimentEnabled: isExperimentEnabled) + authManager.subscribe(observer: self, selector: #selector(handleAuthStateChanged(_:))) + } + + deinit { + authManager.unsubscribe(observer: self) + } + + // MARK: - Auth State Observation + + @objc private func handleAuthStateChanged(_ notification: Notification) { + guard let actionType = notification.userInfo?["actionType"] as? EcosiaAuthActionType, + let authState = notification.userInfo?["authState"] as? AuthWindowState else { + return + } + + // End the sign-in suspension whenever auth resolves if still active + if isSignInFlowActive { + signInFlowDidEnd() + } + + if case .userLoggedIn = actionType, isInProductTour { + applyAccountOriginToTour(authState.accountOrigin) + } + } + + /// Adjusts milestones based on account origin. + /// + /// - New account / no login: full tour (first-search → search spotlight → external website → external website spotlight) + /// - Existing account: skip the search track entirely, only the external website track remains + private func applyAccountOriginToTour(_ accountOrigin: AccountOrigin?) { + guard accountOrigin == .existingAccount else { return } + + // Existing users skip the first-search homepage and search spotlight + completedMilestones.insert(.firstSearchDone) + completedMilestones.insert(.searchSpotlightDone) + notifyObservers(event: .searchTrackCompleted) + completeTourIfAllMilestonesCompleted() + } + + // MARK: - Public API + + /// Register an observer to receive event notifications + public func addObserver(_ observer: ProductTourObserver) { + observers.removeAll { $0.value == nil } + if !observers.contains(where: { $0.value === observer }) { + observers.append(WeakReference(observer)) + } + } + + /// Remove an observer from receiving notifications + public func removeObserver(_ observer: ProductTourObserver) { + observers.removeAll { $0.value === observer } + } + + /// Whether the user is currently in the product tour + public var isInProductTour: Bool { + guard isExperimentEnabled() else { return false } + return !completedMilestones.contains(.all) + } + + /// Whether the homepage should show product tour first-search content. + /// True when the tour is active and the user hasn't searched yet. + public var shouldShowProductTourHomepage: Bool { + return isInProductTour && !completedMilestones.contains(.firstSearchDone) + } + + /// Whether the search spotlight should be shown. + /// True when the first search is done but the spotlight hasn't been shown yet. + public var shouldShowSearchSpotlight: Bool { + return completedMilestones.contains(.firstSearchDone) + && !completedMilestones.contains(.searchSpotlightDone) + } + + /// Whether the external website spotlight should be shown. + /// True when an external website was visited but the spotlight hasn't been shown yet. + public var shouldShowExternalWebsiteSpotlight: Bool { + return completedMilestones.contains(.externalWebsiteVisitDone) + && !completedMilestones.contains(.externalWebsiteSpotlightDone) + } + + // MARK: - Milestone Completion + + /// Call when the user performs their first search on Ecosia. + public func completeFirstSearchIfNeeded() { + guard isInProductTour, + !completedMilestones.contains(.firstSearchDone) else { return } + completedMilestones.insert(.firstSearchDone) + notifyObservers(event: .searchCompleted) + } + + /// Call when the user navigates to an external (non-Ecosia) website. + public func completeExternalWebsiteVisitIfNeeded() { + guard isInProductTour, + !completedMilestones.contains(.externalWebsiteVisitDone) else { return } + completedMilestones.insert(.externalWebsiteVisitDone) + notifyObservers(event: .externalWebsiteVisited) + } + + /// Call when the search spotlight is completed, skipped, or dismissed. + public func completeSearchSpotlight() { + guard !completedMilestones.contains(.searchSpotlightDone) else { return } + completedMilestones.insert(.searchSpotlightDone) + completeTourIfAllMilestonesCompleted() + } + + /// Call when the external website spotlight is completed or dismissed. + public func completeExternalWebsiteSpotlight() { + guard !completedMilestones.contains(.externalWebsiteSpotlightDone) else { return } + completedMilestones.insert(.externalWebsiteSpotlightDone) + completeTourIfAllMilestonesCompleted() + } + + /// Force-complete the entire product tour. + public func completeTour() { + guard isInProductTour else { return } + completedMilestones = .all + notifyObservers(event: .tourCompleted) + } + + // MARK: - Sign-In Flow Suspension + + /// Call when the user starts the sign-in flow from the Welcome screen. + public func signInFlowDidStart() { + guard !isSignInFlowActive else { return } + isSignInFlowActive = true + notifyObservers(event: .signInFlowStarted) + } + + /// Call when the sign-in flow resolves. + public func signInFlowDidEnd() { + guard isSignInFlowActive else { return } + isSignInFlowActive = false + notifyObservers(event: .signInFlowEnded) + } + + /// Reset the tour state (useful for testing or re-onboarding). + public func resetTour() { + completedMilestones = [] + notifyObservers(event: .tourStarted) + } + + // MARK: - Private Methods + + private func completeTourIfAllMilestonesCompleted() { + if completedMilestones.contains(.all) { + notifyObservers(event: .tourCompleted) + } + } + + private static func loadMilestones(from userDefaults: UserDefaults, + isExperimentEnabled: () -> Bool) -> ProductTourMilestones { + guard isExperimentEnabled() else { return .all } + let rawValue = userDefaults.integer(forKey: milestonesKey) + return ProductTourMilestones(rawValue: rawValue) + } + + private func saveMilestones() { + if isExperimentEnabled() { + userDefaults.set(completedMilestones.rawValue, forKey: Self.milestonesKey) + } + } + + private func notifyObservers(event: ProductTourEvent) { + observers.removeAll { $0.value == nil } + observers.forEach { weakObserver in + if let observer = weakObserver.value as? ProductTourObserver { + observer.productTour(didReceiveEvent: event) + } + } + } +} + +/// Weak reference wrapper to avoid retain cycles with observers +private final class WeakReference { + weak var value: AnyObject? + + init(_ value: AnyObject) { + self.value = value + } +} diff --git a/firefox-ios/Ecosia/UI/ProductTour/WelcomeNavigation.swift b/firefox-ios/Ecosia/UI/ProductTour/WelcomeNavigation.swift new file mode 100644 index 0000000000000..9022c19e7b0c5 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ProductTour/WelcomeNavigation.swift @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common + +public final class WelcomeNavigation: UINavigationController { + private let fadeTransitionDelegate: FadeTransitionDelegate + let windowUUID: WindowUUID + + public init(rootViewController: UIViewController, windowUUID: WindowUUID) { + self.windowUUID = windowUUID + + // Transition delegate for fade dismissal + let transition = FadeTransitionDelegate() + let themeManager: ThemeManager = AppContainer.shared.resolve() + let theme = themeManager.getCurrentTheme(for: windowUUID) + // Matches NTP background + transition.dismissalBackgroundColor = theme.colors.ecosia.backgroundPrimaryDecorative + self.fadeTransitionDelegate = transition + + super.init(rootViewController: rootViewController) + transitioningDelegate = fadeTransitionDelegate + } + required init?(coder: NSCoder) { nil } + + override public var supportedInterfaceOrientations: UIInterfaceOrientationMask { + topViewController is WelcomeViewController ? .portrait : .all + } +} diff --git a/firefox-ios/Ecosia/UI/ProductTour/WelcomeVideoPlayer.swift b/firefox-ios/Ecosia/UI/ProductTour/WelcomeVideoPlayer.swift new file mode 100644 index 0000000000000..13659e6f17648 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ProductTour/WelcomeVideoPlayer.swift @@ -0,0 +1,126 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import AVKit +import Combine +import SwiftUI + +class VideoPlayerView: UIView { + let playerLayer = AVPlayerLayer() + + override class var layerClass: AnyClass { + return AVPlayerLayer.self + } + + override func layoutSubviews() { + super.layoutSubviews() + if let playerLayer = layer as? AVPlayerLayer { + playerLayer.videoGravity = .resizeAspectFill + playerLayer.frame = bounds + } + } +} + +struct WelcomeVideoPlayer: UIViewRepresentable { + let videoName: String + var onReady: (() -> Void)? + var shouldPlay: Bool = false + + func makeUIView(context: Context) -> UIView { + let view = VideoPlayerView() + + guard let videoURL = Bundle.ecosia.url(forResource: videoName, withExtension: "mp4") else { + // Fallback to static image if video not found + // TODO: Change fallback image + let imageView = UIImageView(image: .init(named: "forest", in: .ecosia, with: nil)) + imageView.contentMode = .scaleAspectFill + imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(imageView) + // Trigger onReady immediately for fallback + DispatchQueue.main.async { + onReady?() + } + return view + } + + let playerItem = AVPlayerItem(url: videoURL) + let player = AVPlayer(playerItem: playerItem) + + if let playerLayer = view.layer as? AVPlayerLayer { + playerLayer.player = player + playerLayer.videoGravity = .resizeAspectFill + } + + // Store references in coordinator + context.coordinator.player = player + context.coordinator.view = view + context.coordinator.onReady = onReady + + // Monitor player status + context.coordinator.statusObserver = playerItem.publisher(for: \.status) + .receive(on: DispatchQueue.main) + .sink { status in + switch status { + case .readyToPlay: + if shouldPlay { + player.play() + } + context.coordinator.onReady?() + case .failed: + // Call onReady even on failure to prevent animations from never starting + context.coordinator.onReady?() + case .unknown: + break + @unknown default: + break + } + } + + // Monitor buffer status + let bufferObserver = playerItem.publisher(for: \.isPlaybackBufferFull) + .receive(on: DispatchQueue.main) + .sink { _ in + // Buffer monitoring + } + + context.coordinator.bufferObserver = bufferObserver + + return view + } + + func updateUIView(_ uiView: UIView, context: Context) { + // Handle play/pause based on shouldPlay state + if shouldPlay && context.coordinator.player?.timeControlStatus != .playing { + context.coordinator.startPlayback() + } else if !shouldPlay && context.coordinator.player?.timeControlStatus == .playing { + context.coordinator.pausePlayback() + } + } + + func makeCoordinator() -> Coordinator { + Coordinator() + } + + class Coordinator { + var player: AVPlayer? + var view: UIView? + var statusObserver: AnyCancellable? + var bufferObserver: AnyCancellable? + var onReady: (() -> Void)? + + func startPlayback() { + player?.play() + } + + func pausePlayback() { + player?.pause() + } + + deinit { + player?.pause() + statusObserver?.cancel() + bufferObserver?.cancel() + } + } +} diff --git a/firefox-ios/Ecosia/UI/ProductTour/WelcomeView.swift b/firefox-ios/Ecosia/UI/ProductTour/WelcomeView.swift new file mode 100644 index 0000000000000..ce9b1e14e95b5 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ProductTour/WelcomeView.swift @@ -0,0 +1,507 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +public struct WelcomeView: View { + + // MARK: - UX Constants + private struct UX { + static let logoWidth: CGFloat = 112 + static let logoHeight: CGFloat = 28 + static let logoContainerSpacing: CGFloat = 12 + static let welcomeTextFontSize: CGFloat = 17 + static let logoTopOffsetIPhone: CGFloat = 46 + static let logoTopOffsetIPad: CGFloat = 66 + static let maskInitialHeight: CGFloat = 384 + static let maskInitialWidthMargin: CGFloat = 8 + static let maskCornerRadius: CGFloat = 16 + static let contentMaxWidthIPad: CGFloat = 479 + static let contentPadding: CGFloat = 16 + static let bodyTitleBottomSpacing: CGFloat = 20 + static let bodySubtitleBottomSpacing: CGFloat = 36 + static let buttonHeight: CGFloat = 48 + static let buttonCornerRadius: CGFloat = 24 + static let exitOffset: CGFloat = 50 + + // Gradient dimensions + static let topGradientBottomOffset: CGFloat = 19 + static let bodyGradientTopOffset: CGFloat = 20 + static let bodyGradientBottomOffset: CGFloat = 24 + static let bottomGradientTopOffset: CGFloat = 20 + + // Animation timings (relative delays between phases) + static let initialDelay: TimeInterval = 0.5 + static let phase1Duration: TimeInterval = 0.35 + static let phase2Duration: TimeInterval = 0.35 + static let phase3Delay: TimeInterval = 0.5 + static let phase3Duration: TimeInterval = 0.5 + static let exitDuration: TimeInterval = 0.35 + } + + // MARK: - State + + @State private var animationPhase: AnimationPhase = .initial + @State private var transitionMaskScale: CGFloat = 0.0 + @State private var transitionMaskHeight: CGFloat = UX.maskInitialHeight + @State private var transitionMaskWidth: CGFloat = 0.0 + @State private var welcomeTextOpacity: Double = 0.0 + @State private var logoOpacity: Double = 1.0 + @State private var logoColor = Color(uiColor: UIColor.systemBackground) // Will be brandPrimary + @State private var logoOffset: CGFloat = 0.0 + @State private var welcomeTextOffset: CGFloat = 0.0 + @State private var bodyOpacity: Double = 0.0 + @State private var bodyOffset: CGFloat = 0.0 + @State private var showVideoBackground: Bool = false + @State private var shouldPlayVideo: Bool = false + @State private var backgroundOpacity: Double = 1.0 + @State private var topGradientOpacity: Double = 0.0 + @State private var bodyGradientOpacity: Double = 0.0 + @State private var bottomGradientOpacity: Double = 0.0 + @State private var theme = WelcomeViewTheme() + @State private var isVideoReady = false + @State private var animationTask: Task? + @State private var hasAppeared = false + + private let reduceMotionEnabled = UIAccessibility.isReduceMotionEnabled + + let windowUUID: WindowUUID + let onFinish: () -> Void + let onSignIn: () -> Void + + public init(windowUUID: WindowUUID, + onFinish: @escaping () -> Void, + onSignIn: @escaping () -> Void) { + self.windowUUID = windowUUID + self.onFinish = onFinish + self.onSignIn = onSignIn + } + + enum AnimationPhase { + case initial + case phase1Complete + case phase2Complete + case phase3Complete + case final + } + + public var body: some View { + ZStack(alignment: .center) { + // Matching Launch Screen background + Color(.systemBackground) + .ignoresSafeArea() + .opacity(backgroundOpacity) + + // Video background (clipped to transition mask) + WelcomeVideoPlayer( + videoName: "welcome_background", + onReady: { + isVideoReady = true + }, + shouldPlay: shouldPlayVideo + ) + .mask( + RoundedRectangle(cornerRadius: UX.maskCornerRadius) + .frame(height: transitionMaskHeight) + .frame(maxWidth: transitionMaskWidth) + .scaleEffect(transitionMaskScale, anchor: .center) + ) + .ignoresSafeArea(edges: .all) + .opacity(showVideoBackground ? 1 : 0) + + // Top vertical gradient behind logo + if animationPhase == .phase3Complete { + VStack(spacing: 0) { + LinearGradient( + gradient: Gradient(colors: [ + Color.black.opacity(0.38), + Color.black.opacity(0) + ]), + startPoint: .top, + endPoint: .bottom + ) + .frame(height: topGradientHeight) + + Spacer() + } + .ignoresSafeArea() + .opacity(topGradientOpacity) + } + + // Bottom vertical gradient behind buttons + if animationPhase == .phase3Complete { + VStack(spacing: 0) { + Spacer() + + LinearGradient( + gradient: Gradient(colors: [ + Color.black.opacity(0), + Color.black.opacity(0.32) + ]), + startPoint: .top, + endPoint: .bottom + ) + .frame(height: bottomGradientHeight) + } + .ignoresSafeArea() + .opacity(bottomGradientOpacity) + } + + // Welcome text + Text(verbatim: .localized(.welcomeTo)) + .font(.system(size: UX.welcomeTextFontSize, weight: .semibold)) + .foregroundColor(theme.contentTextColor) + .multilineTextAlignment(.center) + .opacity(welcomeTextOpacity) + .offset(y: welcomeTextOffset) + .frame(maxWidth: transitionMaskWidth) + + // Logo + Image("ecosiaLogoLaunch", bundle: .main) + .renderingMode(.template) + .resizable() + .aspectRatio(contentMode: .fit) + .foregroundColor(logoColor) + .frame(width: UX.logoWidth, height: UX.logoHeight) + .opacity(logoOpacity) + .offset(y: logoOffset) + .frame(maxWidth: transitionMaskWidth) + .accessibilityLabel(String.localized(.ecosiaLogoAccessibilityLabel)) + .accessibilityIdentifier(EcosiaAccessibilityIdentifiers.logo) + + // Content + if animationPhase == .phase3Complete { + VStack { + Spacer() + + VStack(spacing: UX.bodyTitleBottomSpacing) { + Text(verbatim: .localized(.realChangeAtYourFingertips)) + .font(.ecosiaFamilyBrand(size: .ecosia.font._6l)) + .foregroundStyle(theme.contentTextColor) + .multilineTextAlignment(.center) + + Text(verbatim: .localized(.joinMillionsPeople)) + .font(.system(size: 17, weight: .semibold)) + .foregroundStyle(theme.contentTextColor) + .multilineTextAlignment(.center) + } + .padding(.top, UX.bodyGradientTopOffset) + .padding(.bottom, UX.bodySubtitleBottomSpacing) + .background( + LinearGradient( + gradient: Gradient(stops: [ + .init(color: Color.black.opacity(0), location: 0.0), + .init(color: Color.black.opacity(0.35), location: 0.3), + .init(color: Color.black.opacity(0.24), location: 0.6), + .init(color: Color.black.opacity(0), location: 1.0) + ]), + startPoint: .top, + endPoint: .bottom + ) + .frame(width: screenWidth) + .opacity(bodyGradientOpacity) + ) + + // Buttons + VStack { + // Sign in button (primary style with icon) + Button(action: { + Analytics.shared.introWelcome(action: .click, property: .signIn) + startExitAnimation(skipFinish: true) { + onSignIn() + } + }) { + HStack(spacing: 8) { + Image("sign-in", bundle: .ecosia) + .renderingMode(.template) + .foregroundColor(theme.buttonTextColor) + .accessibilityHidden(true) + Text(verbatim: .localized(.signIn)) + } + .font(.body) + .foregroundColor(theme.buttonTextColor) + .frame(maxWidth: .infinity) + .frame(height: UX.buttonHeight) + .background(theme.buttonBackgroundColor) + .cornerRadius(UX.buttonCornerRadius) + } + + // Maybe later button (outlined style) + Button(action: { + Analytics.shared.introWelcome(action: .click, property: .maybeLater) + startExitAnimation() + }) { + Text(verbatim: .localized(.maybeLater)) + .font(.body) + .foregroundColor(theme.outlinedButtonTextColor) + .frame(maxWidth: .infinity) + .frame(height: UX.buttonHeight) + .background(Color.clear) + .overlay( + RoundedRectangle(cornerRadius: UX.buttonCornerRadius) + .stroke(theme.outlinedButtonBorderColor, lineWidth: 1.5) + ) + .cornerRadius(UX.buttonCornerRadius) + } + } + .padding(.top, UX.bottomGradientTopOffset) + } + .padding(.horizontal, UX.contentPadding) + .padding(.top, UX.contentPadding) + .padding(.bottom, UX.contentPadding + safeAreaBottom) + .frame(maxWidth: contentMaxWidth) + .opacity(bodyOpacity) + .offset(y: bodyOffset) + } + } + // Needed so initial state matches launch screen + .ignoresSafeArea() + // Theme has to be applied before onAppear for logo color + .ecosiaThemed(windowUUID, $theme) + .onAppear { + guard !hasAppeared else { return } + hasAppeared = true + + Analytics.shared.introWelcome(action: .display) + + logoColor = theme.brandPrimaryColor + + if reduceMotionEnabled { + skipToFinalState() + } else { + // Animation will start when video is ready + } + } + .onChange(of: isVideoReady) { ready in + if ready && !reduceMotionEnabled && hasAppeared { + startAnimationSequence() + } + } + } + + private func skipToFinalState() { + // For reduced motion: skip animations and go directly to final state + showVideoBackground = true + shouldPlayVideo = true + transitionMaskScale = 1.0 + transitionMaskHeight = screenHeight + transitionMaskWidth = screenWidth + logoColor = theme.contentTextColor + welcomeTextOpacity = 1.0 + logoOpacity = 1.0 + logoOffset = phase3LogoOffset + welcomeTextOffset = phase3WelcomeTextOffset + bodyOpacity = 1.0 + topGradientOpacity = 1.0 + bodyGradientOpacity = 1.0 + bottomGradientOpacity = 1.0 + backgroundOpacity = 0.0 + animationPhase = .phase3Complete + } + + private func startAnimationSequence() { + animationTask?.cancel() + animationTask = Task { @MainActor in + // Phase 1: Show centered rounded square mask with logo + try? await Task.sleep(duration: UX.initialDelay) + guard !Task.isCancelled else { return } + + showVideoBackground = true + transitionMaskWidth = screenWidth - UX.maskInitialWidthMargin * 2 + + await animate(duration: UX.phase1Duration, animation: .snappy(duration: UX.phase1Duration)) { + transitionMaskScale = 1.0 + logoColor = theme.contentTextColor + } + + guard !Task.isCancelled else { return } + + // Phase 2: Animate in welcome text above, move logo down + await animate(duration: UX.phase2Duration) { + welcomeTextOpacity = 1.0 + welcomeTextOffset = phase2WelcomeTextOffset + logoOffset = phase2LogoOffset + animationPhase = .phase1Complete + } + + guard !Task.isCancelled else { return } + + // Phase 3: Grow window to full screen, move both to final position, show body + try? await Task.sleep(duration: UX.phase3Delay) + guard !Task.isCancelled else { return } + + // Start video playback with a slight delay to overlap with phase 3 + Task { + try? await Task.sleep(duration: UX.phase3Duration * 0.3) // Start video 30% into phase 3 + guard !Task.isCancelled else { return } + shouldPlayVideo = true + } + + await animate(duration: UX.phase3Duration) { + transitionMaskScale = 1.0 // Already at full scale from phase 1 + transitionMaskHeight = screenHeight + transitionMaskWidth = screenWidth + logoOffset = phase3LogoOffset + welcomeTextOffset = phase3WelcomeTextOffset + bodyOpacity = 1.0 + topGradientOpacity = 1.0 + bodyGradientOpacity = 1.0 + bottomGradientOpacity = 1.0 + backgroundOpacity = 0.0 + animationPhase = .phase3Complete + } + } + } + + @MainActor + private func animate(duration: TimeInterval, animation: Animation? = nil, _ updates: @escaping () -> Void) async { + let finalAnimation = animation ?? .easeInOut(duration: duration) + withAnimation(finalAnimation) { + updates() + } + try? await Task.sleep(duration: duration) + } + + private func startExitAnimation(skipFinish: Bool = false, onComplete: (() -> Void)? = nil) { + animationTask?.cancel() + + // Phase 4: Exit transition - move content out while fading + animationTask = Task { @MainActor in + await animate(duration: UX.exitDuration) { + logoOffset = exitLogoOffset + welcomeTextOffset = exitWelcomeTextOffset + logoOpacity = 0.0 + bodyOffset = UX.exitOffset + bodyOpacity = 0.0 + welcomeTextOpacity = 0.0 + backgroundOpacity = 1.0 + animationPhase = .final + } + + guard !Task.isCancelled else { return } + if !skipFinish { + onFinish() + } + onComplete?() + } + } + + private var simplestWayString: String { + .localized(.theSimplestWay) + } +} + +// MARK: - Dynamic Layout Calculations + +extension WelcomeView { + + private var screenHeight: CGFloat { + UIScreen.main.bounds.height + } + + private var screenWidth: CGFloat { + UIScreen.main.bounds.width + } + + private var isIPad: Bool { + UIDevice.current.userInterfaceIdiom == .pad + } + + private var logoTopOffset: CGFloat { + isIPad ? UX.logoTopOffsetIPad : UX.logoTopOffsetIPhone + } + + private var welcomeTextHeight: CGFloat { + let font = UIFont.systemFont(ofSize: UX.welcomeTextFontSize, weight: .semibold) + return font.lineHeight + } + + private var logoContainerHeight: CGFloat { + welcomeTextHeight + UX.logoContainerSpacing + UX.logoHeight + } + + private var safeAreaTop: CGFloat { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first { + return window.safeAreaInsets.top + } + return 0 + } + + private var safeAreaBottom: CGFloat { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first { + return window.safeAreaInsets.bottom + } + return 0 + } + + // Logo moves down to make room for welcome text + private var phase2LogoOffset: CGFloat { + (welcomeTextHeight + UX.logoContainerSpacing) / 2 + } + + // Welcome text appears above the logo, maintaining spacing + private var phase2WelcomeTextOffset: CGFloat { + phase2LogoOffset - (UX.logoHeight / 2) - UX.logoContainerSpacing - (welcomeTextHeight / 2) + } + + // Position welcome text at specified offset from top + private var phase3WelcomeTextOffset: CGFloat { + let distanceFromTop = safeAreaTop + logoTopOffset + (welcomeTextHeight / 2) + return distanceFromTop - (screenHeight / 2) + } + + // Logo positioned below welcome text + private var phase3LogoOffset: CGFloat { + phase3WelcomeTextOffset + (welcomeTextHeight / 2) + UX.logoContainerSpacing + (UX.logoHeight / 2) + } + + // Move further up by the exit offset + private var exitLogoOffset: CGFloat { + phase3LogoOffset - UX.exitOffset + } + + private var exitWelcomeTextOffset: CGFloat { + phase3WelcomeTextOffset - UX.exitOffset + } + + private var contentMaxWidth: CGFloat { + isIPad ? UX.contentMaxWidthIPad : .infinity + } + + // Top gradient extends from top of screen to some points below logo + private var topGradientHeight: CGFloat { + let logoBottomY = screenHeight / 2 + phase3LogoOffset + (UX.logoHeight / 2) + return logoBottomY + UX.topGradientBottomOffset + } + + // Bottom gradient extends from above the buttons down to the bottom of the screen + private var bottomGradientHeight: CGFloat { + // Covers the button area plus padding plus safe area + let buttonsHeight = UX.buttonHeight * 2 + UX.contentPadding // two buttons + spacing + return buttonsHeight + UX.bottomGradientTopOffset + UX.contentPadding + safeAreaBottom + } +} + +// MARK: - WelcomeViewTheme + +public struct WelcomeViewTheme: EcosiaThemeable { + var contentTextColor = Color.white + var buttonTextColor = Color.white + var buttonBackgroundColor = Color.green + var brandPrimaryColor = Color.green + var outlinedButtonTextColor = Color.white + var outlinedButtonBorderColor = Color.white + + public mutating func applyTheme(theme: Theme) { + contentTextColor = Color(theme.colors.ecosia.textStaticLight) + buttonTextColor = Color(theme.colors.ecosia.buttonContentSecondaryStatic) + buttonBackgroundColor = Color(theme.colors.ecosia.buttonBackgroundFeatured) + brandPrimaryColor = Color(theme.colors.ecosia.brandPrimary) + outlinedButtonTextColor = Color(theme.colors.ecosia.textStaticLight) + outlinedButtonBorderColor = Color(theme.colors.ecosia.textStaticLight) + } +} diff --git a/firefox-ios/Ecosia/UI/ProductTour/WelcomeViewController.swift b/firefox-ios/Ecosia/UI/ProductTour/WelcomeViewController.swift new file mode 100644 index 0000000000000..2c8fb67499772 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ProductTour/WelcomeViewController.swift @@ -0,0 +1,61 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import Common +import SwiftUI + +public protocol WelcomeDelegate: AnyObject { + func welcomeDidFinish(_ welcome: WelcomeViewController) + func welcomeDidRequestSignIn(_ welcome: WelcomeViewController) +} + +public final class WelcomeViewController: UIViewController { + private weak var delegate: WelcomeDelegate? + let windowUUID: WindowUUID + + required init?(coder: NSCoder) { nil } + + public init(delegate: WelcomeDelegate, windowUUID: WindowUUID) { + self.delegate = delegate + self.windowUUID = windowUUID + super.init(nibName: nil, bundle: nil) + modalPresentationCapturesStatusBarAppearance = true + definesPresentationContext = true + } + + override public var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + override public func viewDidLoad() { + super.viewDidLoad() + + let swiftUIView = WelcomeView( + windowUUID: windowUUID, + onFinish: { [weak self] in + guard let self = self else { return } + self.delegate?.welcomeDidFinish(self) + }, + onSignIn: { [weak self] in + guard let self = self else { return } + self.delegate?.welcomeDidRequestSignIn(self) + } + ) + + let hostingController = UIHostingController(rootView: swiftUIView) + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + + addChild(hostingController) + view.addSubview(hostingController.view) + hostingController.didMove(toParent: self) + + NSLayoutConstraint.activate([ + hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } +} diff --git a/firefox-ios/Ecosia/UI/ProductTour/welcome_background.mp4 b/firefox-ios/Ecosia/UI/ProductTour/welcome_background.mp4 new file mode 100644 index 0000000000000..1d9cf52bb0dc6 Binary files /dev/null and b/firefox-ios/Ecosia/UI/ProductTour/welcome_background.mp4 differ diff --git a/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/Animations/default_browser_setup_animation.lottie b/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/Animations/default_browser_setup_animation.lottie new file mode 100644 index 0000000000000..d754406fb93c0 Binary files /dev/null and b/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/Animations/default_browser_setup_animation.lottie differ diff --git a/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/DefaultBrowserCoordinator.swift b/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/DefaultBrowserCoordinator.swift new file mode 100644 index 0000000000000..861ff39a57db3 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/DefaultBrowserCoordinator.swift @@ -0,0 +1,118 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Lottie +import Common + +public struct DefaultBrowserCoordinator { + let navigationController: UINavigationController + let style: InstructionStepsViewStyle + + public init(navigationController: UINavigationController, + style: InstructionStepsViewStyle) { + self.navigationController = navigationController + self.style = style + } + + public func showDetailView(from analyticsLabel: Analytics.Label.DefaultBrowser) { + let steps = [ + InstructionStep(text: .defaultBrowserCardDetailInstructionStep1), + InstructionStep(text: .defaultBrowserCardDetailInstructionStep2), + InstructionStep(text: .defaultBrowserCardDetailInstructionStep3) + ] + + let lottieViewYOffset: CGFloat = UIDevice.current.userInterfaceIdiom == .pad ? 40 : 18 + + let view = InstructionStepsView( + title: .defaultBrowserCardDetailTitle, + steps: steps, + buttonTitle: .defaultBrowserCardDetailButton, + onButtonTap: { + Analytics.shared.defaultBrowserSettingsOpenNativeSettingsVia(analyticsLabel) + if let settingsURL = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(settingsURL, options: [:]) + } + }, + style: style + ) { + GeometryReader { geometry in + VStack { + Spacer() + LottieView { + try await DotLottieFile.named("default_browser_setup_animation", bundle: .ecosia) + } + .configuration(LottieConfiguration(renderingEngine: .mainThread)) + .looping() + .offset(y: lottieViewYOffset) + .aspectRatio(contentMode: .fit) + .frame(width: geometry.size.width) + .clipped() + } + } + } + .onAppear { + Analytics.shared.defaultBrowserSettingsShowsDetailViewVia(analyticsLabel) + } + .onDisappear { + Analytics.shared.defaultBrowserSettingsDismissDetailViewVia(analyticsLabel) + } + + let hostingController = UIHostingController(rootView: view) + hostingController.title = .localized(.defaultBrowserSettingTitle) + hostingController.navigationItem.largeTitleDisplayMode = .never + let doneHandler = DetailViewDoneHandler { + self.navigationController.dismiss(animated: true) + } + objc_setAssociatedObject(hostingController, "detailViewDoneHandler", doneHandler, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + hostingController.navigationItem.rightBarButtonItem = UIBarButtonItem( + title: .localized(.done), + style: .done, + target: doneHandler, + action: #selector(DetailViewDoneHandler.handleDone) + ) + navigationController.pushViewController(hostingController, animated: true) + } +} + +extension DefaultBrowserCoordinator { + + public static func makeDefaultCoordinatorAndShowDetailViewFrom(_ navigationController: UINavigationController?, + analyticsLabel: Analytics.Label.DefaultBrowser, + topViewContentBackground: Color, + with theme: Theme) { + + guard let navigationController = navigationController else { return } + + let style = InstructionStepsViewStyle( + backgroundPrimaryColor: Color(theme.colors.ecosia.backgroundSecondary), + topContentBackgroundColor: topViewContentBackground, + stepsBackgroundColor: Color(theme.colors.ecosia.backgroundPrimary), + textPrimaryColor: Color(theme.colors.ecosia.textPrimary), + textSecondaryColor: Color(theme.colors.ecosia.textSecondary), + buttonBackgroundColor: Color(theme.colors.ecosia.buttonBackgroundPrimary), + buttonTextColor: Color(theme.colors.ecosia.textInversePrimary), + stepRowStyle: StepRowStyle( + stepNumberColor: Color(theme.colors.ecosia.textPrimary), + stepNumberBackgroundColor: Color(theme.colors.ecosia.backgroundSecondary), + stepTextColor: Color(theme.colors.ecosia.textPrimary) + ) + ) + + let coordinator = DefaultBrowserCoordinator(navigationController: navigationController, + style: style) + coordinator.showDetailView(from: analyticsLabel) + } +} + +final class DetailViewDoneHandler: NSObject { + let onDone: () -> Void + init(onDone: @escaping () -> Void) { + self.onDone = onDone + } + + @objc func handleDone() { + onDone() + } +} diff --git a/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/DefaultBrowserSettingsNudgeCardHeaderView.swift b/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/DefaultBrowserSettingsNudgeCardHeaderView.swift new file mode 100644 index 0000000000000..6a8eed4e3a25a --- /dev/null +++ b/firefox-ios/Ecosia/UI/Settings/DefaultBrowser/DefaultBrowserSettingsNudgeCardHeaderView.swift @@ -0,0 +1,98 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit +import SwiftUI +import Common + +/// Reusable Nudge Card Header View that can be configured with any view model. +public final class DefaultBrowserSettingsNudgeCardHeaderView: UITableViewHeaderFooterView, ThemeApplicable, ReusableCell { + + // MARK: - Properties + var theme: Theme! + private var hostingController: UIHostingController? + public var onDismiss: (() -> Void)? + public var onTap: (() -> Void)? + + // MARK: - UX Constants + + private enum UX { + static let paddingTop: CGFloat = 24 + } + + // MARK: - Initializer + + override public init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setup + + private func setupHostingControllerForView(_ view: ConfigurableNudgeCardView) { + let controller = UIHostingController(rootView: AnyView( + VStack(spacing: 0) { + view + .padding(.top, UX.paddingTop) + } + )) + controller.view.translatesAutoresizingMaskIntoConstraints = false + controller.view.backgroundColor = .clear + controller.view.isAccessibilityElement = false + contentView.addSubview(controller.view) + + NSLayoutConstraint.activate([ + controller.view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + controller.view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + controller.view.topAnchor.constraint(equalTo: contentView.topAnchor), + controller.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) + ]) + + hostingController = controller + } + + // MARK: - Configuration Method + + /// Configures the Nudge Card Header View using the ViewModel. + public func configure(theme: Theme?) { + self.theme = theme + guard let theme else { return } + let nudgeCardStyle = NudgeCardStyle(backgroundColor: Color(theme.colors.ecosia.backgroundElevation1), + textPrimaryColor: Color(theme.colors.ecosia.textPrimary), + textSecondaryColor: Color(theme.colors.ecosia.textSecondary), + closeButtonTextColor: Color(theme.colors.ecosia.buttonContentSecondary), + actionButtonTextColor: Color(theme.colors.ecosia.buttonBackgroundPrimary)) + let configurableCardViewModel = NudgeCardViewModel(title: .localized(.defaultBrowserCardTitle), + description: .localized(.defaultBrowserCardDescription), + image: .init(named: "default-browser-card-side-image-koto-illustrations", + in: .ecosia, + with: nil), + style: nudgeCardStyle, + layout: .default) + let view = ConfigurableNudgeCardView(viewModel: configurableCardViewModel, + delegate: self) + setupHostingControllerForView(view) + } + + // MARK: - Theming + public func applyTheme(theme: Theme) { + configure(theme: theme) + } +} + +extension DefaultBrowserSettingsNudgeCardHeaderView: ConfigurableNudgeCardActionDelegate { + + public func nudgeCardRequestToPerformAction() {} + + public func nudgeCardRequestToDimiss() { + onDismiss?() + } + + public func nudgeCardTapped() { + onTap?() + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/addToReadingListUpdate.imageset/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/addToReadingListUpdate.imageset/Contents.json new file mode 100644 index 0000000000000..e214375ac26c4 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/addToReadingListUpdate.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "reader_add.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/addToReadingListUpdate.imageset/reader_add.pdf b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/addToReadingListUpdate.imageset/reader_add.pdf new file mode 100644 index 0000000000000..773a78cc3b384 Binary files /dev/null and b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/addToReadingListUpdate.imageset/reader_add.pdf differ diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/atlas.imageset/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/atlas.imageset/Contents.json new file mode 100644 index 0000000000000..7b53f42b4f00b --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/atlas.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "atlas.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/atlas.imageset/atlas.pdf b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/atlas.imageset/atlas.pdf new file mode 100644 index 0000000000000..092a5269a6faf Binary files /dev/null and b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/atlas.imageset/atlas.pdf differ diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/bookmarkAdd.imageset/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/bookmarkAdd.imageset/Contents.json new file mode 100644 index 0000000000000..1d5c3fdab142c --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/bookmarkAdd.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "bookmark-add.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/bookmarkAdd.imageset/bookmark-add.svg b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/bookmarkAdd.imageset/bookmark-add.svg new file mode 100644 index 0000000000000..78eb2e86f3a93 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/bookmarkAdd.imageset/bookmark-add.svg @@ -0,0 +1,4 @@ + + + + diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/chevronRightLarge.imageset/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/chevronRightLarge.imageset/Contents.json new file mode 100644 index 0000000000000..2d99f8c64b19a --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/chevronRightLarge.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "chevronRightLarge.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/chevronRightLarge.imageset/chevronRightLarge.pdf b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/chevronRightLarge.imageset/chevronRightLarge.pdf new file mode 100644 index 0000000000000..f8436978afd98 Binary files /dev/null and b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/chevronRightLarge.imageset/chevronRightLarge.pdf differ diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/load.imageset/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/load.imageset/Contents.json new file mode 100644 index 0000000000000..ad578fca49622 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/load.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "load.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/load.imageset/load.pdf b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/load.imageset/load.pdf new file mode 100644 index 0000000000000..27b3b87f3d9e2 Binary files /dev/null and b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/load.imageset/load.pdf differ diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/searchLarge.imageset/Contents.json b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/searchLarge.imageset/Contents.json new file mode 100644 index 0000000000000..8fb097f22f6c1 --- /dev/null +++ b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/searchLarge.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "searchLarge.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/searchLarge.imageset/searchLarge.pdf b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/searchLarge.imageset/searchLarge.pdf new file mode 100644 index 0000000000000..64c6579ba5d30 Binary files /dev/null and b/firefox-ios/Ecosia/UI/ShareToAssets.xcassets/searchLarge.imageset/searchLarge.pdf differ diff --git a/firefox-ios/Ecosia/UI/TextAnimationModifier.swift b/firefox-ios/Ecosia/UI/TextAnimationModifier.swift new file mode 100644 index 0000000000000..dd4dc79d775b3 --- /dev/null +++ b/firefox-ios/Ecosia/UI/TextAnimationModifier.swift @@ -0,0 +1,47 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI + +/// A view modifier that provides smooth text animations for numeric values +@available(iOS 16.0, *) +public struct TextAnimationModifier: ViewModifier { + let numericValue: Int + let reduceMotionEnabled: Bool + + private struct UX { + static let fallbackAnimationDuration: TimeInterval = 0.3 + } + + public init(numericValue: Int, reduceMotionEnabled: Bool = UIAccessibility.isReduceMotionEnabled) { + self.numericValue = numericValue + self.reduceMotionEnabled = reduceMotionEnabled + } + + public func body(content: Content) -> some View { + if #available(iOS 17.0, *) { + if reduceMotionEnabled { + content + } else { + content + .contentTransition(.numericText(value: Double(numericValue))) + .animation(.default, value: numericValue) + } + } else { + content + .animation( + reduceMotionEnabled ? .none : .easeInOut(duration: UX.fallbackAnimationDuration), + value: numericValue + ) + } + } +} + +@available(iOS 16.0, *) +public extension View { + /// Applies smooth text animation for numeric values + func animatedText(numericValue: Int, reduceMotionEnabled: Bool = UIAccessibility.isReduceMotionEnabled) -> some View { + modifier(TextAnimationModifier(numericValue: numericValue, reduceMotionEnabled: reduceMotionEnabled)) + } +} diff --git a/firefox-ios/Ecosia/UI/ThemeableSwiftUIView.swift b/firefox-ios/Ecosia/UI/ThemeableSwiftUIView.swift new file mode 100644 index 0000000000000..0196ad1201d8b --- /dev/null +++ b/firefox-ios/Ecosia/UI/ThemeableSwiftUIView.swift @@ -0,0 +1,80 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common +// swiftlint:disable orphaned_doc_comment +/// Ecosia SwiftUI Theming Architecture +/// +/// Usage: +/// 1. Create a theme container: +/// ```swift +/// struct MyComponentTheme: EcosiaThemeable { +/// var backgroundColor = Color.white +/// var textColor = Color.black +/// +/// mutating func applyTheme(theme: Theme) { +/// backgroundColor = Color(theme.colors.ecosia.backgroundPrimary) +/// textColor = Color(theme.colors.ecosia.textPrimary) +/// } +/// } +/// ``` +/// +/// 2. Use in your view: +/// ```swift +/// struct MyComponent: View { +/// private let windowUUID: WindowUUID +/// @State private var theme = MyComponentTheme() +/// +/// var body: some View { +/// Text("Hello") +/// .foregroundColor(theme.textColor) +/// .ecosiaThemed(windowUUID, $theme) +/// } +/// } +/// ``` + +// MARK: - EcosiaThemeable Protocol + +/// Protocol for theme containers that can receive theme updates +public protocol EcosiaThemeable { + /// Apply the given theme to update the theme properties + /// - Parameter theme: The theme to apply + mutating func applyTheme(theme: Theme) +} + +// MARK: - Theme Modifier + +/// ViewModifier for applying theme updates automatically +struct ThemeModifier: ViewModifier { + let windowUUID: WindowUUID? + @Binding var theme: T + + func body(content: Content) -> some View { + content + .onAppear { + let themeManager = AppContainer.shared.resolve() as ThemeManager + theme.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + } + .onReceive(NotificationCenter.default.publisher(for: .ThemeDidChange)) { notification in + guard let uuid = notification.windowUUID, uuid == windowUUID else { return } + let themeManager = AppContainer.shared.resolve() as ThemeManager + theme.applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + } + } +} + +// MARK: - View Extension for Theme Handling + +public extension View { + /// Applies automatic theme handling to a view + /// - Parameters: + /// - windowUUID: The window UUID for theme management + /// - theme: A binding to the themeable object + /// - Returns: A view that automatically updates when theme changes + func ecosiaThemed(_ windowUUID: WindowUUID?, _ theme: Binding) -> some View { + modifier(ThemeModifier(windowUUID: windowUUID, theme: theme)) + } +} +// swiftlint:enable orphaned_doc_comment diff --git a/firefox-ios/Ecosia/UI/Toast/EcosiaErrorToast.swift b/firefox-ios/Ecosia/UI/Toast/EcosiaErrorToast.swift new file mode 100644 index 0000000000000..980ad593dd928 --- /dev/null +++ b/firefox-ios/Ecosia/UI/Toast/EcosiaErrorToast.swift @@ -0,0 +1,87 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +/// A SwiftUI error toast wrapper that animates an EcosiaErrorView for temporary display +@available(iOS 16.0, *) +public struct EcosiaErrorToast: View { + private let subtitle: String + private let windowUUID: WindowUUID + private let onDismiss: () -> Void + + @State private var isVisible = false + + private struct UX { + static let toastMinHeight: CGFloat = 56 + static let animationDuration: TimeInterval = 0.5 + static let displayDuration: TimeInterval = 4.5 + } + + public init( + subtitle: String, + windowUUID: WindowUUID, + onDismiss: @escaping () -> Void + ) { + self.subtitle = subtitle + self.windowUUID = windowUUID + self.onDismiss = onDismiss + } + + public var body: some View { + EcosiaErrorView( + subtitle: subtitle, + windowUUID: windowUUID, + onCloseTapped: { + // User tapped close button - start dismissal + dismiss() + } + ) + .frame(minHeight: UX.toastMinHeight) + .padding(.horizontal, .ecosia.space._m) + .offset(y: isVisible ? 0 : UX.toastMinHeight + .ecosia.space._m) + .opacity(isVisible ? 1 : 0) + .onAppear { + // Animate in + withAnimation(.easeOut(duration: UX.animationDuration)) { + isVisible = true + } + + // Auto-dismiss after duration + DispatchQueue.main.asyncAfter(deadline: .now() + UX.displayDuration) { + dismiss() + } + } + } + + private func dismiss() { + withAnimation(.easeIn(duration: UX.animationDuration)) { + isVisible = false + } + + // Call onDismiss after animation completes + DispatchQueue.main.asyncAfter(deadline: .now() + UX.animationDuration) { + onDismiss() + } + } +} + +#if DEBUG +@available(iOS 16.0, *) +struct EcosiaErrorToast_Previews: PreviewProvider { + static var previews: some View { + VStack { + Spacer() + EcosiaErrorToast( + subtitle: "Something went wrong. Please sign in again.", + windowUUID: .XCTestDefaultUUID, + onDismiss: {} + ) + } + .padding() + .previewLayout(.sizeThatFits) + } +} +#endif diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/Border.colorset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/Border.colorset/Contents.json new file mode 100644 index 0000000000000..b5d2ef7d3e23c --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/Border.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xD9", + "green" : "0xDE", + "red" : "0xDE" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x4C", + "green" : "0x4C", + "red" : "0x4C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryBackground.colorset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryBackground.colorset/Contents.json new file mode 100644 index 0000000000000..fe12c363dfaf1 --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1A", + "green" : "0x1A", + "red" : "0x1A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryBrand.colorset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryBrand.colorset/Contents.json new file mode 100644 index 0000000000000..3d6e7a9334360 --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryBrand.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x33", + "green" : "0x33", + "red" : "0x33" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x25", + "green" : "0x25", + "red" : "0x25" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryText.colorset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryText.colorset/Contents.json new file mode 100644 index 0000000000000..4819a78b20c80 --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/PrimaryText.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x33", + "green" : "0x33", + "red" : "0x33" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/TertiaryBackground.colorset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/TertiaryBackground.colorset/Contents.json new file mode 100644 index 0000000000000..b7205d44c599f --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/TertiaryBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "extended-srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xEB", + "green" : "0xF0", + "red" : "0xF0" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "extended-srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x33", + "green" : "0x33", + "red" : "0x33" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/widgetLabelColors.colorset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/widgetLabelColors.colorset/Contents.json new file mode 100644 index 0000000000000..5372f65ff8a67 --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Colors/widgetLabelColors.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.976", + "green" : "0.976", + "red" : "0.976" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Contents.json new file mode 100644 index 0000000000000..73c00596a7fca --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaDelete.imageset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaDelete.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaDelete.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaDelete.imageset/Icon Set.pdf b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaDelete.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..631210bd470cc Binary files /dev/null and b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaDelete.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaSmallPrivateMask.imageset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaSmallPrivateMask.imageset/Contents.json new file mode 100644 index 0000000000000..6980e06ed02cd --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaSmallPrivateMask.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Icon Set.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaSmallPrivateMask.imageset/Icon Set.pdf b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaSmallPrivateMask.imageset/Icon Set.pdf new file mode 100644 index 0000000000000..274638f12c745 Binary files /dev/null and b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/ecosiaSmallPrivateMask.imageset/Icon Set.pdf differ diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Contents.json new file mode 100644 index 0000000000000..2eecbd394b021 --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Icon Set.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Icon Set-1.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Icon Set-1.png b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Icon Set-1.png new file mode 100644 index 0000000000000..2e7f10c7769f7 Binary files /dev/null and b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Icon Set-1.png differ diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Icon Set.png b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Icon Set.png new file mode 100644 index 0000000000000..c88247498ee65 Binary files /dev/null and b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/openEcosia.imageset/Icon Set.png differ diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/tabTrayLarge.imageset/Contents.json b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/tabTrayLarge.imageset/Contents.json new file mode 100644 index 0000000000000..558e2c93c7fac --- /dev/null +++ b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/tabTrayLarge.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "tabTrayLarge.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/tabTrayLarge.imageset/tabTrayLarge.pdf b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/tabTrayLarge.imageset/tabTrayLarge.pdf new file mode 100644 index 0000000000000..964cc8578a231 Binary files /dev/null and b/firefox-ios/Ecosia/UI/WidgetKitAssets.xcassets/tabTrayLarge.imageset/tabTrayLarge.pdf differ diff --git a/firefox-ios/Ecosia/markets.json b/firefox-ios/Ecosia/markets.json new file mode 100644 index 0000000000000..2ecb918b18e3c --- /dev/null +++ b/firefox-ios/Ecosia/markets.json @@ -0,0 +1,260 @@ +[ + { + "value": "es-ar", + "label": "Argentina", + }, + { + "value": "en-au", + "label": "Australia", + }, + { + "value": "de-at", + "label": "Austria", + }, + { + "value": "fr-be", + "label": "Belgium (French)", + "languageInLabel": "true", + }, + { + "value": "nl-be", + "label": "Belgium (Dutch)", + "languageInLabel": "true", + }, + { + "value": "pt-br", + "label": "Brazil", + }, + { + "value": "bg-bg", + "label": "Bulgaria", + }, + { + "value": "en-ca", + "label": "Canada (English)", + "languageInLabel": "true", + }, + { + "value": "fr-ca", + "label": "Canada (French)", + "languageInLabel": "true", + }, + { + "value": "es-cl", + "label": "Chile", + }, + { + "value": "zh-cn", + "label": "China", + }, + { + "value": "es-co", + "label": "Colombia", + }, + { + "value": "hr-hr", + "label": "Croatia", + }, + { + "value": "cz-cz", + "label": "Czech Republic", + }, + { + "value": "da-dk", + "label": "Denmark", + }, + { + "value": "ee-ee", + "label": "Estonia", + }, + { + "value": "fi-fi", + "label": "Finland", + }, + { + "value": "fr-fr", + "label": "France", + }, + { + "value": "de-de", + "label": "Germany", + }, + { + "value": "gr-gr", + "label": "Greece", + }, + { + "value": "zh-hk", + "label": "Hong Kong", + }, + { + "value": "en-in", + "label": "India", + }, + { + "value": "en-id", + "label": "Indonesia", + }, + { + "value": "en-ie", + "label": "Ireland", + }, + { + "value": "it-it", + "label": "Italy", + }, + { + "value": "ja-jp", + "label": "Japan", + }, + { + "value": "lv-lv", + "label": "Latvia", + }, + { + "value": "lt-lt", + "label": "Lithuania", + }, + { + "value": "en-my", + "label": "Malaysia", + }, + { + "value": "es-mx", + "label": "Mexico", + }, + { + "value": "nl-nl", + "label": "Netherlands", + }, + { + "value": "en-nz", + "label": "New Zealand", + }, + { + "value": "nb-no", + "label": "Norway", + }, + { + "value": "es-pe", + "label": "Peru", + }, + { + "value": "en-ph", + "label": "Philippines", + }, + { + "value": "pl-pl", + "label": "Poland", + }, + { + "value": "pt-pt", + "label": "Portugal", + }, + { + "value": "ro-ro", + "label": "Romania", + }, + { + "value": "ru-ru", + "label": "Russia", + }, + { + "value": "ar-sa", + "label": "Saudi Arabia (Arabic)", + "languageInLabel": "true", + }, + { + "value": "en-sa", + "label": "Saudi Arabia (English)", + "languageInLabel": "true", + }, + { + "value": "en-sg", + "label": "Singapore", + }, + { + "value": "sk-sk", + "label": "Slovakia", + }, + { + "value": "en-za", + "label": "South Africa", + }, + { + "value": "ko-kr", + "label": "South Korea", + }, + { + "value": "es-es", + "label": "Spain", + }, + { + "value": "sv-se", + "label": "Sweden", + }, + { + "value": "de-ch", + "label": "Switzerland (German)", + "languageInLabel": "true", + }, + { + "value": "fr-ch", + "label": "Switzerland (French)", + "languageInLabel": "true", + }, + { + "value": "zh-tw", + "label": "Taiwan", + }, + { + "value": "en-th", + "label": "Thailand (English)", + "languageInLabel": "true", + }, + { + "value": "th-th", + "label": "Thailand (Thai)", + "languageInLabel": "true", + }, + { + "value": "tr-tr", + "label": "Turkey", + }, + { + "value": "uk-ua", + "label": "Ukraine", + }, + { + "value": "en-gb", + "label": "United Kingdom", + }, + { + "value": "en-us", + "label": "United States (English)", + "languageInLabel": "true", + }, + { + "value": "es-us", + "label": "United States (Spanish)", + "languageInLabel": "true", + }, + { + "value": "es-ve", + "label": "Venezuela", + }, + { + "value": "en-vn", + "label": "Vietnam (English)", + "languageInLabel": "true", + }, + { + "value": "vi-vn", + "label": "Vietnam (Vietnamese)", + "languageInLabel": "true", + }, + { + "value": "en-ww", + "label": "All regions", + }, +] diff --git a/firefox-ios/EcosiaTests/Account/Auth/AuthNotificationSystemTests.swift b/firefox-ios/EcosiaTests/Account/Auth/AuthNotificationSystemTests.swift new file mode 100644 index 0000000000000..5bea20f7845cb --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Auth/AuthNotificationSystemTests.swift @@ -0,0 +1,416 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Common +@testable import Ecosia + +final class AuthNotificationSystemTests: XCTestCase { + + var authStateManager: EcosiaBrowserWindowAuthManager! + var windowRegistry: EcosiaAuthWindowRegistry! + var testWindowUUID: WindowUUID! + fileprivate var notificationObserver: NotificationObserver! + + override func setUp() { + super.setUp() + authStateManager = EcosiaBrowserWindowAuthManager.shared + windowRegistry = EcosiaAuthWindowRegistry.shared + testWindowUUID = WindowUUID.XCTestDefaultUUID + notificationObserver = NotificationObserver() + + // Register a test window + windowRegistry.registerWindow(testWindowUUID) + } + + override func tearDown() { + // Clean up state after each test + authStateManager.clearAllStates() + windowRegistry.clearAllWindows() + notificationObserver.cleanup() + notificationObserver = nil + authStateManager = nil + windowRegistry = nil + testWindowUUID = nil + super.tearDown() + } + + // MARK: - Notification Names Tests + + func testNotificationNames_haveCorrectValues() { + // Test notification name has correct string value + XCTAssertEqual(Notification.Name.EcosiaAuthStateChanged.rawValue, "EcosiaAuthStateChanged") + } + + // MARK: - EcosiaAuthStateChanged Notification Tests + + func testEcosiaAuthStateChanged_withUserLoggedIn_postsCorrectNotification() { + // Arrange + let action = AuthStateAction( + type: .userLoggedIn, + windowUUID: testWindowUUID, + isLoggedIn: true + ) + + notificationObserver.expectNotification( + name: .EcosiaAuthStateChanged, + object: authStateManager, + expectedCount: 1 + ) + + // Act + authStateManager.dispatch(action: action, for: testWindowUUID) + + // Assert + notificationObserver.waitForExpectations(timeout: 1.0) + + let receivedNotification = notificationObserver.receivedNotifications.first + XCTAssertNotNil(receivedNotification, "Should receive notification") + XCTAssertEqual(receivedNotification?.name, .EcosiaAuthStateChanged, "Should have correct notification name") + XCTAssertTrue(receivedNotification?.object is EcosiaBrowserWindowAuthManager, "Should have correct object") + + // Verify userInfo content + if let userInfo = receivedNotification?.userInfo { + XCTAssertEqual(userInfo["windowUUID"] as? WindowUUID, testWindowUUID, "Should include window UUID") + XCTAssertEqual(userInfo["actionType"] as? EcosiaAuthActionType, .userLoggedIn, "Should include action type") + + if let authState = userInfo["authState"] as? AuthWindowState { + XCTAssertEqual(authState.windowUUID, testWindowUUID, "Auth state should have correct window UUID") + XCTAssertTrue(authState.isLoggedIn, "Auth state should indicate user is logged in") + } else { + XCTFail("Should include auth state in userInfo") + } + } else { + XCTFail("Should include userInfo in notification") + } + } + + func testEcosiaAuthStateChanged_withUserLoggedOut_postsCorrectNotification() { + // Arrange + let action = AuthStateAction( + type: .userLoggedOut, + windowUUID: testWindowUUID, + isLoggedIn: false + ) + + notificationObserver.expectNotification( + name: .EcosiaAuthStateChanged, + object: authStateManager, + expectedCount: 1 + ) + + // Act + authStateManager.dispatch(action: action, for: testWindowUUID) + + // Assert + notificationObserver.waitForExpectations(timeout: 1.0) + + let receivedNotification = notificationObserver.receivedNotifications.first + XCTAssertNotNil(receivedNotification, "Should receive notification") + + // Verify userInfo content + if let userInfo = receivedNotification?.userInfo { + XCTAssertEqual(userInfo["actionType"] as? EcosiaAuthActionType, .userLoggedOut, "Should include action type") + + if let authState = userInfo["authState"] as? AuthWindowState { + XCTAssertFalse(authState.isLoggedIn, "Auth state should indicate user is logged out") + } else { + XCTFail("Should include auth state in userInfo") + } + } else { + XCTFail("Should include userInfo in notification") + } + } + + func testEcosiaAuthStateChanged_withAuthStateLoaded_postsCorrectNotification() { + // Arrange + let action = AuthStateAction( + type: .authStateLoaded, + windowUUID: testWindowUUID, + isLoggedIn: true + ) + + notificationObserver.expectNotification( + name: .EcosiaAuthStateChanged, + object: authStateManager, + expectedCount: 1 + ) + + // Act + authStateManager.dispatch(action: action, for: testWindowUUID) + + // Assert + notificationObserver.waitForExpectations(timeout: 1.0) + + let receivedNotification = notificationObserver.receivedNotifications.first + XCTAssertNotNil(receivedNotification, "Should receive notification") + + // Verify userInfo content + if let userInfo = receivedNotification?.userInfo { + XCTAssertEqual(userInfo["actionType"] as? EcosiaAuthActionType, .authStateLoaded, "Should include action type") + + if let authState = userInfo["authState"] as? AuthWindowState { + XCTAssertTrue(authState.isLoggedIn, "Auth state should indicate user is logged in") + XCTAssertTrue(authState.authStateLoaded, "Auth state should indicate state is loaded") + } else { + XCTFail("Should include auth state in userInfo") + } + } else { + XCTFail("Should include userInfo in notification") + } + } + + // MARK: - Multi-Window Notification Tests + + func testEcosiaAuthStateChanged_withMultipleWindows_postsNotificationForEachWindow() { + // Arrange + let windowUUID2 = WindowUUID() + let windowUUID3 = WindowUUID() + + windowRegistry.registerWindow(windowUUID2) + windowRegistry.registerWindow(windowUUID3) + + notificationObserver.expectNotification( + name: .EcosiaAuthStateChanged, + object: authStateManager, + expectedCount: 3 + ) + + // Act + authStateManager.dispatchAuthState(isLoggedIn: true, actionType: .userLoggedIn) + + // Assert + notificationObserver.waitForExpectations(timeout: 1.0) + + XCTAssertEqual(notificationObserver.receivedNotifications.count, 3, "Should receive 3 notifications") + + // Verify all notifications have correct content + let windowUUIDs = Set([testWindowUUID, windowUUID2, windowUUID3]) + var receivedWindowUUIDs = Set() + + for notification in notificationObserver.receivedNotifications { + XCTAssertEqual(notification.name, .EcosiaAuthStateChanged, "Should have correct notification name") + + if let userInfo = notification.userInfo, + let windowUUID = userInfo["windowUUID"] as? WindowUUID { + receivedWindowUUIDs.insert(windowUUID) + XCTAssertEqual(userInfo["actionType"] as? EcosiaAuthActionType, .userLoggedIn, "Should have correct action type") + } else { + XCTFail("Should include window UUID in userInfo") + } + } + + XCTAssertEqual(receivedWindowUUIDs, windowUUIDs, "Should receive notifications for all windows") + } + + // MARK: - Notification Subscription Tests + + func testSubscribe_withObserver_receivesNotifications() { + // Arrange + let observer = TestNotificationObserver() + let expectation = expectation(description: "Observer should receive notification") + observer.expectation = expectation + + authStateManager.subscribe(observer: observer, selector: #selector(TestNotificationObserver.handleAuthStateChanged(_:))) + + let action = AuthStateAction( + type: .userLoggedIn, + windowUUID: testWindowUUID, + isLoggedIn: true + ) + + // Act + authStateManager.dispatch(action: action, for: testWindowUUID) + + // Assert + waitForExpectations(timeout: 1.0) + XCTAssertEqual(observer.receivedNotifications.count, 1, "Observer should receive one notification") + + if let notification = observer.receivedNotifications.first { + XCTAssertEqual(notification.name, .EcosiaAuthStateChanged, "Should have correct notification name") + XCTAssertTrue(notification.object is EcosiaBrowserWindowAuthManager, "Should have correct object") + } + } + + func testUnsubscribe_withObserver_stopsReceivingNotifications() { + // Arrange + let observer = TestNotificationObserver() + let firstExpectation = expectation(description: "Observer should receive first notification") + observer.expectation = firstExpectation + + authStateManager.subscribe(observer: observer, selector: #selector(TestNotificationObserver.handleAuthStateChanged(_:))) + + let action = AuthStateAction( + type: .userLoggedIn, + windowUUID: testWindowUUID, + isLoggedIn: true + ) + + // Act - First dispatch should trigger notification + authStateManager.dispatch(action: action, for: testWindowUUID) + + // Wait for first notification + waitForExpectations(timeout: 1.0) + + // Unsubscribe + authStateManager.unsubscribe(observer: observer) + + // Clear expectation so second dispatch won't fulfill it + observer.expectation = nil + + // Dispatch another action + let action2 = AuthStateAction( + type: .userLoggedOut, + windowUUID: testWindowUUID, + isLoggedIn: false + ) + authStateManager.dispatch(action: action2, for: testWindowUUID) + + // Assert - Should only have received the first notification + XCTAssertEqual(observer.receivedNotifications.count, 1, "Observer should receive only one notification (before unsubscribe)") + } + + // MARK: - Notification Regression Tests + + func testNotificationDelivery_withManyActions_handlesAllCorrectly() { + // Arrange + let observer = TestNotificationObserver() + let expectation = expectation(description: "All notifications should be delivered") + expectation.expectedFulfillmentCount = 100 + observer.expectation = expectation + + authStateManager.subscribe(observer: observer, selector: #selector(TestNotificationObserver.handleAuthStateChanged(_:))) + + // Act - Dispatch many actions to test system stability + for i in 0..<50 { + let loginAction = AuthStateAction(type: .userLoggedIn, windowUUID: testWindowUUID, isLoggedIn: true) + let logoutAction = AuthStateAction(type: .userLoggedOut, windowUUID: testWindowUUID, isLoggedIn: false) + + authStateManager.dispatch(action: loginAction, for: testWindowUUID) + authStateManager.dispatch(action: logoutAction, for: testWindowUUID) + } + + // Assert + waitForExpectations(timeout: 2.0) + XCTAssertEqual(observer.receivedNotifications.count, 100, "Should receive all 100 notifications") + + // Verify alternating login/logout pattern + for i in 0.. String { + let header = #"{"alg":"RS256","typ":"JWT"}"# + let payloadData = try JSONSerialization.data(withJSONObject: claims) + let payload = try XCTUnwrap(String(data: payloadData, encoding: .utf8)) + + func base64URLEncode(_ string: String) -> String { + Data(string.utf8) + .base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + } + + return "\(base64URLEncode(header)).\(base64URLEncode(payload)).mock-signature" + } +} diff --git a/firefox-ios/EcosiaTests/Account/Auth/AuthWorkflowTests.swift b/firefox-ios/EcosiaTests/Account/Auth/AuthWorkflowTests.swift new file mode 100644 index 0000000000000..b4f8b785eff97 --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Auth/AuthWorkflowTests.swift @@ -0,0 +1,438 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Auth0 +@testable import Ecosia + +final class AuthWorkflowTests: XCTestCase { + + var auth: EcosiaAuthenticationService! + var mockProvider: MockAuth0Provider! + var mockCredentialsManager: MockCredentialsManager! + + override func setUp() { + super.setUp() + mockCredentialsManager = MockCredentialsManager() + mockProvider = MockAuth0Provider() + mockProvider.credentialsManager = mockCredentialsManager + auth = EcosiaAuthenticationService(auth0Provider: mockProvider) + auth.skipUserInfoFetch = true + } + + override func tearDown() { + mockProvider?.reset() + mockCredentialsManager?.reset() + mockProvider = nil + mockCredentialsManager = nil + auth = nil + super.tearDown() + } + + // MARK: - Full Authentication Lifecycle Tests + + func testCompleteAuthenticationLifecycle_loginLogout_worksEndToEnd() async { + // Arrange + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + + XCTAssertFalse(auth.isLoggedIn) + + // Act - Login + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + + // Assert - Logged in state + XCTAssertTrue(auth.isLoggedIn) + XCTAssertEqual(auth.idToken, testCredentials.idToken) + XCTAssertEqual(auth.accessToken, testCredentials.accessToken) + XCTAssertEqual(auth.refreshToken, testCredentials.refreshToken) + XCTAssertEqual(mockProvider.startAuthCallCount, 1) + XCTAssertEqual(mockProvider.storeCredentialsCallCount, 1) + + // Act - Logout + do { + try await auth.logout() + } catch { + XCTFail("Logout should succeed, but failed with: \(error)") + } + + // Assert - Logged out state + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken, "ID token should be cleared") + XCTAssertNil(auth.accessToken, "Access token should be cleared") + XCTAssertNil(auth.refreshToken, "Refresh token should be cleared") + XCTAssertEqual(mockProvider.clearSessionCallCount, 1) + XCTAssertEqual(mockProvider.clearCredentialsCallCount, 1) + } + + func testCompleteAuthenticationLifecycle_loginRenewLogout_worksEndToEnd() async { + // Arrange + let originalCredentials = createTestCredentials() + let renewedCredentials = Credentials( + accessToken: "renewed-access-token", + tokenType: "Bearer", + idToken: "renewed-id-token", + refreshToken: "renewed-refresh-token", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + + mockProvider.mockCredentials = originalCredentials + mockCredentialsManager.storedCredentials = originalCredentials + mockProvider.canRenewCredentialsResult = true + + // Act - Login + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + let originalIdToken = auth.idToken + + // Assert - Logged in + XCTAssertTrue(auth.isLoggedIn) + XCTAssertNotNil(originalIdToken) + + // Act - Renew credentials + mockProvider.mockCredentials = renewedCredentials + mockCredentialsManager.storedCredentials = renewedCredentials + do { + try await auth.renewCredentialsIfNeeded() + } catch { + XCTFail("Renew credentials should succeed, but failed with: \(error)") + } + + // Assert - Credentials renewed + XCTAssertTrue(auth.isLoggedIn) + XCTAssertNotEqual(auth.idToken, originalIdToken) + XCTAssertEqual(auth.idToken, renewedCredentials.idToken) + XCTAssertEqual(mockProvider.canRenewCredentialsCallCount, 1) + XCTAssertEqual(mockProvider.renewCredentialsCallCount, 1) + + // Act - Logout + do { + try await auth.logout() + } catch { + XCTFail("Logout should succeed, but failed with: \(error)") + } + + // Assert - Logged out + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken) + XCTAssertNil(auth.accessToken) + XCTAssertNil(auth.refreshToken) + } + + func testCompleteAuthenticationLifecycle_persistenceAfterRestart_worksEndToEnd() async { + // Arrange + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + + // Act - Login + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + + // Assert - Logged in + XCTAssertTrue(auth.isLoggedIn) + + // Act - Simulate app restart by creating new EcosiaAuthenticationService instance + let newMockProvider = MockAuth0Provider() + newMockProvider.credentialsManager = mockCredentialsManager + newMockProvider.mockCredentials = testCredentials + _ = EcosiaAuthenticationService(auth0Provider: newMockProvider) + + // Allow time for credential retrieval to complete + try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds + + // Assert - Should automatically retrieve stored credentials + XCTAssertEqual(newMockProvider.retrieveCredentialsCallCount, 1) + // Note: The actual state depends on the credential retrieval success + } + + // MARK: - Error Recovery Tests + + func testAuthenticationErrorRecovery_loginFailureRecovery_handlesGracefully() async { + // Arrange + mockProvider.shouldFailAuth = true + + // Act - First login attempt (fails) + do { + try await auth.login() + XCTFail("Expected login to throw but it didn't") + } catch { + // Expected to fail + } + + // Assert - Should remain logged out + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken) + XCTAssertEqual(mockProvider.startAuthCallCount, 1) + XCTAssertEqual(mockProvider.storeCredentialsCallCount, 0) + + // Act - Second login attempt (succeeds) + mockProvider.shouldFailAuth = false + mockProvider.mockCredentials = createTestCredentials() + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + + // Assert - Should now be logged in + XCTAssertTrue(auth.isLoggedIn) + XCTAssertNotNil(auth.idToken) + XCTAssertEqual(mockProvider.startAuthCallCount, 2) + XCTAssertEqual(mockProvider.storeCredentialsCallCount, 1) + } + + func testAuthenticationErrorRecovery_renewFailureHandling_maintainsState() async { + // Arrange + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + + // Login first + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + let originalIdToken = auth.idToken + let originalAccessToken = auth.accessToken + + mockProvider.canRenewCredentialsResult = true + mockProvider.shouldFailRenewCredentials = true + + // Act - Attempt to renew (fails) + do { + try await auth.renewCredentialsIfNeeded() + } catch { + // Expected to potentially fail, but we don't want to fail the test + } + + // Assert - Should maintain original state + XCTAssertTrue(auth.isLoggedIn) + XCTAssertEqual(auth.idToken, originalIdToken) + XCTAssertEqual(auth.accessToken, originalAccessToken) + XCTAssertEqual(mockProvider.canRenewCredentialsCallCount, 1) + XCTAssertEqual(mockProvider.renewCredentialsCallCount, 1) + } + + func testAuthenticationErrorRecovery_logoutFailureHandling_clearsCredentials() async { + // Arrange + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + + // Login first + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + XCTAssertTrue(auth.isLoggedIn) + + mockProvider.shouldFailClearSession = true + + // Act - Logout (session clear fails, but credential clear succeeds) + do { + try await auth.logout() + } catch { + XCTFail("Logout should succeed, but failed with: \(error)") + } + + // Assert - Should still clear credentials despite session clear failure + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken) + XCTAssertNil(auth.accessToken) + XCTAssertNil(auth.refreshToken) + XCTAssertEqual(mockProvider.clearSessionCallCount, 1) + XCTAssertEqual(mockProvider.clearCredentialsCallCount, 1) + } + + // MARK: - Concurrent Operations Tests + + func testConcurrentOperations_multipleLoginAttempts_handledCorrectly() async { + // Arrange + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + + // Act - Multiple concurrent login attempts + await withTaskGroup(of: Void.self) { group in + for _ in 0..<3 { + group.addTask { + do { + try await self.auth.login() + } catch { + // Expected to potentially fail during concurrent operations + } + } + } + } + + // Assert - Should be logged in only once + XCTAssertTrue(auth.isLoggedIn) + XCTAssertNotNil(auth.idToken) + XCTAssertGreaterThanOrEqual(mockProvider.startAuthCallCount, 1) + } + + func testConcurrentOperations_loginAndRenew_handledCorrectly() async { + // Arrange + let testCredentials = createTestCredentials() + let renewedCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + mockProvider.canRenewCredentialsResult = true + + // Act - Login first + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + + // Act - Concurrent login and renew + await withTaskGroup(of: Void.self) { group in + group.addTask { + do { + try await self.auth.login() + } catch { + // Expected to potentially fail during concurrent operations + } + } + group.addTask { + self.mockProvider.mockCredentials = renewedCredentials + do { + try await self.auth.renewCredentialsIfNeeded() + } catch { + // Expected to potentially fail during concurrent operations + } + } + } + + // Assert - Should maintain consistent state + XCTAssertTrue(auth.isLoggedIn) + XCTAssertNotNil(auth.idToken) + XCTAssertNotNil(auth.accessToken) + XCTAssertNotNil(auth.refreshToken) + } + + // MARK: - Edge Cases Tests + + func testEdgeCase_logoutWithoutLogin_handledGracefully() async { + // Arrange + XCTAssertFalse(auth.isLoggedIn) + + // Act - Logout without being logged in + do { + try await auth.logout() + } catch { + XCTFail("Logout should succeed, but failed with: \(error)") + } + + // Assert - Should handle gracefully + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken) + XCTAssertEqual(mockProvider.clearSessionCallCount, 1) + XCTAssertEqual(mockProvider.clearCredentialsCallCount, 1) + } + + func testEdgeCase_renewWithoutLogin_handledGracefully() async { + // Arrange + XCTAssertFalse(auth.isLoggedIn) + mockProvider.canRenewCredentialsResult = false + + // Act - Attempt to renew without being logged in + do { + try await auth.renewCredentialsIfNeeded() + } catch { + // Expected to potentially fail, but we don't want to fail the test + } + + // Assert - Should handle gracefully + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken) + XCTAssertEqual(mockProvider.canRenewCredentialsCallCount, 1) + XCTAssertEqual(mockProvider.renewCredentialsCallCount, 0) + } + + func testEdgeCase_webInitiatedLogout_skipsWebLogout() async { + // Arrange + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + mockCredentialsManager.storedCredentials = testCredentials + + // Login first + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + XCTAssertTrue(auth.isLoggedIn) + + // Act - Web-initiated logout (skip web logout) + do { + try await auth.logout(triggerWebLogout: false) + } catch { + XCTFail("Logout should succeed, but failed with: \(error)") + } + + // Assert - Should clear credentials but not call clearSession + XCTAssertFalse(auth.isLoggedIn) + XCTAssertNil(auth.idToken) + XCTAssertEqual(mockProvider.clearSessionCallCount, 0) + XCTAssertEqual(mockProvider.clearCredentialsCallCount, 1) + } + + // MARK: - Integration with Real Components + + func testIntegrationWithRealCredentialsManager_basicFlow_worksCorrectly() async { + // Arrange + let realCredentialsManager = DefaultCredentialsManager() + mockProvider.credentialsManager = realCredentialsManager + let testCredentials = createTestCredentials() + mockProvider.mockCredentials = testCredentials + + _ = realCredentialsManager.clear() + + // Act - Login + do { + try await auth.login() + } catch { + XCTFail("Login should succeed, but failed with: \(error)") + } + + // Assert - Should use real credentials manager + XCTAssertTrue(auth.isLoggedIn) + XCTAssertNotNil(auth.idToken) + XCTAssertEqual(mockProvider.startAuthCallCount, 1) + XCTAssertEqual(mockProvider.storeCredentialsCallCount, 1) + + // Cleanup + _ = realCredentialsManager.clear() + } + + // MARK: - Helper Methods + + private func createTestCredentials() -> Credentials { + return Credentials( + accessToken: "integration-test-access-token-\(UUID().uuidString)", + tokenType: "Bearer", + idToken: "integration-test-id-token-\(UUID().uuidString)", + refreshToken: "integration-test-refresh-token-\(UUID().uuidString)", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + } +} diff --git a/firefox-ios/EcosiaTests/Account/Auth/DefaultAuth0SettingsProviderTests.swift b/firefox-ios/EcosiaTests/Account/Auth/DefaultAuth0SettingsProviderTests.swift new file mode 100644 index 0000000000000..5028c2c34deaf --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Auth/DefaultAuth0SettingsProviderTests.swift @@ -0,0 +1,41 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class DefaultAuth0SettingsProviderTests: XCTestCase { + + var settingsProvider = DefaultAuth0SettingsProvider() + + // MARK: - Domain Tests + + func testDomain_returnsValueFromURLProvider() { + // Arrange + let expectedDomain = EcosiaEnvironment.current.urlProvider.auth0Domain + + // Act + let domain = settingsProvider.domain + + // Assert + XCTAssertEqual(domain, expectedDomain, "Domain should match the URLProvider auth0Domain") + XCTAssertFalse(domain.isEmpty, "Domain should not be empty") + } + + // MARK: - Cookie Domain Tests + + func testCookieDomain_returnsValueFromURLProvider() { + // Arrange + let expectedCookieDomain = EcosiaEnvironment.current.urlProvider.auth0CookieDomain + + // Act + let cookieDomain = settingsProvider.cookieDomain + + // Assert + XCTAssertEqual(cookieDomain, expectedCookieDomain, "Cookie domain should match the URLProvider auth0CookieDomain") + XCTAssertFalse(cookieDomain.isEmpty, "Cookie domain should not be empty") + } + + // MARK: - Protocol Conformance Tests +} diff --git a/firefox-ios/EcosiaTests/Account/Auth/DefaultCredentialsManagerTests.swift b/firefox-ios/EcosiaTests/Account/Auth/DefaultCredentialsManagerTests.swift new file mode 100644 index 0000000000000..8e0abc74f83c4 --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Auth/DefaultCredentialsManagerTests.swift @@ -0,0 +1,290 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Auth0 +@testable import Ecosia +@testable import Client + +final class DefaultCredentialsManagerTests: XCTestCase { + + var credentialsManager: DefaultCredentialsManager! + var testCredentials: Credentials! + + override func setUp() { + super.setUp() + credentialsManager = DefaultCredentialsManager() + testCredentials = createTestCredentials() + } + + override func tearDown() { + _ = credentialsManager.clear() + credentialsManager = nil + testCredentials = nil + super.tearDown() + } + + // MARK: - Store Credentials Tests + + func testStoreCredentials_withValidCredentials_returnsTrue() { + // Arrange + let credentials = testCredentials! + + // Act + let result = credentialsManager.store(credentials: credentials) + + // Assert + XCTAssertTrue(result) + } + + // MARK: - Retrieve Credentials Tests + + func testCredentials_withStoredCredentials_returnsStoredCredentials() async throws { + // Arrange + let storedCredentials = testCredentials! + _ = credentialsManager.store(credentials: storedCredentials) + + // Act + let retrievedCredentials = try await credentialsManager.credentials() + + // Assert + XCTAssertEqual(retrievedCredentials.accessToken, storedCredentials.accessToken) + XCTAssertEqual(retrievedCredentials.idToken, storedCredentials.idToken) + XCTAssertEqual(retrievedCredentials.refreshToken, storedCredentials.refreshToken) + } + + func testCredentials_withNoStoredCredentials_throwsError() async { + // Arrange + // No credentials stored + + // Act & Assert + do { + _ = try await credentialsManager.credentials() + XCTFail("Should throw error when no credentials are stored") + } catch { + // Expected behavior + XCTAssertNotNil(error) + } + } + + func testCredentials_afterClearingCredentials_throwsError() async { + // Arrange + _ = credentialsManager.store(credentials: testCredentials) + _ = credentialsManager.clear() + + // Act & Assert + do { + _ = try await credentialsManager.credentials() + XCTFail("Should throw error after clearing credentials") + } catch { + // Expected behavior + XCTAssertNotNil(error) + } + } + + // MARK: - Clear Credentials Tests + + func testClear_withStoredCredentials_returnsTrue() { + // Arrange + _ = credentialsManager.store(credentials: testCredentials) + + // Act + let result = credentialsManager.clear() + + // Assert + XCTAssertTrue(result) + } + + func testClear_withNoStoredCredentials_returnsFalse() { + // Arrange + // No credentials stored + + // Act + let result = credentialsManager.clear() + + // Assert + // Auth0's CredentialsManager returns false when there are no credentials to clear + XCTAssertFalse(result, "Clearing when no credentials exist returns false per Auth0 implementation") + } + + func testClear_actuallyRemovesCredentials() async { + // Arrange + _ = credentialsManager.store(credentials: testCredentials) + + // Act + _ = credentialsManager.clear() + + // Assert + do { + _ = try await credentialsManager.credentials() + XCTFail("Should not be able to retrieve credentials after clearing") + } catch { + // Expected behavior - credentials should be gone + XCTAssertNotNil(error) + } + } + + // MARK: - Can Renew Tests + + func testCanRenew_withValidRefreshToken_returnsTrue() { + // Arrange + let credentialsWithRefreshToken = Credentials( + accessToken: "test-access-token", + tokenType: "Bearer", + idToken: "test-id-token", + refreshToken: "test-refresh-token", // Valid refresh token + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + _ = credentialsManager.store(credentials: credentialsWithRefreshToken) + + // Act + let canRenew = credentialsManager.canRenew() + + // Assert + XCTAssertTrue(canRenew, "Should be able to renew with valid refresh token") + } + + func testCanRenew_withNoStoredCredentials_returnsFalse() { + // Arrange + // No credentials stored + + // Act + let canRenew = credentialsManager.canRenew() + + // Assert + XCTAssertFalse(canRenew, "Should not be able to renew without stored credentials") + } + + func testCanRenew_withEmptyRefreshToken_returnsTrue() { + // Arrange + let credentialsWithoutRefreshToken = Credentials( + accessToken: "test-access-token", + tokenType: "Bearer", + idToken: "test-id-token", + refreshToken: "", // Empty refresh token (Auth0 treats empty string as valid) + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + _ = credentialsManager.store(credentials: credentialsWithoutRefreshToken) + + // Act + let canRenew = credentialsManager.canRenew() + + // Assert + // Auth0's actual behavior allows empty refresh tokens to be renewable + // This test verifies the actual behavior rather than expected behavior + XCTAssertTrue(canRenew, "Auth0 CredentialsManager allows renewal with empty refresh token") + } + + // MARK: - Renew Credentials Tests + + func testRenew_withValidRefreshToken_returnsNewCredentials() async throws { + // Arrange + _ = credentialsManager.store(credentials: testCredentials) + + // Act & Assert + // Note: This test requires a real Auth0 environment, so we expect it to fail + // In a production test environment, this would work with valid Auth0 credentials + do { + let renewedCredentials = try await credentialsManager.renew() + // If somehow this succeeds in test environment, verify the structure + XCTAssertNotNil(renewedCredentials) + XCTAssertNotNil(renewedCredentials.accessToken) + XCTAssertNotNil(renewedCredentials.idToken) + } catch { + // Expected behavior in test environment with invalid credentials + XCTAssertNotNil(error) + // Verify it's the expected Auth0 error + let errorMessage = error.localizedDescription + XCTAssertTrue(errorMessage.contains("refresh token") || errorMessage.contains("renewal"), + + "Error should be related to credential renewal") + } + } + + func testRenew_withNoStoredCredentials_throwsError() async { + // Arrange + // No credentials stored + + // Act & Assert + do { + _ = try await credentialsManager.renew() + XCTFail("Should throw error when trying to renew without stored credentials") + } catch { + // Expected behavior + XCTAssertNotNil(error) + } + } + + func testRenew_withEmptyRefreshToken_throwsError() async { + // Arrange + let credentialsWithoutRefreshToken = Credentials( + accessToken: "test-access-token", + tokenType: "Bearer", + idToken: "test-id-token", + refreshToken: "", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + _ = credentialsManager.store(credentials: credentialsWithoutRefreshToken) + + // Act & Assert + do { + _ = try await credentialsManager.renew() + XCTFail("Should throw error when trying to renew without valid refresh token") + } catch { + // Expected behavior + XCTAssertNotNil(error) + } + } + + // MARK: - Integration Tests + + func testCompleteCredentialsLifecycle_storeRetrieveClear_worksCorrectly() async throws { + // Arrange + let originalCredentials = testCredentials! + + // Act - Store + let storeResult = credentialsManager.store(credentials: originalCredentials) + + // Assert - Store successful + XCTAssertTrue(storeResult) + + // Act - Retrieve + let retrievedCredentials = try await credentialsManager.credentials() + + // Assert - Retrieved correctly + XCTAssertEqual(retrievedCredentials.accessToken, originalCredentials.accessToken) + XCTAssertEqual(retrievedCredentials.idToken, originalCredentials.idToken) + + // Act - Clear + let clearResult = credentialsManager.clear() + + // Assert - Clear successful + XCTAssertTrue(clearResult) + + // Assert - Credentials actually cleared + do { + _ = try await credentialsManager.credentials() + XCTFail("Should not be able to retrieve credentials after clearing") + } catch { + // Expected behavior + XCTAssertNotNil(error) + } + } + + // MARK: - Helper Methods + + private func createTestCredentials() -> Credentials { + return Credentials( + accessToken: "test-access-token-\(UUID().uuidString)", + tokenType: "Bearer", + idToken: "test-id-token-\(UUID().uuidString)", + refreshToken: "test-refresh-token-\(UUID().uuidString)", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + } +} diff --git a/firefox-ios/EcosiaTests/Account/Auth/EcosiaAuthWindowRegistryTests.swift b/firefox-ios/EcosiaTests/Account/Auth/EcosiaAuthWindowRegistryTests.swift new file mode 100644 index 0000000000000..2b5a458b71364 --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Auth/EcosiaAuthWindowRegistryTests.swift @@ -0,0 +1,363 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Common +@testable import Ecosia + +final class EcosiaAuthWindowRegistryTests: XCTestCase { + + var windowRegistry: EcosiaAuthWindowRegistry! + var testWindowUUID1: WindowUUID! + var testWindowUUID2: WindowUUID! + var testWindowUUID3: WindowUUID! + + override func setUp() { + super.setUp() + windowRegistry = EcosiaAuthWindowRegistry.shared + testWindowUUID1 = WindowUUID.XCTestDefaultUUID + testWindowUUID2 = WindowUUID() + testWindowUUID3 = WindowUUID() + + windowRegistry.clearAllWindows() + } + + // MARK: - Initialization Tests + + func testSharedInstance_returnsSameInstance() { + // Arrange & Act + let instance1 = EcosiaAuthWindowRegistry.shared + let instance2 = EcosiaAuthWindowRegistry.shared + + // Assert + XCTAssertTrue(instance1 === instance2) + } + + func testInitialState_isEmpty() { + // Arrange & Act + let registeredWindows = windowRegistry.registeredWindows + let windowCount = windowRegistry.windowCount + + // Assert + XCTAssertTrue(registeredWindows.isEmpty) + XCTAssertEqual(windowCount, 0) + } + + // MARK: - Window Registration Tests + + func testRegisterWindow_addsWindowToRegistry() { + // Arrange + let windowUUID = testWindowUUID1! + + // Act + windowRegistry.registerWindow(windowUUID) + + // Assert + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID)) + XCTAssertEqual(windowRegistry.windowCount, 1) + XCTAssertTrue(windowRegistry.registeredWindows.contains(windowUUID), "Registered windows should contain the window") + } + + func testRegisterWindow_multipleDifferentWindows_addsAllWindows() { + // Arrange + let windowUUID1 = testWindowUUID1! + let windowUUID2 = testWindowUUID2! + let windowUUID3 = testWindowUUID3! + + // Act + windowRegistry.registerWindow(windowUUID1) + windowRegistry.registerWindow(windowUUID2) + windowRegistry.registerWindow(windowUUID3) + + // Assert + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID1), "Window 1 should be registered") + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID2), "Window 2 should be registered") + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID3), "Window 3 should be registered") + XCTAssertEqual(windowRegistry.windowCount, 3, "Window count should be 3") + + let registeredWindows = windowRegistry.registeredWindows + XCTAssertTrue(registeredWindows.contains(windowUUID1), "Should contain window 1") + XCTAssertTrue(registeredWindows.contains(windowUUID2), "Should contain window 2") + XCTAssertTrue(registeredWindows.contains(windowUUID3), "Should contain window 3") + } + + func testRegisterWindow_sameWindowTwice_doesNotDuplicate() { + // Arrange + let windowUUID = testWindowUUID1! + + // Act + windowRegistry.registerWindow(windowUUID) + windowRegistry.registerWindow(windowUUID) // Register same window again + + // Assert + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID), "Window should be registered") + XCTAssertEqual(windowRegistry.windowCount, 1, "Window count should remain 1") + XCTAssertEqual(windowRegistry.registeredWindows.count, 1, "Should have exactly one registered window") + } + + // MARK: - Window Unregistration Tests + + func testUnregisterWindow_removesWindowFromRegistry() { + // Arrange + let windowUUID = testWindowUUID1! + windowRegistry.registerWindow(windowUUID) + + // Act + windowRegistry.unregisterWindow(windowUUID) + + // Assert + XCTAssertFalse(windowRegistry.isWindowRegistered(windowUUID), "Window should not be registered") + XCTAssertEqual(windowRegistry.windowCount, 0, "Window count should be 0") + XCTAssertFalse(windowRegistry.registeredWindows.contains(windowUUID), "Registered windows should not contain the window") + } + + func testUnregisterWindow_unregisteredWindow_doesNotAffectOtherWindows() { + // Arrange + let windowUUID1 = testWindowUUID1! + let windowUUID2 = testWindowUUID2! + let unregisteredWindowUUID = testWindowUUID3! + + windowRegistry.registerWindow(windowUUID1) + windowRegistry.registerWindow(windowUUID2) + + // Act + windowRegistry.unregisterWindow(unregisteredWindowUUID) // Unregister non-existent window + + // Assert + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID1), "Window 1 should still be registered") + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID2), "Window 2 should still be registered") + XCTAssertFalse(windowRegistry.isWindowRegistered(unregisteredWindowUUID), "Unregistered window should not be registered") + XCTAssertEqual(windowRegistry.windowCount, 2, "Window count should remain 2") + } + + func testUnregisterWindow_multipleWindows_removesOnlySpecified() { + // Arrange + let windowUUID1 = testWindowUUID1! + let windowUUID2 = testWindowUUID2! + let windowUUID3 = testWindowUUID3! + + windowRegistry.registerWindow(windowUUID1) + windowRegistry.registerWindow(windowUUID2) + windowRegistry.registerWindow(windowUUID3) + + // Act + windowRegistry.unregisterWindow(windowUUID2) + + // Assert + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID1), "Window 1 should still be registered") + XCTAssertFalse(windowRegistry.isWindowRegistered(windowUUID2), "Window 2 should not be registered") + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID3), "Window 3 should still be registered") + XCTAssertEqual(windowRegistry.windowCount, 2, "Window count should be 2") + } + + // MARK: - Window Query Tests + + func testIsWindowRegistered_withRegisteredWindow_returnsTrue() { + // Arrange + let windowUUID = testWindowUUID1! + windowRegistry.registerWindow(windowUUID) + + // Act + let isRegistered = windowRegistry.isWindowRegistered(windowUUID) + + // Assert + XCTAssertTrue(isRegistered, "Should return true for registered window") + } + + func testIsWindowRegistered_withUnregisteredWindow_returnsFalse() { + // Arrange + let windowUUID = testWindowUUID1! + // Don't register the window + + // Act + let isRegistered = windowRegistry.isWindowRegistered(windowUUID) + + // Assert + XCTAssertFalse(isRegistered, "Should return false for unregistered window") + } + + func testRegisteredWindows_returnsAllRegisteredWindows() { + // Arrange + let windowUUID1 = testWindowUUID1! + let windowUUID2 = testWindowUUID2! + let windowUUID3 = testWindowUUID3! + + windowRegistry.registerWindow(windowUUID1) + windowRegistry.registerWindow(windowUUID2) + windowRegistry.registerWindow(windowUUID3) + + // Act + let registeredWindows = windowRegistry.registeredWindows + + // Assert + XCTAssertEqual(registeredWindows.count, 3, "Should return all registered windows") + XCTAssertTrue(registeredWindows.contains(windowUUID1), "Should contain window 1") + XCTAssertTrue(registeredWindows.contains(windowUUID2), "Should contain window 2") + XCTAssertTrue(registeredWindows.contains(windowUUID3), "Should contain window 3") + } + + func testWindowCount_returnsCorrectCount() { + // Arrange + let windowUUID1 = testWindowUUID1! + let windowUUID2 = testWindowUUID2! + + // Act & Assert - Initial count + XCTAssertEqual(windowRegistry.windowCount, 0, "Initial count should be 0") + + // Act & Assert - After registering one window + windowRegistry.registerWindow(windowUUID1) + XCTAssertEqual(windowRegistry.windowCount, 1, "Count should be 1 after registering one window") + + // Act & Assert - After registering another window + windowRegistry.registerWindow(windowUUID2) + XCTAssertEqual(windowRegistry.windowCount, 2, "Count should be 2 after registering two windows") + + // Act & Assert - After unregistering one window + windowRegistry.unregisterWindow(windowUUID1) + XCTAssertEqual(windowRegistry.windowCount, 1, "Count should be 1 after unregistering one window") + } + + // MARK: - Cleanup Tests + + func testClearAllWindows_removesAllWindows() { + // Arrange + let windowUUID1 = testWindowUUID1! + let windowUUID2 = testWindowUUID2! + let windowUUID3 = testWindowUUID3! + + windowRegistry.registerWindow(windowUUID1) + windowRegistry.registerWindow(windowUUID2) + windowRegistry.registerWindow(windowUUID3) + + // Act + windowRegistry.clearAllWindows() + + // Assert + XCTAssertEqual(windowRegistry.windowCount, 0, "Window count should be 0 after clearing") + XCTAssertTrue(windowRegistry.registeredWindows.isEmpty, "Should have no registered windows") + XCTAssertFalse(windowRegistry.isWindowRegistered(windowUUID1), "Window 1 should not be registered") + XCTAssertFalse(windowRegistry.isWindowRegistered(windowUUID2), "Window 2 should not be registered") + XCTAssertFalse(windowRegistry.isWindowRegistered(windowUUID3), "Window 3 should not be registered") + } + + func testClearAllWindows_whenEmpty_doesNotCrash() { + // Arrange - Registry is already empty + + // Act + windowRegistry.clearAllWindows() + + // Assert + XCTAssertEqual(windowRegistry.windowCount, 0, "Window count should remain 0") + XCTAssertTrue(windowRegistry.registeredWindows.isEmpty, "Should have no registered windows") + } + + // MARK: - Thread Safety Tests + + func testConcurrentRegistration_maintainsDataIntegrity() { + // Arrange + let expectation = expectation(description: "Concurrent registrations should complete") + expectation.expectedFulfillmentCount = 50 + let windowUUIDs = (0..<50).map { _ in WindowUUID() } + + // Act - Perform concurrent registrations + for windowUUID in windowUUIDs { + DispatchQueue.global().async { + self.windowRegistry.registerWindow(windowUUID) + expectation.fulfill() + } + } + + // Assert + waitForExpectations(timeout: 5.0) + XCTAssertEqual(windowRegistry.windowCount, 50, "All concurrent registrations should complete successfully") + + // Verify all windows are actually registered + for windowUUID in windowUUIDs { + XCTAssertTrue(windowRegistry.isWindowRegistered(windowUUID), "Window \(windowUUID) should be registered") + } + } + + func testConcurrentRegistrationAndUnregistration_maintainsDataIntegrity() { + // Arrange + let expectation = expectation(description: "Concurrent operations should complete") + expectation.expectedFulfillmentCount = 100 + let windowUUIDs = (0..<25).map { _ in WindowUUID() } + + // Act - Perform concurrent registrations and unregistrations + for windowUUID in windowUUIDs { + DispatchQueue.global().async { + self.windowRegistry.registerWindow(windowUUID) + expectation.fulfill() + } + + DispatchQueue.global().async { + self.windowRegistry.unregisterWindow(windowUUID) + expectation.fulfill() + } + + DispatchQueue.global().async { + self.windowRegistry.registerWindow(windowUUID) + expectation.fulfill() + } + + DispatchQueue.global().async { + _ = self.windowRegistry.isWindowRegistered(windowUUID) + expectation.fulfill() + } + } + + // Assert + waitForExpectations(timeout: 5.0) + // The exact count is unpredictable due to race conditions, but it should be consistent + let finalCount = windowRegistry.windowCount + XCTAssertTrue(finalCount >= 0 && finalCount <= 25, "Final count should be within expected range") + } + + func testConcurrentQueries_returnConsistentResults() { + // Arrange + let expectation = expectation(description: "Concurrent queries should complete") + expectation.expectedFulfillmentCount = 100 + let windowUUIDs = (0..<10).map { _ in WindowUUID() } + + // Register some windows first + for windowUUID in windowUUIDs { + windowRegistry.registerWindow(windowUUID) + } + + // Act - Perform concurrent queries + for _ in 0..<100 { + DispatchQueue.global().async { + _ = self.windowRegistry.registeredWindows + _ = self.windowRegistry.windowCount + if let windowUUID = windowUUIDs.randomElement() { + _ = self.windowRegistry.isWindowRegistered(windowUUID) + } + expectation.fulfill() + } + } + + // Assert + waitForExpectations(timeout: 5.0) + XCTAssertEqual(windowRegistry.windowCount, 10, "Window count should remain consistent") + } + + // MARK: - Memory Management Tests + + func testMemoryUsage_withManyWindows_doesNotLeak() { + // Arrange + let windowUUIDs = (0..<1000).map { _ in WindowUUID() } + + // Act + for windowUUID in windowUUIDs { + windowRegistry.registerWindow(windowUUID) + } + + // Assert + XCTAssertEqual(windowRegistry.windowCount, 1000, "Should handle many windows") + + // Cleanup + windowRegistry.clearAllWindows() + XCTAssertEqual(windowRegistry.windowCount, 0, "Should clean up properly") + } +} diff --git a/firefox-ios/EcosiaTests/Account/Auth/NativeToWebSSOAuth0ProviderTests.swift b/firefox-ios/EcosiaTests/Account/Auth/NativeToWebSSOAuth0ProviderTests.swift new file mode 100644 index 0000000000000..e2d9ae210e25c --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Auth/NativeToWebSSOAuth0ProviderTests.swift @@ -0,0 +1,185 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Auth0 +@testable import Ecosia + +final class NativeToWebSSOAuth0ProviderTests: XCTestCase { + + var provider: NativeToWebSSOAuth0Provider! + var mockSettings: MockAuth0SettingsProvider! + var mockCredentialsManager: MockCredentialsManager! + + override func setUp() { + super.setUp() + mockSettings = MockAuth0SettingsProvider() + mockCredentialsManager = MockCredentialsManager() + provider = NativeToWebSSOAuth0Provider( + settings: mockSettings, + credentialsManager: mockCredentialsManager + ) + } + + // MARK: - Initialization Tests + + func testInit_withDefaultParameters_createsProvider() { + // Arrange & Act + let provider = NativeToWebSSOAuth0Provider() + + // Assert + XCTAssertNotNil(provider.settings) + XCTAssertNotNil(provider.credentialsManager) + XCTAssertTrue(provider.settings is DefaultAuth0SettingsProvider) + XCTAssertTrue(provider.credentialsManager is DefaultCredentialsManager) + } + + func testInit_withSettingsButNoCredentialsManager_createsCredentialsManagerWithSettings() { + // Arrange + let customSettings = MockAuth0SettingsProvider() + + // Act + let provider = NativeToWebSSOAuth0Provider(settings: customSettings) + + // Assert + XCTAssertNotNil(provider.credentialsManager) + } + + // MARK: - SSO Credentials Tests + + func testGetSSOCredentials_withValidRefreshToken_returnsSSOCredentials() async throws { + // Arrange + let testCredentials = createTestCredentials() + mockCredentialsManager.storedCredentials = testCredentials + + // getSSOCredentials calls Auth0.authentication().ssoExchange().start() which is difficult to mock + // However, we can test that it properly retrieves credentials and validates the refresh token + + // Act & Assert + do { + // This will fail at the Auth0 API call, but we can verify it gets past the initial validation + _ = try await provider.getSSOCredentials() + // If we reach here in unit tests, something unexpected happened (Auth0 SDK shouldn't work without setup) + XCTFail("getSSOCredentials should fail in unit test environment without Auth0 setup") + } catch let error as NativeToWebSSOAuth0Provider.NativeToWebSSOError { + // If we get our custom error, something went wrong with our logic + XCTFail("Got NativeToWebSSOError when we expected Auth0 SDK error: \(error)") + } catch { + // Expected - Auth0 SDK call should fail in unit test environment + // This means our validation logic (refresh token check) passed successfully + XCTAssertNotNil(error) + + // Verify that credentials were retrieved (should have been called) + XCTAssertEqual(mockCredentialsManager.credentialsCallCount, 1) + } + + // Additional test: Verify the method properly uses the provider's settings + XCTAssertEqual(provider.settings.id, mockSettings.id) + XCTAssertEqual(provider.settings.domain, mockSettings.domain) + } + + func testGetSSOCredentials_withMissingRefreshToken_throwsError() async { + // Arrange + let credentialsWithoutRefreshToken = Credentials( + accessToken: "test-access-token", + tokenType: "Bearer", + idToken: "test-id-token", + refreshToken: nil, // Missing refresh token + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + mockCredentialsManager.storedCredentials = credentialsWithoutRefreshToken + + // Act & Assert + do { + _ = try await provider.getSSOCredentials() + XCTFail("Expected error to be thrown for missing refresh token") + } catch let error as NativeToWebSSOAuth0Provider.NativeToWebSSOError { + if case .missingRefreshToken(let message) = error { + XCTAssertEqual(message, "Refresh token is missing. Please check your credentials.") + } else { + XCTFail("Expected missingRefreshToken error, got: \(error)") + } + } catch { + XCTFail("Expected NativeToWebSSOError, got: \(error)") + } + } + + func testGetSSOCredentials_withCredentialsRetrievalFailure_throwsError() async { + // Arrange + mockCredentialsManager.shouldFailCredentials = true + mockCredentialsManager.mockError = NSError( + domain: "TestError", + code: 500, + userInfo: [NSLocalizedDescriptionKey: "Failed to retrieve credentials"] + ) + + // Act & Assert + do { + _ = try await provider.getSSOCredentials() + XCTFail("Expected error to be thrown for credentials retrieval failure") + } catch { + XCTAssertEqual((error as NSError).domain, "TestError") + XCTAssertEqual((error as NSError).code, 500) + } + } + + // MARK: - Protocol Conformance Tests + + func testProvider_conformsToAuth0ProviderProtocol() { + // Arrange & Act + let provider: Auth0ProviderProtocol = NativeToWebSSOAuth0Provider() + + // Assert + XCTAssertNotNil(provider) + XCTAssertNotNil(provider.settings) + XCTAssertNotNil(provider.credentialsManager) + } + + func testProvider_usesCorrectSettings() { + // Arrange + let customSettings = MockAuth0SettingsProvider() + customSettings.id = "custom-client-id" + customSettings.domain = "custom.auth0.com" + customSettings.cookieDomain = "custom.ecosia.org" + + // Act + let provider = NativeToWebSSOAuth0Provider(settings: customSettings) + + // Assert + XCTAssertEqual(provider.settings.id, "custom-client-id") + XCTAssertEqual(provider.settings.domain, "custom.auth0.com") + XCTAssertEqual(provider.settings.cookieDomain, "custom.ecosia.org") + } + + // MARK: - Error Handling Tests + + func testNativeToWebSSOError_equatableConformance() { + // Arrange + let error1 = NativeToWebSSOAuth0Provider.NativeToWebSSOError.invalidResponse + let error2 = NativeToWebSSOAuth0Provider.NativeToWebSSOError.invalidResponse + let error3 = NativeToWebSSOAuth0Provider.NativeToWebSSOError.missingRefreshToken("test") + let error4 = NativeToWebSSOAuth0Provider.NativeToWebSSOError.missingRefreshToken("test") + let error5 = NativeToWebSSOAuth0Provider.NativeToWebSSOError.missingRefreshToken("different") + + // Act & Assert + XCTAssertEqual(error1, error2) + XCTAssertEqual(error3, error4) + XCTAssertNotEqual(error3, error5) + XCTAssertNotEqual(error1, error3) + } + + // MARK: - Helper Methods + + private func createTestCredentials() -> Credentials { + return Credentials( + accessToken: "test-access-token", + tokenType: "Bearer", + idToken: "test-id-token", + refreshToken: "test-refresh-token", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email offline_access" + ) + } +} diff --git a/firefox-ios/EcosiaTests/Account/Mocks/MockAuth0Provider.swift b/firefox-ios/EcosiaTests/Account/Mocks/MockAuth0Provider.swift new file mode 100644 index 0000000000000..7ef7198813191 --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Mocks/MockAuth0Provider.swift @@ -0,0 +1,157 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Auth0 +import Combine +@testable import Ecosia + +class MockAuth0Provider: Auth0ProviderProtocol { + + // MARK: - Test Control Properties + var shouldFailAuth = false + var shouldFailStoreCredentials = false + var shouldFailRetrieveCredentials = false + var shouldFailClearSession = false + var shouldFailRenewCredentials = false + var canRenewCredentialsResult = true + var clearCredentialsResult = true + + // MARK: - Mock Data + var mockCredentials: Credentials? + var storedCredentials: Credentials? + var mockError: Error? + var hasStoredCredentials = false + + // MARK: - Call Tracking + var startAuthCallCount = 0 + var clearSessionCallCount = 0 + var storeCredentialsCallCount = 0 + var retrieveCredentialsCallCount = 0 + var clearCredentialsCallCount = 0 + var renewCredentialsCallCount = 0 + var canRenewCredentialsCallCount = 0 + + // MARK: - Protocol Requirements + var settings: Auth0SettingsProviderProtocol = MockAuth0SettingsProvider() + var credentialsManager: CredentialsManagerProtocol = MockCredentialsManager() + lazy var webAuth: WebAuth = Auth0.webAuth(clientId: "test-client", domain: "test.auth0.com") + + // MARK: - Mock Implementations + func startAuth() async throws -> Credentials { + startAuthCallCount += 1 + + if shouldFailAuth { + throw mockError ?? NSError(domain: "MockAuth0Provider", code: 1001, userInfo: [NSLocalizedDescriptionKey: "Mock auth failure"]) + } + + return mockCredentials ?? createMockCredentials() + } + + func clearSession() async throws { + clearSessionCallCount += 1 + + if shouldFailClearSession { + throw mockError ?? NSError(domain: "MockAuth0Provider", code: 1002, userInfo: [NSLocalizedDescriptionKey: "Mock clear session failure"]) + } + } + + func storeCredentials(_ credentials: Credentials) throws -> Bool { + storeCredentialsCallCount += 1 + + if shouldFailStoreCredentials { + throw mockError ?? NSError(domain: "MockAuth0Provider", code: 1003, userInfo: [NSLocalizedDescriptionKey: "Mock store credentials failure"]) + } + + // Mark that we now have stored credentials + hasStoredCredentials = true + return true + } + + func retrieveCredentials() async throws -> Credentials { + retrieveCredentialsCallCount += 1 + + if shouldFailRetrieveCredentials { + throw mockError ?? NSError(domain: "MockAuth0Provider", code: 1004, userInfo: [NSLocalizedDescriptionKey: "Mock retrieve credentials failure"]) + } + + // Only return credentials if we actually have stored credentials + guard hasStoredCredentials else { + throw NSError(domain: "MockAuth0Provider", code: 1006, userInfo: [NSLocalizedDescriptionKey: "No credentials were found in the store."]) + } + + // Simulate Auth0's CredentialsManager auto-refresh: when stored credentials are expired, + // return mockCredentials to represent the freshly refreshed token + if let storedCreds = storedCredentials, storedCreds.expiresIn < Date() { + return mockCredentials ?? createMockCredentials() + } + + return storedCredentials ?? mockCredentials ?? createMockCredentials() + } + + func clearCredentials() -> Bool { + clearCredentialsCallCount += 1 + + if clearCredentialsResult { + hasStoredCredentials = false + mockCredentials = nil + } + + return clearCredentialsResult + } + + func canRenewCredentials() -> Bool { + canRenewCredentialsCallCount += 1 + return canRenewCredentialsResult && hasStoredCredentials + } + + func renewCredentials() async throws -> Credentials { + renewCredentialsCallCount += 1 + + if shouldFailRenewCredentials { + throw mockError ?? NSError(domain: "MockAuth0Provider", code: 1005, userInfo: [NSLocalizedDescriptionKey: "Mock renew credentials failure"]) + } + + guard hasStoredCredentials else { + throw NSError(domain: "MockAuth0Provider", code: 1007, userInfo: [NSLocalizedDescriptionKey: "Cannot renew credentials: no stored credentials"]) + } + + return mockCredentials ?? createMockCredentials() + } + + // MARK: - Helper Methods + private func createMockCredentials() -> Credentials { + return Credentials( + accessToken: "mock-access-token", + tokenType: "Bearer", + idToken: "mock-id-token", + refreshToken: "mock-refresh-token", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + } + + func reset() { + shouldFailAuth = false + shouldFailStoreCredentials = false + shouldFailRetrieveCredentials = false + shouldFailClearSession = false + shouldFailRenewCredentials = false + canRenewCredentialsResult = true + clearCredentialsResult = true + + mockCredentials = nil + storedCredentials = nil + mockError = nil + hasStoredCredentials = false // Clear stored credentials state + + startAuthCallCount = 0 + clearSessionCallCount = 0 + storeCredentialsCallCount = 0 + retrieveCredentialsCallCount = 0 + clearCredentialsCallCount = 0 + renewCredentialsCallCount = 0 + canRenewCredentialsCallCount = 0 + } +} diff --git a/firefox-ios/EcosiaTests/Account/Mocks/MockCredentialsManager.swift b/firefox-ios/EcosiaTests/Account/Mocks/MockCredentialsManager.swift new file mode 100644 index 0000000000000..6d8822b7d114a --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Mocks/MockCredentialsManager.swift @@ -0,0 +1,116 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Auth0 +@testable import Ecosia + +final class MockCredentialsManager: CredentialsManagerProtocol { + + // MARK: - Test Control Properties + var shouldFailCredentials = false + var shouldFailRenew = false + var canRenewResult = true + var clearResult = true + + // MARK: - Mock Data + var storedCredentials: Credentials? + var mockCredentials: Credentials? + var mockError: Error? + var lastStoredCredentials: Credentials? // Track the last credentials that were stored + + // MARK: - Call Tracking + var credentialsCallCount = 0 + var renewCallCount = 0 + var storeCallCount = 0 + var clearCallCount = 0 + var canRenewCallCount = 0 + + // MARK: - Mock Implementations + func credentials() async throws -> Credentials { + credentialsCallCount += 1 + + if shouldFailCredentials { + throw mockError ?? NSError(domain: "MockCredentialsManager", code: 2001, userInfo: [NSLocalizedDescriptionKey: "Mock credentials failure"]) + } + + return mockCredentials ?? storedCredentials ?? createMockCredentials() + } + + func credentials(withScope scope: String?, minTTL: Int) async throws -> Credentials { + // For mock purposes, delegate to the main credentials method + // In a real implementation, minTTL would affect whether credentials are refreshed + return try await credentials() + } + + func renew() async throws -> Credentials { + renewCallCount += 1 + + if shouldFailRenew { + throw mockError ?? NSError(domain: "MockCredentialsManager", code: 2002, userInfo: [NSLocalizedDescriptionKey: "Mock renew failure"]) + } + + let renewedCredentials = mockCredentials ?? createMockCredentials() + storedCredentials = renewedCredentials + return renewedCredentials + } + + func store(credentials: Credentials) -> Bool { + storeCallCount += 1 + storedCredentials = credentials + lastStoredCredentials = credentials // Track the last stored credentials + return true + } + + func clear() -> Bool { + clearCallCount += 1 + if clearResult { + storedCredentials = nil + lastStoredCredentials = nil + } + return clearResult + } + + func canRenew() -> Bool { + canRenewCallCount += 1 + return canRenewResult + } + + // MARK: - Helper Methods + private func createMockCredentials() -> Credentials { + return Credentials( + accessToken: "mock-access-token-\(UUID().uuidString)", + tokenType: "Bearer", + idToken: "mock-id-token-\(UUID().uuidString)", + refreshToken: "mock-refresh-token-\(UUID().uuidString)", + expiresIn: Date().addingTimeInterval(3600), + scope: "openid profile email" + ) + } + + func reset() { + shouldFailCredentials = false + shouldFailRenew = false + canRenewResult = true + clearResult = true + + storedCredentials = nil + mockCredentials = nil + mockError = nil + lastStoredCredentials = nil + + credentialsCallCount = 0 + renewCallCount = 0 + storeCallCount = 0 + clearCallCount = 0 + canRenewCallCount = 0 + } +} + +// MARK: - Mock Auth0SettingsProvider +class MockAuth0SettingsProvider: Auth0SettingsProviderProtocol { + var domain: String = "test.auth0.com" + var id: String = "mock-client-id" + var cookieDomain: String = "test.ecosia.org" +} diff --git a/firefox-ios/EcosiaTests/Account/Mocks/MockNativeToWebSSOAuth0Provider.swift b/firefox-ios/EcosiaTests/Account/Mocks/MockNativeToWebSSOAuth0Provider.swift new file mode 100644 index 0000000000000..b85194a2b6884 --- /dev/null +++ b/firefox-ios/EcosiaTests/Account/Mocks/MockNativeToWebSSOAuth0Provider.swift @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Auth0 +@testable import Ecosia + +/// A mock that simulates NativeToWebSSOAuth0Provider behavior for testing +/// Note: Since NativeToWebSSOAuth0Provider is a struct, we can't inherit from it +/// Instead, we create a separate mock that can be used in tests that need SSO functionality +final class MockNativeToWebSSOAuth0Provider: MockAuth0Provider { + + // MARK: - Additional Test Control Properties for SSO + var shouldFailGetSSOCredentials = false + var mockSSOError: Error? + var getSSOCredentialsCallCount = 0 + + /// Mock implementation of getSSOCredentials for testing SSO functionality + func getSSOCredentials() async throws -> SSOCredentials { + getSSOCredentialsCallCount += 1 + + if shouldFailGetSSOCredentials { + throw mockSSOError ?? NSError(domain: "MockNativeToWebSSOAuth0Provider", code: 1008, userInfo: [NSLocalizedDescriptionKey: "Mock getSSOCredentials failure"]) + } + + // Check if we have stored credentials (simulating the real implementation's behavior) + guard hasStoredCredentials else { + throw NSError(domain: "MockNativeToWebSSOAuth0Provider", code: 1009, userInfo: [NSLocalizedDescriptionKey: "No credentials available for SSO"]) + } + + // Check if refresh token exists (simulating the real implementation's validation) + guard let credentials = mockCredentials, credentials.refreshToken != nil else { + throw NSError(domain: "MockNativeToWebSSOAuth0Provider", code: 1010, userInfo: [NSLocalizedDescriptionKey: "No refresh token available for SSO"]) + } + + // Since SSOCredentials is from Auth0 SDK and difficult to instantiate in tests, + // we'll throw an error that simulates the Auth0 SDK call failing in a test environment + // This allows us to test the validation logic without needing actual Auth0 integration + throw NSError(domain: "MockNativeToWebSSOAuth0Provider", code: 1011, userInfo: [NSLocalizedDescriptionKey: "Mock Auth0 SDK call - would succeed in real environment"]) + } + + override func reset() { + super.reset() + shouldFailGetSSOCredentials = false + mockSSOError = nil + getSSOCredentialsCallCount = 0 + } +} diff --git a/firefox-ios/EcosiaTests/Analytics/AnalyticsSpyTests.swift b/firefox-ios/EcosiaTests/Analytics/AnalyticsSpyTests.swift new file mode 100644 index 0000000000000..29ac26c403c2f --- /dev/null +++ b/firefox-ios/EcosiaTests/Analytics/AnalyticsSpyTests.swift @@ -0,0 +1,996 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Storage +import SnowplowTracker +import Common +import SwiftUI +import ViewInspector +import WebKit +@testable import Client +@testable import Ecosia + +// MARK: - AnalyticsSpy + +final class AnalyticsSpy: Analytics { + + // MARK: - AnalyticsSpy Properties to Capture Calls + + var trackedEvents: [SnowplowTracker.Event] = [] + + override func track(_ event: SnowplowTracker.Event) { + super.track(event) + trackedEvents.append(event) + } + + var installCalled = false + override func install() { + installCalled = true + } + + var activityActionCalled: Analytics.Action.Activity? + override func activity(_ action: Analytics.Action.Activity) { + activityActionCalled = action + super.activity(action) + } + + var bookmarksImportExportPropertyCalled: Analytics.Property.Bookmarks? + override func bookmarksPerformImportExport(_ property: Analytics.Property.Bookmarks) { + bookmarksImportExportPropertyCalled = property + } + + var bookmarksEmptyLearnMoreClickedCalled = false + override func bookmarksEmptyLearnMoreClicked() { + bookmarksEmptyLearnMoreClickedCalled = true + } + + var bookmarksImportEndedPropertyCalled: Analytics.Property.Bookmarks? + override func bookmarksImportEnded(_ property: Analytics.Property.Bookmarks) { + bookmarksImportEndedPropertyCalled = property + } + + var menuClickItemCalled: Analytics.Label.Menu? + var menuClickExpectation: XCTestExpectation? + override func menuClick(_ item: Analytics.Label.Menu) { + menuClickItemCalled = item + DispatchQueue.main.async { + self.menuClickExpectation?.fulfill() + } + } + + var menuShareContentCalled: Analytics.Property.ShareContent? + override func menuShare(_ content: Analytics.Property.ShareContent) { + menuShareContentCalled = content + } + + var menuStatusItemCalled: Analytics.Label.MenuStatus? + var menuStatusItemChangedTo: Bool? + var menuStatusExpectation: XCTestExpectation? + override func menuStatus(changed item: Analytics.Label.MenuStatus, to: Bool) { + menuStatusItemCalled = item + menuStatusItemChangedTo = to + DispatchQueue.main.async { + self.menuStatusExpectation?.fulfill() + } + } + + var introWelcomeActionCalled: Action.Welcome? + var introWelcomePropertyCalled: Property.Welcome? + override func introWelcome(action: Action.Welcome, property: Property.Welcome? = nil) { + introWelcomeActionCalled = action + introWelcomePropertyCalled = property + } + + var navigationActionCalled: Action? + var navigationLabelCalled: Label.Navigation? + override func navigation(_ action: Action, label: Label.Navigation) { + navigationActionCalled = action + navigationLabelCalled = label + } + + var navigationOpenNewsIdCalled: String? + override func navigationOpenNews(_ id: String) { + navigationOpenNewsIdCalled = id + } + + var referralActionCalled: Action.Referral? + var referralLabelCalled: Label.Referral? + + // Separate expectations for different referral actions + var referralClickExpectation: XCTestExpectation? + var referralSendExpectation: XCTestExpectation? + + override func referral(action: Action.Referral, label: Label.Referral? = nil) { + referralActionCalled = action + referralLabelCalled = label + DispatchQueue.main.async { + switch action { + case .click: + self.referralClickExpectation?.fulfill() + case .send: + self.referralSendExpectation?.fulfill() + default: + break + } + } + } + + var inappSearchUrlCalled: URL? + override func inappSearch(url: URL) { + inappSearchUrlCalled = url + } + + var ntpTopSiteActionCalled: Action.TopSite? + var ntpTopSitePropertyCalled: Property.TopSite? + var ntpTopSitePositionCalled: NSNumber? + override func ntpTopSite(_ action: Action.TopSite, property: Property.TopSite, position: NSNumber? = nil) { + ntpTopSiteActionCalled = action + ntpTopSitePropertyCalled = property + ntpTopSitePositionCalled = position + } + + var clearAllPrivateDataSectionCalled: Property.SettingsPrivateDataSection? + override func clearsDataFromSection(_ section: Analytics.Property.SettingsPrivateDataSection) { + clearAllPrivateDataSectionCalled = section + } + + var defaultBrowserSettingsShowsDetailViewLabelCalled: Analytics.Label.DefaultBrowser? + override func defaultBrowserSettingsShowsDetailViewVia(_ label: Analytics.Label.DefaultBrowser) { + defaultBrowserSettingsShowsDetailViewLabelCalled = label + } + + var defaultBrowserSettingsViaNudgeCardDismissCalled = false + override func defaultBrowserSettingsViaNudgeCardDismiss() { + defaultBrowserSettingsViaNudgeCardDismissCalled = true + } + + var defaultBrowserSettingsOpenNativeSettingsLabelCalled: Analytics.Label.DefaultBrowser? + override func defaultBrowserSettingsOpenNativeSettingsVia(_ label: Analytics.Label.DefaultBrowser) { + defaultBrowserSettingsOpenNativeSettingsLabelCalled = label + } + + var defaultBrowserSettingsDismissDetailViewLabelCalled: Analytics.Label.DefaultBrowser? + override func defaultBrowserSettingsDismissDetailViewVia(_ label: Analytics.Label.DefaultBrowser) { + defaultBrowserSettingsDismissDetailViewLabelCalled = label + } +} + +// MARK: - AnalyticsSpyTests + +final class AnalyticsSpyTests: XCTestCase { + + // MARK: - Properties and Setup + + var analyticsSpy: AnalyticsSpy! + var profileMock: MockProfile { MockProfile() } + var tabManagerMock: TabManager { + let mock = MockTabManager() + let tab = Tab(profile: profileMock, windowUUID: .XCTestDefaultUUID) + mock.selectedTab = tab + mock.selectedTab?.url = URL(string: "https://example.com") + mock.subscriptedTab = tab + return mock + } + + override func setUp() { + super.setUp() + DependencyHelperMock().bootstrapDependencies(injectedTabManager: tabManagerMock, themeManager: EcosiaMockThemeManager()) + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + } + + override func tearDown() { + super.tearDown() + analyticsSpy = nil + Analytics.shared = Analytics() + } + + // MARK: - AppDelegate Tests + + var appDelegate: AppDelegate { AppDelegate() } + + func testTrackLaunchAndInstallOnDidFinishLaunching() async { + // Arrange + XCTAssertNil(analyticsSpy.activityActionCalled) + let application = await UIApplication.shared + + // Act + _ = await appDelegate.application(application, didFinishLaunchingWithOptions: nil) + + waitForCondition(timeout: 3) { // Wait detached tasks until launch is called + analyticsSpy.activityActionCalled == .launch + } + + // Assert + XCTAssertEqual(analyticsSpy.activityActionCalled, .launch) + XCTAssertTrue(analyticsSpy.installCalled) + } + + func testTrackResumeOnDidBecomeActive() async { + // Arrange + XCTAssertNil(analyticsSpy.activityActionCalled) + let application = await UIApplication.shared + + // Act + _ = await appDelegate.applicationDidBecomeActive(application) + + waitForCondition(timeout: 2) { // Wait detached tasks until resume is called + analyticsSpy.activityActionCalled == .resume + } + + // Assert + XCTAssertEqual(analyticsSpy.activityActionCalled, .resume) + } + + // MARK: - Bookmarks Tests + + var panel: LegacyBookmarksPanel { + let viewModel = BookmarksPanelViewModel(profile: profileMock, + bookmarksHandler: profileMock.places, + bookmarkFolderGUID: "TestGuid") + return LegacyBookmarksPanel(viewModel: viewModel, windowUUID: .XCTestDefaultUUID) + } + + func testTrackImportClick() { + // Arrange + XCTAssertNil(analyticsSpy.bookmarksImportExportPropertyCalled) + + // Act + panel.importBookmarksActionHandler() + + // Assert + XCTAssertEqual(analyticsSpy.bookmarksImportExportPropertyCalled, .import, "Analytics should track bookmarks import.") + } + + func testTrackExportClick() { + // Arrange + XCTAssertNil(analyticsSpy.bookmarksImportExportPropertyCalled) + + // Act + panel.exportBookmarksActionHandler() + + // Assert + XCTAssertEqual(analyticsSpy.bookmarksImportExportPropertyCalled, .export, "Analytics should track bookmarks export.") + } + + func testTrackLearnMoreClick() { + // Arrange + let view = EmptyBookmarksView(initialBottomMargin: 0) + XCTAssertFalse(analyticsSpy.bookmarksEmptyLearnMoreClickedCalled, "Analytics should not have tracked learn more click yet.") + + // Act + view.onLearnMoreTapped() + + // Assert + XCTAssertTrue(analyticsSpy.bookmarksEmptyLearnMoreClickedCalled, "Analytics should track bookmarks empty learn more click.") + } + + // MARK: - Menu Tests + + var menuHelper: MainMenuActionHelper { + MainMenuActionHelper(profile: profileMock, + tabManager: tabManagerMock, + buttonView: .init(), + toastContainer: .init(), + themeManager: MockThemeManager()) + } + + func testTrackMenuAction() { + let testCases: [(Analytics.Label.Menu, String)] = [ + (.openInSafari, .localized(.openInSafari)), + (.history, .LegacyAppMenu.AppMenuHistory), + (.downloads, .LegacyAppMenu.AppMenuDownloads), + (.zoom, String(format: .LegacyAppMenu.ZoomPageTitle, NumberFormatter.localizedString(from: NSNumber(value: 1), number: .percent))), + (.findInPage, .LegacyAppMenu.AppMenuFindInPageTitleString), + (.requestDesktopSite, .LegacyAppMenu.AppMenuViewDesktopSiteTitleString), + (.copyLink, .LegacyAppMenu.AppMenuCopyLinkTitleString), + (.help, .LegacyAppMenu.Help), + (.customizeHomepage, .LegacyAppMenu.CustomizeHomePage), + (.readingList, .LegacyAppMenu.ReadingList), + (.bookmarks, .LegacyAppMenu.Bookmarks) + ] + + for (label, title) in testCases { + XCTContext.runActivity(named: "Menu action \(label.rawValue) is tracked") { _ in + // Arrange + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + XCTAssertNil(analyticsSpy.menuClickItemCalled, "Analytics menuClickItemCalled should be nil before action.") + tabManagerMock.selectedTab?.url = URL(string: "https://example.com") + + // Create expectation + let expectation = self.expectation(description: "Analytics menuClick called with \(label.rawValue)") + analyticsSpy.menuClickExpectation = expectation + + // Act + menuHelper.getToolbarActions(navigationController: .init()) { actions in + let action = actions + .flatMap { $0 } // Flatten sections + .flatMap { $0.items } // Flatten items in sections + .first { $0.title == title } + if let action = action { + action.tapHandler!(action) + } else { + XCTFail("No action title with \(title) found") + } + } + + // Wait for expectation + wait(for: [expectation], timeout: 2) + + // Assert + XCTAssertEqual(analyticsSpy.menuClickItemCalled, label, "Analytics should track menu click with label \(label.rawValue).") + } + } + } + + func testTrackMenuShare() { + let testCases: [(Analytics.Property.ShareContent, URL?)] = [ + (.ntp, URL(string: "file://example.com")), + (.web, URL(string: "https://example.com")), + (.ntp, nil) + ] + for (label, url) in testCases { + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + XCTContext.runActivity(named: "Menu share \(label.rawValue) is tracked") { _ in + XCTAssertNil(analyticsSpy.menuShareContentCalled) + + // Requires valid url to add action + tabManagerMock.selectedTab?.url = url + + let action = menuHelper.getSharingAction().items.first + if let action = action { + action.tapHandler!(action) + } else { + XCTFail("No sharing action found for url \(url?.absoluteString ?? "nil")") + } + } + } + } + + func testTrackMenuStatus() { + struct MenuStatusTestCase { + let label: Analytics.Label.MenuStatus + let value: Bool + let title: String + } + + let testCases: [MenuStatusTestCase] = [ + .init(label: .readingList, value: true, title: .ShareAddToReadingList), + .init(label: .readingList, value: false, title: .LegacyAppMenu.RemoveReadingList), + .init(label: .bookmark, value: true, title: .KeyboardShortcuts.AddBookmark), + .init(label: .shortcut, value: true, title: .AddToShortcutsActionTitle), + .init(label: .shortcut, value: false, title: .LegacyAppMenu.RemoveFromShortcuts) + ] + + for testCase in testCases { + XCTContext.runActivity(named: "Track menu status change \(testCase.label.rawValue) to \(testCase.value)") { _ in + // Reset state + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + tabManagerMock.selectedTab?.url = URL(string: "https://example.com") + + let semaphore = DispatchSemaphore(value: 0) + let expectation = self.expectation(description: "menuStatus called for \(testCase.label.rawValue) to \(testCase.value)") + analyticsSpy.menuStatusExpectation = expectation + + // Act + menuHelper.getToolbarActions(navigationController: .init()) { actions in + let flatItems = actions.flatMap { $0 }.flatMap { $0.items } + guard let action = flatItems.first(where: { $0.title == testCase.title }) else { + XCTFail("No action with title \(testCase.title) found") + semaphore.signal() + return + } + + action.tapHandler?(action) + semaphore.signal() + } + + // Wait for async completion + _ = semaphore.wait(timeout: .now() + 2) + wait(for: [expectation], timeout: 2) + + XCTAssertEqual(analyticsSpy.menuStatusItemCalled, testCase.label, "Expected menu status label to be \(testCase.label.rawValue)") + XCTAssertEqual(analyticsSpy.menuStatusItemChangedTo, testCase.value, "Expected menu status value to be \(testCase.value)") + } + } + } + + // MARK: - News Detail Tests + + func testNewsControllerViewDidAppearTracksNavigationViewNews() { + // Arrange + do { + let item = try createMockNewsModel()! + let items = [item] + let newsController = NewsController(items: items, windowUUID: .XCTestDefaultUUID) + XCTAssertNil(analyticsSpy.navigationActionCalled) + XCTAssertNil(analyticsSpy.navigationLabelCalled) + + // Act + newsController.loadViewIfNeeded() + newsController.viewDidAppear(false) + + // Assert + XCTAssertEqual(analyticsSpy.navigationActionCalled, .view, "Analytics should track navigation action as .view.") + XCTAssertEqual(analyticsSpy.navigationLabelCalled, .news, "Analytics should track navigation label as .news.") + } catch { + XCTFail(error.localizedDescription) + } + } + + func testNewsControllerDidSelectItemTracksNavigationOpenNews() { + // Arrange + do { + let item = try createMockNewsModel()! + let items = [item] + let newsController = NewsController(items: items, windowUUID: .XCTestDefaultUUID) + XCTAssertNil(analyticsSpy.navigationOpenNewsIdCalled) + newsController.loadView() + newsController.collection.reloadData() + let indexPath = IndexPath(row: 0, section: 0) + + // Act + newsController.collectionView(newsController.collection, didSelectItemAt: indexPath) + + // Assert + XCTAssertEqual(analyticsSpy.navigationOpenNewsIdCalled, "example_news_tracking", "Analytics should track navigation open news with ID 'example_news_tracking'.") + } catch { + XCTFail(error.localizedDescription) + } + } + + // MARK: - Multiply Impact - Referrals Tests + + func testMultiplyImpactViewDidAppearTracksReferralViewInviteScreen() { + // Arrange + let referrals = Referrals() + let multiplyImpact = MultiplyImpact(referrals: referrals, windowUUID: .XCTestDefaultUUID) + multiplyImpact.loadViewIfNeeded() + + // Act + multiplyImpact.viewDidAppear(false) + + // Assert + XCTAssertEqual(analyticsSpy.referralActionCalled, .view, "Analytics should track referral action as .view.") + XCTAssertEqual(analyticsSpy.referralLabelCalled, .inviteScreen, "Analytics should track referral label as .inviteScreen.") + } + + func testMultiplyImpactLearnMoreButtonTracksReferralClickLearnMore() { + // Arrange + let referrals = Referrals() + let multiplyImpact = MultiplyImpact(referrals: referrals, windowUUID: .XCTestDefaultUUID) + multiplyImpact.loadViewIfNeeded() + + // Ensure learnMoreButton is not nil + guard let learnMoreButton = multiplyImpact.learnMoreButton else { + XCTFail("learnMoreButton should not be nil after view is loaded") + return + } + + // Create an expectation + let expectation = self.expectation(description: "Analytics referral called for Learn More") + analyticsSpy.referralClickExpectation = expectation + + // Act + learnMoreButton.sendActions(for: .primaryActionTriggered) + + // Wait for the expectation + wait(for: [expectation], timeout: 2) + + // Assert + XCTAssertEqual(analyticsSpy.referralActionCalled, .click, "Analytics should track referral action as .click.") + XCTAssertEqual(analyticsSpy.referralLabelCalled, .learnMore, "Analytics should track referral label as .learnMore.") + } + + func testMultiplyImpactInviteFriendsTracksReferralClickInvite() { + // Arrange + let referrals = Referrals() + let multiplyImpact = MultiplyImpact(referrals: referrals, windowUUID: .XCTestDefaultUUID) + User.shared.referrals.code = "testCode" + multiplyImpact.loadViewIfNeeded() + + // Ensure inviteButton is not nil + guard let inviteButton = multiplyImpact.inviteButton else { + XCTFail("Invite Friends button should not be nil after view is loaded") + return + } + + // Create an expectation + let expectation = self.expectation(description: "Analytics referral called for Invite") + analyticsSpy.referralClickExpectation = expectation + + // Act + inviteButton.sendActions(for: .touchUpInside) + + // Wait for the expectation + wait(for: [expectation], timeout: 2) + + // Assert + XCTAssertEqual(analyticsSpy.referralActionCalled, .click, "Analytics should track referral action as .click.") + XCTAssertEqual(analyticsSpy.referralLabelCalled, .invite, "Analytics should track referral label as .invite.") + } + + func testMultiplyImpactInviteFriendsCompletionTracksReferralSendInvite() { + // Arrange + let referrals = Referrals() + let multiplyImpact = MultiplyImpactTestable(referrals: referrals, windowUUID: .XCTestDefaultUUID) + User.shared.referrals.code = "testCode" + multiplyImpact.loadViewIfNeeded() + + // Ensure inviteButton is not nil + guard let inviteButton = multiplyImpact.inviteButton else { + XCTFail("Invite Friends button should not be nil after view is loaded") + return + } + + // Create expectations for .click and .send actions + let clickExpectation = self.expectation(description: "Analytics referral click called") + let sendExpectation = self.expectation(description: "Analytics referral send called") + analyticsSpy.referralClickExpectation = clickExpectation + analyticsSpy.referralSendExpectation = sendExpectation + + // Act + inviteButton.sendActions(for: .touchUpInside) + + // Wait for the click expectation + wait(for: [clickExpectation], timeout: 2) + + // Assert initial click analytics + XCTAssertEqual(analyticsSpy.referralActionCalled, .click, "Analytics should track referral action as .click.") + XCTAssertEqual(analyticsSpy.referralLabelCalled, .invite, "Analytics should track referral label as .invite.") + + // Reset analyticsSpy properties for the next action + analyticsSpy.referralActionCalled = nil + analyticsSpy.referralLabelCalled = nil + + // Assert that the share sheet is intended to be presented + XCTAssertNotNil(multiplyImpact.capturedPresentedViewController, "Expected a view controller to be presented") + XCTAssertTrue(multiplyImpact.capturedPresentedViewController is UIActivityViewController, "Expected UIActivityViewController to be presented") + + // Simulate share completion + if let activityVC = multiplyImpact.capturedPresentedViewController as? UIActivityViewController, + let completionHandler = activityVC.completionWithItemsHandler { + + // Act: Simulate user completed the share action + completionHandler(nil, true, nil, nil) + + // Wait for the send expectation + wait(for: [sendExpectation], timeout: 2) + + // Assert send analytics + XCTAssertEqual(analyticsSpy.referralActionCalled, .send, "Analytics should track referral action as .send.") + XCTAssertEqual(analyticsSpy.referralLabelCalled, .invite, "Analytics should track referral label as .invite.") + } else { + XCTFail("UIActivityViewController not found or completion handler not set") + } + } + + // MARK: - Top Sites Tests + + func testTilePressedTracksAnalyticsForPinnedSite() { + // Arrange + let viewModel = TopSitesViewModel(profile: profileMock, + isZeroSearch: false, + theme: EcosiaLightTheme(), + wallpaperManager: WallpaperManager()) + let topSite = TopSite(site: PinnedSite(site: Site(url: "http://www.example.com", title: "Example Site"), faviconResource: nil)) + let position = 1 + + // Act + viewModel.tilePressed(site: topSite, position: position) + + // Assert + XCTAssertEqual(analyticsSpy.ntpTopSiteActionCalled, .click, "Analytics should track ntpTopSiteActionCalled as .click.") + XCTAssertEqual(analyticsSpy.ntpTopSitePropertyCalled, .pinned, "Analytics should track ntpTopSitePropertyCalled as .pinned.") + XCTAssertEqual(analyticsSpy.ntpTopSitePositionCalled, NSNumber(value: position), "Analytics should track ntpTopSitePositionCalled correctly.") + } + + func testTilePressedTracksAnalyticsForMostVisitedSite() { + // Arrange + let viewModel = TopSitesViewModel(profile: profileMock, + isZeroSearch: false, + theme: EcosiaLightTheme(), + wallpaperManager: WallpaperManager()) + let topSite = TopSite(site: Site(url: "http://www.example.org", title: "Example Site")) + let position = 1 + + // Act + viewModel.tilePressed(site: topSite, position: position) + + // Assert + XCTAssertEqual(analyticsSpy.ntpTopSiteActionCalled, .click, "Analytics should track ntpTopSiteActionCalled as .click.") + XCTAssertEqual(analyticsSpy.ntpTopSitePropertyCalled, .mostVisited, "Analytics should track ntpTopSitePropertyCalled as .mostVisited.") + XCTAssertEqual(analyticsSpy.ntpTopSitePositionCalled, NSNumber(value: position), "Analytics should track ntpTopSitePositionCalled correctly.") + } + + func testTrackTopSiteMenuActionTracksAnalytics() { + // Arrange + let viewModel = TopSitesViewModel(profile: profileMock, + isZeroSearch: false, + theme: EcosiaLightTheme(), + wallpaperManager: WallpaperManager()) + let action: Analytics.Action.TopSite = .remove + let site = Site(url: "http://www.example.org", title: "Example Site") + + // Act + viewModel.trackTopSiteMenuAction(site: site, action: action) + + // Assert + XCTAssertEqual(analyticsSpy.ntpTopSiteActionCalled, action, "Analytics should track ntpTopSiteActionCalled correctly.") + XCTAssertEqual(analyticsSpy.ntpTopSitePropertyCalled, .mostVisited, "Analytics should track ntpTopSitePropertyCalled as .mostVisited.") + XCTAssertNil(analyticsSpy.ntpTopSitePositionCalled, "Analytics ntpTopSitePositionCalled should be nil.") + } + + // MARK: - WebView delegate Search Event + + func testWebViewDelegateTracksSearchEventOnEcosiaVerticalURLChange() { + let browser = BrowserViewController(profile: profileMock, tabManager: tabManagerMock) + + let rootURL = EcosiaEnvironment.current.urlProvider.root + let testCases = [ + ("https://www.example.org", false, "Does not track external URLs"), + ("\(rootURL)", false, "Does not track index page"), + ("\(rootURL)/search?q=test", true, "Tracks search query"), + ("\(rootURL)/search?q=test", true, "Tracks same URL again with .other type"), + ("\(rootURL)/images?q=test1", true, "Tracks images query"), + ("\(rootURL)/news?q=test2&p=1", true, "Tracks news query"), + ("\(rootURL)/videos?q=test3", true, "Tracks videos query"), + ("\(rootURL)/settings", false, "Does not track non-search pages"), + ("https://blog.ecosia.org/", false, "Does not track on other Ecosia urls"), + ] + + for (urlString, shouldTrack, message) in testCases { + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + let url = URL(string: urlString)! + let action = FakeNavigationAction(url: url, navigationType: .other) + browser.webView(makeWebView(), + decidePolicyFor: action) { policy in + XCTAssertEqual(policy, .allow, "Should allow independent of tracking behavior") + } + // inappSearch is now fired at didCommit, not decidePolicyFor + browser.ecosiaHandleDidCommit(url: url) + + if shouldTrack { + XCTAssertEqual(analyticsSpy.inappSearchUrlCalled?.absoluteString, + url.absoluteString, + "Failure on: \(message)") + } else { + XCTAssertNil(analyticsSpy.inappSearchUrlCalled, "Failure on: \(message)") + } + analyticsSpy = nil + Analytics.shared = Analytics() + } + } + + func testWebViewDelegateTracksSearchEventBasedOnNavigationType() { + let browser = BrowserViewController(profile: profileMock, tabManager: tabManagerMock) + + let rootURL = EcosiaEnvironment.current.urlProvider.root + let testCases = [ + (WKNavigationType.other, "\(rootURL)/search?q=test", true, "Tracks regular navigation"), + (WKNavigationType.reload, "\(rootURL)/search?q=test", true, "Tracks reload"), + (WKNavigationType.backForward, "\(rootURL)/search?q=test", false, "Does not track back/forward"), + ] + + for (type, urlString, shouldTrack, message) in testCases { + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + let url = URL(string: urlString)! + let action = FakeNavigationAction(url: url, navigationType: type) + browser.webView(makeWebView(), + decidePolicyFor: action) { policy in + XCTAssertEqual(policy, .allow, "Should allow independent of tracking behavior") + } + browser.ecosiaHandleDidCommit(url: url) + + if shouldTrack { + XCTAssertEqual(analyticsSpy.inappSearchUrlCalled?.absoluteString, + url.absoluteString, + "Failure on: \(message)") + } else { + XCTAssertNil(analyticsSpy.inappSearchUrlCalled, "Failure on: \(message)") + } + analyticsSpy = nil + Analytics.shared = Analytics() + } + } + + func testWebViewDelegateTracksSearchEventOnSameURLWhenLinkActivated() { + let browser = BrowserViewController(profile: profileMock, tabManager: tabManagerMock) + let rootURL = EcosiaEnvironment.current.urlProvider.root + let url = URL(string: "\(rootURL)/search?q=test")! + + // Load the URL once to establish it as the current page + let firstAction = FakeNavigationAction(url: url, navigationType: .other) + browser.webView(makeWebView(), decidePolicyFor: firstAction) { _ in } + browser.ecosiaHandleDidCommit(url: url) + + // Navigate to the same URL again via link activation (e.g. tapping the same search vertical) + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + let secondAction = FakeNavigationAction(url: url, navigationType: .linkActivated) + browser.webView(makeWebView(), decidePolicyFor: secondAction) { _ in } + browser.ecosiaHandleDidCommit(url: url) + + XCTAssertEqual(analyticsSpy.inappSearchUrlCalled?.absoluteString, + url.absoluteString, + "Should track same URL when navigated via link activation, not treated as tab restore") + } + + func testEcosiaHandleDidCommitDoesNotFireWhenURLDoesNotMatchPending() { + let browser = BrowserViewController(profile: profileMock, tabManager: tabManagerMock) + analyticsSpy = AnalyticsSpy() + Analytics.shared = analyticsSpy + + let rootURL = EcosiaEnvironment.current.urlProvider.root + let searchUrl = URL(string: "\(rootURL)/search?q=test")! + let differentUrl = URL(string: "\(rootURL)/images?q=test")! + + // Simulate decidePolicyFor setting the pending URL to searchUrl + let action = FakeNavigationAction(url: searchUrl, navigationType: .other) + browser.webView(makeWebView(), decidePolicyFor: action) { _ in } + + // didCommit fires with a different URL (e.g. a redirect landed elsewhere) + browser.ecosiaHandleDidCommit(url: differentUrl) + + XCTAssertNil(analyticsSpy.inappSearchUrlCalled, + "Should not track when committed URL does not match pending URL") + } + + // MARK: - Analytics Context Tests + + func testAddUserStateContextOnResumeEvent() { + // Arrange + let analyticsSpy = makeAnalyticsSpyContextSUT(status: .ephemeral) + let expectation = self.expectation(description: "Event tracked") + let event = Structured(category: "", + action: Analytics.Action.Activity.resume.rawValue) + + // Act + analyticsSpy.appendActivityContextIfNeeded(.resume, event) { + expectation.fulfill() + } + waitForExpectations(timeout: 1.0, handler: nil) + + // Assert + let userContext = event.entities.first { $0.schema == Analytics.userSchema } + XCTAssertNotNil(userContext, "User state context not found in event entities") + if let userContext = userContext { + XCTAssertEqual(userContext.data["push_notification_state"] as? String, "enabled") + } + } + + func testAddUserStateContextOnLaunchEvent() { + // Arrange + let analyticsSpy = makeAnalyticsSpyContextSUT(status: .denied) + let expectation = self.expectation(description: "Event tracked") + let event = Structured(category: "", + action: Analytics.Action.Activity.launch.rawValue) + + // Act + analyticsSpy.appendActivityContextIfNeeded(.resume, event) { + expectation.fulfill() + } + waitForExpectations(timeout: 1.0, handler: nil) + + // Assert + let userContext = event.entities.first { $0.schema == Analytics.userSchema } + XCTAssertNotNil(userContext, "User state context not found in event entities") + if let userContext = userContext { + XCTAssertEqual(userContext.data["push_notification_state"] as? String, "disabled") + } + } + + func testAddUserSeedCountContextToAllEvents() { + // Arrange + let analyticsSpy = makeAnalyticsSpyContextSUT() + User.shared.sendAnonymousUsageData = true + let event = Structured(category: Analytics.Category.bookmarks.rawValue, + action: Analytics.Action.click.rawValue) + + // Act + analyticsSpy.track(event) + + // Assert + XCTAssertEqual(analyticsSpy.trackedEvents.count, 1, "Should have tracked one event") + let seedCountContext = event.entities.first { $0.schema == Analytics.impactBalanceSchema } + XCTAssertNotNil(seedCountContext, "User seed count context must be added to the structured event") + if let seedCountContext = seedCountContext { + XCTAssertEqual(seedCountContext.data["amount"] as? Int, User.shared.seedCount) + } + } + + func testAddUserSeedCountContextToResumeEventOnDidBecomeActive() async { + // Arrange + User.shared.sendAnonymousUsageData = true + XCTAssertNil(analyticsSpy.activityActionCalled) + let application = await UIApplication.shared + + // Act + _ = await appDelegate.applicationDidBecomeActive(application) + + waitForCondition(timeout: 2) { + !analyticsSpy.trackedEvents.isEmpty && analyticsSpy.activityActionCalled == .resume + } + + // Assert + XCTAssertEqual(analyticsSpy.activityActionCalled, .resume) + XCTAssertEqual(analyticsSpy.trackedEvents.count, 1) + + if let structuredEvent = analyticsSpy.trackedEvents.first(where: { ($0 as? Structured)?.action == Analytics.Action.Activity.resume.rawValue }) { + let seedCountContext = structuredEvent.entities.first { $0.schema == Analytics.impactBalanceSchema } + XCTAssertNotNil(seedCountContext) + if let seedCountContext = seedCountContext { + XCTAssertEqual(seedCountContext.data["amount"] as? Int, User.shared.seedCount) + } + } else { + XCTFail("Tracked event should be a Structured event") + } + } + + // MARK: Analytics Private Data clearance + + func testClearPrivateDataTracksEvent() { + // Arrange + let vc = ClearPrivateDataTableViewController(profile: profileMock, tabManager: tabManagerMock) + vc.loadViewIfNeeded() + + // Act + vc.tableView(vc.tableView, didSelectRowAt: IndexPath(row: 0, section: 2)) + + // Assert + XCTAssertEqual(analyticsSpy.clearAllPrivateDataSectionCalled, .main, "Analytics should track clearAllPrivateDataSectionCalled as .main because we are simulating the click on Clear Private Data") + } + + func testClearWebsitesDataTracksEvent() { + // Arrange + let vc = WebsiteDataManagementViewController(windowUUID: .XCTestDefaultUUID) + vc.loadViewIfNeeded() + + // Act + vc.tableView(vc.tableView, didSelectRowAt: IndexPath(row: 0, section: 2)) + + // Assert + XCTAssertEqual(analyticsSpy.clearAllPrivateDataSectionCalled, .websites, "Analytics should track clearAllPrivateDataSectionCalled as .websites because we are simulating the click on Clear Websiste Data") + } + + // MARK: Analytics Default Browser + + func testShowInstructionStepsTriggersAnalyticsEvent() throws { + User.shared.showDefaultBrowserSettingNudgeCard() + DependencyHelperMock().bootstrapDependencies() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: AppContainer.shared.resolve()) + + let view = makeInstructionsViewSUT() + .onAppear { + Analytics.shared.defaultBrowserSettingsDismissDetailViewVia(.settingsNudgeCard) + } + + try view.inspect().callOnAppear() + + XCTAssertEqual(analyticsSpy.defaultBrowserSettingsDismissDetailViewLabelCalled, .settingsNudgeCard) + } + + func testTappingDismissButtonOnNudgeCardTriggersAnalyticsEvent() { + DependencyHelperMock().bootstrapDependencies() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: AppContainer.shared.resolve()) + User.shared.showDefaultBrowserSettingNudgeCard() + let vc = AppSettingsTableViewController(with: profileMock, + and: tabManagerMock) + vc.loadViewIfNeeded() + vc.viewWillAppear(false) + + guard let header = vc.tableView(vc.tableView, viewForHeaderInSection: 0) as? DefaultBrowserSettingsNudgeCardHeaderView else { + XCTFail("Expected nudge card header") + return + } + + header.onDismiss?() + + XCTAssertTrue(analyticsSpy.defaultBrowserSettingsViaNudgeCardDismissCalled) + } + + func testDismissInstructionStepsTriggersAnalyticsEvent() throws { + User.shared.showDefaultBrowserSettingNudgeCard() + DependencyHelperMock().bootstrapDependencies() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: AppContainer.shared.resolve()) + + let view = makeInstructionsViewSUT() + .onDisappear { + Analytics.shared.defaultBrowserSettingsDismissDetailViewVia(.settingsNudgeCard) + } + + try view.inspect().callOnDisappear() + + XCTAssertEqual(analyticsSpy.defaultBrowserSettingsDismissDetailViewLabelCalled, .settingsNudgeCard) + } + + func testDefaultBrowserSettingsOpenNativeSettingsTracksLabelAndProperty() throws { + User.shared.showDefaultBrowserSettingNudgeCard() + DependencyHelperMock().bootstrapDependencies() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: AppContainer.shared.resolve()) + + let view = makeInstructionsViewSUT(onButtonTap: { + Analytics.shared.defaultBrowserSettingsOpenNativeSettingsVia(.settings) + }) + + try view.inspect().find(button: String.Key.defaultBrowserCardDetailButton.rawValue).tap() + + analyticsSpy.defaultBrowserSettingsOpenNativeSettingsVia(.settings) + XCTAssertEqual(analyticsSpy.defaultBrowserSettingsOpenNativeSettingsLabelCalled, .settings, "Expected label 'default_browser_settings' to be tracked.") + } +} + +// MARK: - Helper SUTs +extension AnalyticsSpyTests { + + func makeAnalyticsSpyContextSUT(status: UNAuthorizationStatus = .notDetermined) -> AnalyticsSpy { + let mockSettings = MockUNNotificationSettings(authorizationStatus: status) + let mockNotificationCenter = MockAnalyticsUserNotificationCenter(mockSettings: mockSettings) + let analyticsSpy = AnalyticsSpy(notificationCenter: mockNotificationCenter) + return analyticsSpy + } + + func makeWelcomeView() -> WelcomeView { + WelcomeView(windowUUID: .XCTestDefaultUUID, onFinish: {}, onSignIn: {}) + } + + func makeInstructionsViewSUT(onButtonTap: @escaping () -> Void = {}) -> InstructionStepsView { + let style = InstructionStepsViewStyle( + backgroundPrimaryColor: .blue, + + topContentBackgroundColor: .blue, + stepsBackgroundColor: .blue, + textPrimaryColor: .blue, + textSecondaryColor: .blue, + buttonBackgroundColor: .blue, + buttonTextColor: .blue, + stepRowStyle: StepRowStyle(stepNumberColor: .blue, stepTextColor: .blue) + ) + + return InstructionStepsView( + title: .defaultBrowserCardDetailTitle, + steps: [InstructionStep(text: .defaultBrowserCardDetailInstructionStep1)], + buttonTitle: .defaultBrowserCardDetailButton, + onButtonTap: onButtonTap, + style: style + ) { + EmptyView() + } + } + + func makeWebView() -> WKWebView { + return WKWebView(frame: CGRect(width: 100, height: 100)) + } +} + +// MARK: - Helper Classes + +class MultiplyImpactTestable: MultiplyImpact { + var capturedPresentedViewController: UIViewController? + + override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { + capturedPresentedViewController = viewControllerToPresent + } +} + +final class FakeNavigationAction: WKNavigationAction { + let urlRequest: URLRequest + let type: WKNavigationType + + override var request: URLRequest { urlRequest } + + override var navigationType: WKNavigationType { type } + + init(url: URL, navigationType: WKNavigationType) { + self.urlRequest = URLRequest(url: url) + self.type = navigationType + super.init() + } +} diff --git a/firefox-ios/EcosiaTests/Analytics/AnalyticsTests.swift b/firefox-ios/EcosiaTests/Analytics/AnalyticsTests.swift new file mode 100644 index 0000000000000..1231987eddf60 --- /dev/null +++ b/firefox-ios/EcosiaTests/Analytics/AnalyticsTests.swift @@ -0,0 +1,172 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +import XCTest +@testable import Client +@testable import Ecosia + +final class AnalyticsTests: XCTestCase { + + // Cleanup method that runs after each test + override func tearDownWithError() throws { + let defaults = UserDefaults.standard + // Remove any saved dates or flags after each test to avoid side effects between tests + defaults.removeObject(forKey: "dayPassedCheckIdentifier") + defaults.removeObject(forKey: "installCheckIdentifier") + } + + // MARK: - hasDayPassedSinceLastCheck Tests + + func testFirstCheckAlwaysReturnsTrue() throws { + // Given: No previous date exists in UserDefaults for the identifier + // (Handled by setUpWithError) + + // When: The method is called for the first time + let result = Analytics.hasDayPassedSinceLastCheck(for: "dayPassedCheckIdentifier") + + // Then: The result should be true because no previous date exists + XCTAssertTrue(result, "The first check should return true because no previous date exists.") + } + + func testCheckWithinADayReturnsFalse() throws { + // Given: The current date is saved as the last check date + let defaults = UserDefaults.standard + defaults.set(Date(), forKey: "dayPassedCheckIdentifier") + + // When: The method is called within the same day + let result = Analytics.hasDayPassedSinceLastCheck(for: "dayPassedCheckIdentifier") + + // Then: The result should be false since less than a day has passed + XCTAssertFalse(result, "The check should return false if it's been less than a day since the last check.") + } + + func testDateUpdateAfterADayPasses() throws { + // Given: A date more than a day ago is saved as the last check date + let defaults = UserDefaults.standard + let moreThanADayAgo = Calendar.current.date(byAdding: .day, value: -2, to: Date())! + defaults.set(moreThanADayAgo, forKey: "dayPassedCheckIdentifier") + + // When: The method is called after more than a day has passed + let result = Analytics.hasDayPassedSinceLastCheck(for: "dayPassedCheckIdentifier") + + // Then: The method should return true and update the last check date to today + XCTAssertTrue(result, "The check should return true since more than a day has passed.") + let updatedDate = defaults.object(forKey: "dayPassedCheckIdentifier") as? Date + XCTAssertNotNil(updatedDate, "The date should be updated in UserDefaults.") + XCTAssertTrue(Calendar.current.isDateInToday(updatedDate!), "The last check date should be updated to today's date.") + } + + func testHandleCorruptedOrMissingData() throws { + // Given: Corrupted data (e.g., a string instead of a Date) is saved in UserDefaults + let defaults = UserDefaults.standard + defaults.set("corruptedData", forKey: "dayPassedCheckIdentifier") + + // When: The method is called + let result = Analytics.hasDayPassedSinceLastCheck(for: "dayPassedCheckIdentifier") + + // Then: The method should return true, treat it as the first check, and reset the last check date to today + XCTAssertTrue(result, "The method should handle corrupted data gracefully and treat it as the first check.") + let updatedDate = defaults.object(forKey: "dayPassedCheckIdentifier") as? Date + XCTAssertNotNil(updatedDate, "The date should be reset in UserDefaults after detecting corrupted data.") + XCTAssertTrue(Calendar.current.isDateInToday(updatedDate!), "The last check date should be updated to today's date after handling corrupted data.") + } + + // MARK: - isFirstInstall Tests + + func testIsFirstInstall_FirstCall_ReturnsTrue() { + // Given: No previous installation flag exists in UserDefaults for the identifier + // Also, EcosiaInstallType is not set to .upgrade + let defaults = UserDefaults.standard + defaults.removeObject(forKey: "installCheckIdentifier") + defaults.removeObject(forKey: EcosiaInstallType.installTypeKey) + + // Set EcosiaInstallType to .fresh to simulate a fresh install + EcosiaInstallType.set(type: .fresh) + + // When: The method is called for the first time + let result = Analytics.isFirstInstall(for: "installCheckIdentifier") + + // Then: The result should be true indicating the first install + XCTAssertTrue(result, "The first install should return TRUE when not an upgrade") + } + + func testIsFirstInstall_SecondCall_ReturnsFalse() { + // Given: The method has already been called once + let defaults = UserDefaults.standard + defaults.removeObject(forKey: "installCheckIdentifier") + defaults.removeObject(forKey: EcosiaInstallType.installTypeKey) + + // Set EcosiaInstallType to .fresh to simulate a fresh install + EcosiaInstallType.set(type: .fresh) + + // First call to set the flag + _ = Analytics.isFirstInstall(for: "installCheckIdentifier") + + // When: The method is called again + let result = Analytics.isFirstInstall(for: "installCheckIdentifier") + + // Then: The result should be false indicating it is no longer the first install + XCTAssertFalse(result, "The second call should return FALSE as the app is no longer on its first install") + } + + func testIsFirstInstall_StoresValueInUserDefaults() { + // Given: The method is called for the first time + let defaults = UserDefaults.standard + defaults.removeObject(forKey: "installCheckIdentifier") + defaults.removeObject(forKey: EcosiaInstallType.installTypeKey) + + // Set EcosiaInstallType to .fresh to simulate a fresh install + EcosiaInstallType.set(type: .fresh) + + _ = Analytics.isFirstInstall(for: "installCheckIdentifier") + + // When: The value is retrieved from UserDefaults + let storedValue = defaults.object(forKey: "installCheckIdentifier") as? Bool + + // Then: The stored value should be false indicating the first install has been recorded + XCTAssertNotNil(storedValue, "The value should be stored in UserDefaults") + XCTAssertEqual(storedValue, false, "The stored value in UserDefaults should be FALSE after the first call") + } + + func testIsFirstInstall_FirstCallOnUpgrade_ReturnsFalse() { + // Given: No previous installation flag exists in UserDefaults for the identifier + // Also, EcosiaInstallType is set to .upgrade + let defaults = UserDefaults.standard + defaults.removeObject(forKey: "installCheckIdentifier") + defaults.removeObject(forKey: EcosiaInstallType.installTypeKey) + + // Set EcosiaInstallType to .upgrade to simulate an upgrade scenario + EcosiaInstallType.set(type: .upgrade) + + // When: The method is called for the first time + let result = Analytics.isFirstInstall(for: "installCheckIdentifier") + + // Then: The result should be false because it's an upgrade + XCTAssertFalse(result, "The first install should return FALSE when it's an upgrade") + } + + func test_makeNetworkConfig_usesStandardEndpoint_whenShouldUseMicroIsFalse() { + Analytics.shouldUseMicroInstance = false + let config = Analytics.makeNetworkConfig(environment: .staging) + let expectedHost = URLProvider.staging.snowplow.asURL?.host + XCTAssertEqual(expectedHost, config.endpoint?.asURL?.host) + } + + func test_makeNetworkConfig_usesMicroEndpoint_whenShouldUseMicroIsTrue() { + Analytics.shouldUseMicroInstance = true + let config = Analytics.makeNetworkConfig(environment: .staging) + XCTAssertEqual(URLProvider.staging.snowplowMicro, config.endpoint) + XCTAssertEqual(config.requestHeaders?.keys.contains(CloudflareKeyProvider.clientId), true) + XCTAssertEqual(config.requestHeaders?.keys.contains(CloudflareKeyProvider.clientSecret), true) + } + + func test_makeNetworkConfig_usesProductionEndpoint_whenShouldUseMicroIsFalse_andEnvironment_isProduction() { + Analytics.shouldUseMicroInstance = false + let config = Analytics.makeNetworkConfig(environment: .production) + XCTAssertEqual(config.endpoint?.asURL?.host, "sp.ecosia.org") + XCTAssertNil(config.requestHeaders, "Production environment should not include Cloudflare authentication headers") + } +} diff --git a/firefox-ios/EcosiaTests/BrazeServiceTests.swift b/firefox-ios/EcosiaTests/BrazeServiceTests.swift new file mode 100644 index 0000000000000..0d3b642ed12cf --- /dev/null +++ b/firefox-ios/EcosiaTests/BrazeServiceTests.swift @@ -0,0 +1,92 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class BrazeServiceTests: XCTestCase { + var brazeService: BrazeService! + + override func setUp() { + super.setUp() + brazeService = BrazeService.shared + } + + override func tearDown() { + super.tearDown() + brazeService = nil + } + + func testConfigurationWithStagingEnvironment() { + // Set up your test environment for staging + let apiKey = "staging_api_key" + + // Call the configuration retrieval + do { + let brazeConfiguration = try brazeService.getBrazeConfiguration(apiKey: apiKey, environment: .staging) + + // Assert that the configuration is not nil + XCTAssertNotNil(brazeConfiguration) + + // Assert that the configuration has the expected values for staging + XCTAssertEqual(brazeConfiguration.api.key, "staging_api_key") + } catch { + // If an unexpected error occurs, fail the test + XCTFail("Unexpected error: \(error)") + } + } + + func testConfigurationWithProductionEnvironment() { + // Set up your test environment for production + let apiKey = "production_api_key" + + // Call the configuration retrieval + do { + let brazeConfiguration = try brazeService.getBrazeConfiguration(apiKey: apiKey, environment: .production) + + // Assert that the configuration is not nil + XCTAssertNotNil(brazeConfiguration) + + // Assert that the configuration has the expected values for production + XCTAssertEqual(brazeConfiguration.api.key, "production_api_key") + } catch { + // If an unexpected error occurs, fail the test + XCTFail("Unexpected error: \(error)") + } + } + + func testConfigurationWithInvalidEnvironment_InavlidApiKey() { + // Set up your test environment with missing or invalid configuration + let apiKey = "" + + // Call the configuration retrieval + do { + _ = try brazeService.getBrazeConfiguration(apiKey: apiKey, environment: .production) + + XCTFail("Expected getBrazeConfiguration to throw an error for invalid configuration") + } catch BrazeService.Error.invalidConfiguration { + // If the error is as expected, the test succeeds + } catch { + // If an unexpected error occurs, fail the test + XCTFail("Unexpected error: \(error)") + } + } + + func testConfigurationWithInvalidEnvironment_InavlidEndpoint() { + // Set up your test environment with missing or invalid configuration + let apiKey = "" + + // Call the configuration retrieval + do { + _ = try brazeService.getBrazeConfiguration(apiKey: apiKey, environment: .production) + + XCTFail("Expected getBrazeConfiguration to throw an error for invalid configuration") + } catch BrazeService.Error.invalidConfiguration { + // If the error is as expected, the test succeeds + } catch { + // If an unexpected error occurs, fail the test + XCTFail("Unexpected error: \(error)") + } + } +} diff --git a/firefox-ios/EcosiaTests/ClimateImpactCounter/UnleashUserDefaultsSeedProgressManagerTests.swift b/firefox-ios/EcosiaTests/ClimateImpactCounter/UnleashUserDefaultsSeedProgressManagerTests.swift new file mode 100644 index 0000000000000..b7c432a695b4b --- /dev/null +++ b/firefox-ios/EcosiaTests/ClimateImpactCounter/UnleashUserDefaultsSeedProgressManagerTests.swift @@ -0,0 +1,76 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia +@testable import Client + +/// Tests for local seed collection system. Logged-out users start at 0 seeds and level 1. +/// They are capped at 3 seeds and always remain at level 1 (no level progression). +final class UnleashUserDefaultsSeedProgressManagerTests: XCTestCase { + + override func setUp() { + super.setUp() + // Reset UserDefaults before each test + UserDefaults.standard.removeObject(forKey: "CurrentLevel") + UserDefaults.standard.removeObject(forKey: "TotalSeedsCollected") + UserDefaults.standard.removeObject(forKey: "LastAppOpenDate") + } + + // Test that logged-out users are capped at 3 seeds even with config override + func test_experimental_cap_respected_with_json_provided_levels() { + // Update the config to use experimental cap with JSON-provided levels + UserDefaultsSeedProgressManager.seedCounterConfig = SeedCounterConfig( + sparklesAnimationDuration: 10, + maxCappedLevel: 2, + maxCappedSeeds: 10, + levels: [ + SeedCounterConfig.SeedLevel(level: 1, requiredSeeds: 1), + SeedCounterConfig.SeedLevel(level: 2, requiredSeeds: 3), + SeedCounterConfig.SeedLevel(level: 3, requiredSeeds: 10) + ] + ) + + // Attempt to add seeds beyond the 3-seed cap for logged-out users + UserDefaultsSeedProgressManager.addSeeds(3) // +3 seeds; total: 3 seeds (capped) + + // Ensure user is capped at 3 seeds and stays at level 1 + let totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let currentLevel = UserDefaultsSeedProgressManager.loadCurrentLevel() + + XCTAssertEqual(currentLevel, 1, "User should always stay at level 1.") + XCTAssertEqual(totalSeedsCollected, 3, "Total seeds should be capped at 3 for logged-out users.") + } + + // Test that logged-out users never level up regardless of config + func test_add_seeds_with_json_provided_levels() { + // Update the config to use current Unleash's JSON-provided levels + UserDefaultsSeedProgressManager.seedCounterConfig = SeedCounterConfig( + sparklesAnimationDuration: 10, + maxCappedLevel: nil, + maxCappedSeeds: nil, + levels: [ + SeedCounterConfig.SeedLevel(level: 1, requiredSeeds: 1), + SeedCounterConfig.SeedLevel(level: 2, requiredSeeds: 3), + SeedCounterConfig.SeedLevel(level: 3, requiredSeeds: 10) + ] + ) + + // Add seeds up to the 3-seed cap + UserDefaultsSeedProgressManager.addSeeds(2) // +2 seeds; total: 2 seeds + var totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + var currentLevel = UserDefaultsSeedProgressManager.loadCurrentLevel() + + XCTAssertEqual(currentLevel, 1, "User should always stay at level 1.") + XCTAssertEqual(totalSeedsCollected, 2, "Total seeds should be 2.") + + // Add more seeds to reach 3-seed cap + UserDefaultsSeedProgressManager.addSeeds(1) // +1 seed; total: 3 seeds + totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + currentLevel = UserDefaultsSeedProgressManager.loadCurrentLevel() + + XCTAssertEqual(currentLevel, 1, "User should always stay at level 1.") + XCTAssertEqual(totalSeedsCollected, 3, "Total seeds should be capped at 3.") + } +} diff --git a/firefox-ios/EcosiaTests/ClimateImpactCounter/UserDefaultsSeedProgressManagerTests.swift b/firefox-ios/EcosiaTests/ClimateImpactCounter/UserDefaultsSeedProgressManagerTests.swift new file mode 100644 index 0000000000000..0f1907db976ab --- /dev/null +++ b/firefox-ios/EcosiaTests/ClimateImpactCounter/UserDefaultsSeedProgressManagerTests.swift @@ -0,0 +1,184 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia +@testable import Client + +/// Tests for logged-out user seed collection. Users start at 0 seeds and level 1. +final class UserDefaultsSeedProgressManagerTests: XCTestCase { + + override func setUp() { + super.setUp() + // Reset UserDefaults before each test + UserDefaults.standard.removeObject(forKey: "CurrentLevel") + UserDefaults.standard.removeObject(forKey: "TotalSeedsCollected") + UserDefaults.standard.removeObject(forKey: "LastAppOpenDate") + + // Default Seed Levels for testing (arbitrary levels) + UserDefaultsSeedProgressManager.seedCounterConfig = SeedCounterConfig( + sparklesAnimationDuration: 10, + maxCappedLevel: nil, + maxCappedSeeds: nil, + levels: [ + SeedCounterConfig.SeedLevel(level: 1, requiredSeeds: 2), + SeedCounterConfig.SeedLevel(level: 2, requiredSeeds: 3) + ] + ) + } + + // Test the initial state + func test_initial_seed_progress_state() { + // Given / When + let level = UserDefaultsSeedProgressManager.loadCurrentLevel() + let totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + + // Then + XCTAssertEqual(level, 1, "Initial level should be 1") + XCTAssertEqual(totalSeedsCollected, 0, "Initial totalSeedsCollected should be 0") + } + + // Test that logged-out users never level up + func test_logged_out_users_never_level_up() { + // Given + UserDefaultsSeedProgressManager.addSeeds(2) + + // When / Then + var level = UserDefaultsSeedProgressManager.loadCurrentLevel() + var totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + + XCTAssertEqual(level, 1) + XCTAssertEqual(totalSeedsCollected, 2) + + // When: Try to add more seeds + UserDefaultsSeedProgressManager.addSeeds(10) + + // Then: Level remains 1, seeds capped at 3 + level = UserDefaultsSeedProgressManager.loadCurrentLevel() + totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + + XCTAssertEqual(level, 1) + XCTAssertEqual(totalSeedsCollected, 3) + } + + // Test resetting the progress to first-launch state + func test_reset_local_seed_progress() { + // Given + UserDefaultsSeedProgressManager.addSeeds(2) + + // When + UserDefaultsSeedProgressManager.resetLocalSeedProgress() + + // Then + let level = UserDefaultsSeedProgressManager.loadCurrentLevel() + let totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let lastAppOpenDate = UserDefaultsSeedProgressManager.loadLastAppOpenDate() + + XCTAssertEqual(level, 1) + XCTAssertEqual(totalSeedsCollected, 0) + XCTAssertNil(lastAppOpenDate, "Last app open date should be cleared to allow immediate seed collection") + } + + // Test collecting a seed once per day + func test_collect_seed_once_per_day() { + // Given: Start with 0 seeds + let initialSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + XCTAssertEqual(initialSeeds, 0) + + // When: Collect seed on first day + UserDefaultsSeedProgressManager.collectDailySeed() + let totalSeedsAfterFirstCollect = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + + // When: Try to collect again same day + UserDefaultsSeedProgressManager.collectDailySeed() + let totalSeedsAfterSecondCollect = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + + // Then: First collect should add 1, second should do nothing + XCTAssertEqual(totalSeedsAfterFirstCollect, 1) + XCTAssertEqual(totalSeedsAfterSecondCollect, 1) + } + + // Test that a seed can be collected the next day but stays at level 1 + func test_collect_seed_next_day_stays_level_1() { + // Given / When + UserDefaultsSeedProgressManager.collectDailySeed() + var totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + XCTAssertEqual(totalSeedsCollected, 1) + + let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) + UserDefaults.standard.set(yesterday, forKey: "LastAppOpenDate") + UserDefaultsSeedProgressManager.collectDailySeed() + + // Then + totalSeedsCollected = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let level = UserDefaultsSeedProgressManager.loadCurrentLevel() + + XCTAssertEqual(totalSeedsCollected, 2) + XCTAssertEqual(level, 1) + } + + // Test that logged-out users are capped at 3 seeds and always remain at level 1 + func test_logged_out_users_capped_at_max_seeds_and_level_1() { + // Given + let initialSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let initialLevel = UserDefaultsSeedProgressManager.loadCurrentLevel() + XCTAssertEqual(initialSeeds, 0) + XCTAssertEqual(initialLevel, 1) + + // When + UserDefaultsSeedProgressManager.addSeeds(3) + + // Then + var totalSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + var level = UserDefaultsSeedProgressManager.loadCurrentLevel() + XCTAssertEqual(totalSeeds, UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers) + XCTAssertEqual(level, 1) + + // When + UserDefaultsSeedProgressManager.addSeeds(5) + + // Then + totalSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + level = UserDefaultsSeedProgressManager.loadCurrentLevel() + XCTAssertEqual(totalSeeds, UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers) + XCTAssertEqual(level, 1) + } + + // Test that bulk seed addition caps at 3 seeds and level remains 1 + func test_logged_out_users_bulk_addition_caps_at_3_seeds_level_1() { + // Given + let initialSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + XCTAssertEqual(initialSeeds, 0) + + // When + UserDefaultsSeedProgressManager.addSeeds(10) + + // Then + let totalSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + let level = UserDefaultsSeedProgressManager.loadCurrentLevel() + XCTAssertEqual(totalSeeds, UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers) + XCTAssertEqual(level, 1) + } + + // Test that daily seed collection respects cap and level remains 1 + func test_logged_out_users_daily_seed_respects_cap_and_level_1() { + // Given + UserDefaultsSeedProgressManager.addSeeds(3) + var totalSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + var level = UserDefaultsSeedProgressManager.loadCurrentLevel() + XCTAssertEqual(totalSeeds, 3) + XCTAssertEqual(level, 1) + + // When + let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) + UserDefaults.standard.set(yesterday, forKey: "LastAppOpenDate") + UserDefaultsSeedProgressManager.collectDailySeed() + + // Then + totalSeeds = UserDefaultsSeedProgressManager.loadTotalSeedsCollected() + level = UserDefaultsSeedProgressManager.loadCurrentLevel() + XCTAssertEqual(totalSeeds, UserDefaultsSeedProgressManager.maxSeedsForLoggedOutUsers) + XCTAssertEqual(level, 1) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Accounts/AccountsServiceTests.swift b/firefox-ios/EcosiaTests/Core/Accounts/AccountsServiceTests.swift new file mode 100644 index 0000000000000..e4f6b702893d7 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Accounts/AccountsServiceTests.swift @@ -0,0 +1,368 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class AccountsServiceTests: XCTestCase { + + private var mockHTTPClient: HTTPClientMock! + private var accountsService: AccountsService! + + override func setUp() { + super.setUp() + mockHTTPClient = HTTPClientMock() + accountsService = AccountsService(client: mockHTTPClient) + } + + func testRegisterVisit_Success() async throws { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let expectedResponse = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 5, + totalAmount: 5, + previousTotalAmount: 4, + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 75, + level: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 25 + ), + previousLevel: AccountVisitResponse.Level( + number: 1, + totalGrowthPointsRequired: 0, + seedsRewardedForLevelUp: 1, + growthPointsToUnlockNextLevel: 75, + growthPointsEarnedTowardsNextLevel: 75 + ), + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + let responseData = try JSONEncoder().encode(expectedResponse) + mockHTTPClient.data = responseData + mockHTTPClient.response = HTTPURLResponse( + url: URL(string: "https://example.com")!, + statusCode: 200, + httpVersion: nil, + headerFields: nil + ) + + // Act + let response = try await accountsService.registerVisit(accessToken: "test-access-token") + + // Assert + XCTAssertEqual(response.seeds.totalAmount, 5) + XCTAssertEqual(response.seeds.isModified, true) + XCTAssertEqual(response.seeds.previousTotalAmount, 4) + XCTAssertEqual(response.seedsIncrement, 1) + XCTAssertEqual(response.growthPoints.level.number, 2) + XCTAssertTrue(response.didLevelUp) + XCTAssertEqual(mockHTTPClient.requests.count, 1) + + let request = mockHTTPClient.requests.first as? AccountVisitRequest + XCTAssertNotNil(request) + XCTAssertEqual(request?.additionalHeaders?["Authorization"], "Bearer test-access-token") + } + + func testRegisterVisit_NetworkError() async throws { + // Arrange + mockHTTPClient.response = HTTPURLResponse( + url: URL(string: "https://example.com")!, + statusCode: 500, + httpVersion: nil, + headerFields: nil + ) + + // Act & Assert + do { + _ = try await accountsService.registerVisit(accessToken: "test-access-token") + XCTFail("Expected network error") + } catch AccountsService.Error.network { + // Expected error + } catch { + XCTFail("Unexpected error: \(error)") + } + } + + func testSeedsIncrement_NoChange() { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 5, + totalAmount: 5, + previousTotalAmount: 5, + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 100, + level: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 25 + ), + previousLevel: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 25 + ), + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // Act & Assert + XCTAssertNil(response.seedsIncrement) + } + + func testSeedsIncrement_WithChange() { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 8, + totalAmount: 8, + previousTotalAmount: 5, + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 75, + level: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 25 + ), + previousLevel: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 0 + ), + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // Act & Assert + XCTAssertEqual(response.seedsIncrement, 3) + } + + func testRegisterVisit_UnauthorizedError_ThrowsUnauthorized() async throws { + // This test verifies that a 401 Unauthorized response throws an `.unauthorized` error. + // With the new proactive token refresh approach, 401s only occur for genuine auth failures + // (revoked token, logged out on server, etc.) since expired tokens are refreshed before the request. + + // Arrange + mockHTTPClient.response = HTTPURLResponse( + url: URL(string: "https://example.com")!, + statusCode: 401, + httpVersion: nil, + headerFields: nil + ) + + // Act & Assert + do { + _ = try await accountsService.registerVisit(accessToken: "invalid-token") + XCTFail("Expected unauthorized error") + } catch AccountsService.Error.unauthorized { + // Expected error - 401 now indicates a genuine auth failure + } catch { + XCTFail("Unexpected error: \(error)") + } + } + + func testGrowthPointsIncrement() { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 5, + totalAmount: 5, + previousTotalAmount: 4, + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 200, + totalAmount: 200, + previousTotalAmount: 175, + level: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 125 + ), + previousLevel: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 100 + ), + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // Act & Assert + XCTAssertEqual(response.growthPointsIncrement, 25) + } + + func testLevelUp_Detection() { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 10, + totalAmount: 10, + previousTotalAmount: 8, + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 200, + totalAmount: 200, + previousTotalAmount: 175, + level: AccountVisitResponse.Level( + number: 3, + totalGrowthPointsRequired: 150, + seedsRewardedForLevelUp: 3, + growthPointsToUnlockNextLevel: 250, + growthPointsEarnedTowardsNextLevel: 50 + ), + previousLevel: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 100 + ), + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // Act & Assert + XCTAssertTrue(response.didLevelUp) + XCTAssertEqual(response.growthPoints.level.number, 3) + XCTAssertEqual(response.growthPoints.previousLevel.number, 2) + } + + func testProgressToNextLevel_Calculation() { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 5, + totalAmount: 5, + previousTotalAmount: 5, + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 100, + level: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 125 + ), + previousLevel: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 125 + ), + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // Act + let progress = response.progressToNextLevel + + // Assert + XCTAssertEqual(progress, 125.0 / 175.0, accuracy: 0.01) + } + + func testGrowthPointsIncrement_NoChange() { + // Arrange + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 5, + totalAmount: 5, + previousTotalAmount: 5, + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 100, + level: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 125 + ), + previousLevel: AccountVisitResponse.Level( + number: 2, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: 175, + growthPointsEarnedTowardsNextLevel: 125 + ), + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // Act & Assert + XCTAssertNil(response.growthPointsIncrement) + } +} diff --git a/firefox-ios/EcosiaTests/Core/BookmarkParserTests.swift b/firefox-ios/EcosiaTests/Core/BookmarkParserTests.swift new file mode 100644 index 0000000000000..5f83ac431cd0a --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/BookmarkParserTests.swift @@ -0,0 +1,41 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class BookmarkParserTests: XCTestCase { + func testParsesFolderAndBookmarks_Chrome() async throws { + let html = BookmarkFixtures.html(.chrome).value + + let importer = try BookmarkParser(html: html) + let bookmarks = try await importer.parseBookmarks() + + let expectedResult = BookmarkFixtures.debugString(.chrome).value + + XCTAssertEqual(bookmarks.debugDescription, expectedResult) + } + + func testParsesFolderAndBookmarks_Firefox() async throws { + let html = BookmarkFixtures.html(.firefox).value + + let importer = try BookmarkParser(html: html) + let bookmarks = try await importer.parseBookmarks() + + let expectedResult = BookmarkFixtures.debugString(.firefox).value + + XCTAssertEqual(bookmarks.debugDescription, expectedResult) + } + + func testParsesFolderAndBookmarks_Safari() async throws { + let html = BookmarkFixtures.html(.safari).value + + let importer = try BookmarkParser(html: html) + let bookmarks = try await importer.parseBookmarks() + + let expectedResult = BookmarkFixtures.debugString(.safari).value + + XCTAssertEqual(bookmarks.debugDescription, expectedResult) + } +} diff --git a/firefox-ios/EcosiaTests/Core/BookmarkSerializerTests.swift b/firefox-ios/EcosiaTests/Core/BookmarkSerializerTests.swift new file mode 100644 index 0000000000000..2e9eb3adfca8f --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/BookmarkSerializerTests.swift @@ -0,0 +1,80 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class BookmarkSerializerTests: XCTestCase { + func test_SerializesBookmarks() async throws { + let bookmarks: [BookmarkItem] = [ + .folder( + "Favorites", + [ + .bookmark( + "One", + "https://example.com/one", + .empty + ), + .bookmark( + "Two", + "https://example.com/two", + .empty + ), + .folder( + "My Folder #1", + [ + .bookmark( + "Three", + "https://example.com/three", + .init( + addedAt: .init(timeIntervalSince1970: 123), + modifiedAt: nil + ) + ) + ], + .empty + ), + .folder( + "My Folder #2", + [ + .bookmark( + "Four", + "https://example.com/four", + .init( + addedAt: .init(timeIntervalSince1970: 456), + modifiedAt: nil + ) + ), + .folder( + "My Subfolder #1", + [ + .bookmark( + "Five", + "https://example.com/five", + .init( + addedAt: .init(timeIntervalSince1970: 789), + modifiedAt: .init(timeIntervalSince1970: 987) + ) + ) + ], + .empty + ) + ], + .empty + ) + ], + .empty + ) + ] + + let html = try await BookmarkSerializer() + .serializeBookmarks(bookmarks) + .filter { !$0.isWhitespace } + + XCTAssertEqual( + html, + BookmarkFixtures.ecosiaExportedHtml.filter { !$0.isWhitespace } + ) + } +} diff --git a/firefox-ios/EcosiaTests/Core/BookmarkTests.swift b/firefox-ios/EcosiaTests/Core/BookmarkTests.swift new file mode 100644 index 0000000000000..0a956c92ae6c1 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/BookmarkTests.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class BookmarkTests: XCTestCase { + func test_BookmarkSerializer_Parser() async throws { + let input: [BookmarkItem] = [ + .bookmark("One", "https://example.com/one", .empty), + .folder("My first folder 😆", + [ + .bookmark("Two &!/{}", "https://example.com/two", .empty), + .bookmark("Three ÖÄÜ'*", "https://example.com/three", .empty) + ], .empty) + ] + + let html = try await BookmarkSerializer().serializeBookmarks(input) + + let output = try await BookmarkParser(html: html).parseBookmarks() + + XCTAssertEqual(input, output) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Bundle+EcosiaTests.swift b/firefox-ios/EcosiaTests/Core/Bundle+EcosiaTests.swift new file mode 100644 index 0000000000000..b5f340e9e1877 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Bundle+EcosiaTests.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension Bundle { + static var ecosiaTests: Bundle { + Bundle(identifier: "com.ecosia.framework.EcosiaTests")! + } +} diff --git a/firefox-ios/EcosiaTests/Core/Cookie/AIOverviewsCookieHandlerTests.swift b/firefox-ios/EcosiaTests/Core/Cookie/AIOverviewsCookieHandlerTests.swift new file mode 100644 index 0000000000000..ed332157dccb0 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Cookie/AIOverviewsCookieHandlerTests.swift @@ -0,0 +1,131 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest +import WebKit + +final class AIOverviewsCookieHandlerTests: XCTestCase { + + override func setUp() { + super.setUp() + Cookie.setURLProvider(.production) + User.shared.aiOverviews = false + } + + override func tearDown() { + super.tearDown() + try? FileManager.default.removeItem(at: FileManager.user) + Cookie.resetURLProvider() + } + + func testMakeCookieWithAIOverviewsEnabled() { + User.shared.aiOverviews = true + + let handler = AIOverviewsCookieHandler() + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create AI overviews cookie") + return + } + + XCTAssertEqual(cookie.name, "ECAIO") + XCTAssertEqual(cookie.value, "true") + XCTAssertEqual(cookie.domain, ".ecosia.org") + XCTAssertEqual(cookie.path, "/") + } + + func testMakeCookieWithAIOverviewsDisabled() { + User.shared.aiOverviews = false + + let handler = AIOverviewsCookieHandler() + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create AI overviews cookie") + return + } + + XCTAssertEqual(cookie.name, "ECAIO") + XCTAssertEqual(cookie.value, "false") + XCTAssertEqual(cookie.domain, ".ecosia.org") + XCTAssertEqual(cookie.path, "/") + } + + func testReceivedMethodWithTrueValue() { + let handler = AIOverviewsCookieHandler() + let cookie = HTTPCookie(properties: [ + .name: "ECAIO", + .domain: ".ecosia.org", + .path: "/", + .value: "true" + ])! + + handler.received(cookie, in: MockHTTPCookieStore()) + XCTAssertTrue(User.shared.aiOverviews) + } + + func testReceivedMethodWithFalseValue() { + User.shared.aiOverviews = true // Start with true to verify it changes + + let handler = AIOverviewsCookieHandler() + let cookie = HTTPCookie(properties: [ + .name: "ECAIO", + .domain: ".ecosia.org", + .path: "/", + .value: "false" + ])! + + handler.received(cookie, in: MockHTTPCookieStore()) + XCTAssertFalse(User.shared.aiOverviews) + } + + func testReceivedMethodWithInvalidValue() { + User.shared.aiOverviews = true // Start with true to verify it changes to false for invalid values + + let handler = AIOverviewsCookieHandler() + let testValues = ["invalid", "1", "0", "", "yes", "no", "enabled", "disabled"] + + for value in testValues { + let cookie = HTTPCookie(properties: [ + .name: "ECAIO", + .domain: ".ecosia.org", + .path: "/", + .value: value + ])! + + handler.received(cookie, in: MockHTTPCookieStore()) + XCTAssertFalse(User.shared.aiOverviews, "Invalid value '\(value)' should result in false") + } + } + + func testCookieNameIsCorrect() { + let handler = AIOverviewsCookieHandler() + XCTAssertEqual(handler.cookieName, "ECAIO") + } + + // MARK: - Integration Tests + + func testAIOverviewsCookieInRequiredCookies() { + User.shared.aiOverviews = true + + let cookies = Cookie.makeRequiredCookies(isPrivate: false) + let aiOverviewsCookies = cookies.filter { $0.name == "ECAIO" } + + XCTAssertEqual(aiOverviewsCookies.count, 1) + XCTAssertEqual(aiOverviewsCookies.first?.value, "true") + } + + func testAIOverviewsCookieWorksInBothPrivateModes() { + User.shared.aiOverviews = true + + let standardCookies = Cookie.makeRequiredCookies(isPrivate: false) + let privateCookies = Cookie.makeRequiredCookies(isPrivate: true) + + let standardAICookie = standardCookies.first { $0.name == "ECAIO" } + let privateAICookie = privateCookies.first { $0.name == "ECAIO" } + + XCTAssertNotNil(standardAICookie) + XCTAssertNotNil(privateAICookie) + XCTAssertEqual(standardAICookie?.value, "true") + XCTAssertEqual(privateAICookie?.value, "true") + } +} diff --git a/firefox-ios/EcosiaTests/Core/Cookie/ConsentCookieHandlerTests.swift b/firefox-ios/EcosiaTests/Core/Cookie/ConsentCookieHandlerTests.swift new file mode 100644 index 0000000000000..650bff28d1e63 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Cookie/ConsentCookieHandlerTests.swift @@ -0,0 +1,137 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest +import WebKit + +final class ConsentCookieHandlerTests: XCTestCase { + + override func setUp() { + super.setUp() + Cookie.setURLProvider(.production) + + User.shared.cookieConsentValue = nil + } + + override func tearDown() { + super.tearDown() + try? FileManager.default.removeItem(at: FileManager.user) + Cookie.resetURLProvider() + } + + func testMakeCookieWithConsentValue() { + User.shared.cookieConsentValue = "eampg" + + let handler = ConsentCookieHandler() + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create consent cookie") + return + } + + XCTAssertEqual(cookie.name, "ECCC") + XCTAssertEqual(cookie.value, "eampg") + XCTAssertEqual(cookie.domain, ".ecosia.org") + XCTAssertEqual(cookie.path, "/") + } + + func testMakeCookieWithoutConsentValue() { + User.shared.cookieConsentValue = nil + + let handler = ConsentCookieHandler() + let cookie = handler.makeCookie() + + XCTAssertNil(cookie, "Cookie should not be created when consent value is nil") + } + + func testMakeCookieWithEmptyConsentValue() { + User.shared.cookieConsentValue = "" + + let handler = ConsentCookieHandler() + let cookie = handler.makeCookie() + + XCTAssertEqual(cookie?.value, "") + } + + func testReceivedMethodWithVariousConsentStrings() { + let handler = ConsentCookieHandler() + let testValues = [ + "e", + "eampg", + "ea", + "eamp", + "custom123" + ] + + for value in testValues { + let cookie = HTTPCookie(properties: [ + .name: "ECCC", + .domain: ".ecosia.org", + .path: "/", + .value: value + ])! + + handler.received(cookie, in: MockHTTPCookieStore()) + XCTAssertEqual(User.shared.cookieConsentValue, value) + } + } + + func testCookieNameIsCorrect() { + let handler = ConsentCookieHandler() + XCTAssertEqual(handler.cookieName, "ECCC") + } + + // MARK: - Integration Tests + + func testConsentCookieInRequiredCookies() { + User.shared.cookieConsentValue = "eampg" + + let cookies = Cookie.makeRequiredCookies(isPrivate: false) + let consentCookies = cookies.filter { $0.name == "ECCC" } + + XCTAssertEqual(consentCookies.count, 1) + XCTAssertEqual(consentCookies.first?.value, "eampg") + } + + func testConsentCookieNotInRequiredCookiesWhenNil() { + User.shared.cookieConsentValue = nil + + let cookies = Cookie.makeRequiredCookies(isPrivate: false) + let consentCookies = cookies.filter { $0.name == "ECCC" } + + XCTAssertEqual(consentCookies.count, 0) + } + + // MARK: - Analytics Consent Detection Tests + + func testAnalyticsConsentDetection() { + let handler = ConsentCookieHandler() + + // Test values that should indicate analytics consent + let eampgCookie = HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: "eampg"])! + handler.received(eampgCookie, in: MockHTTPCookieStore()) + XCTAssertTrue(User.shared.hasAnalyticsCookieConsent) + + let eampCookie = HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: "eamp"])! + handler.received(eampCookie, in: MockHTTPCookieStore()) + XCTAssertTrue(User.shared.hasAnalyticsCookieConsent) + + let eaCookie = HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: "ea"])! + handler.received(eaCookie, in: MockHTTPCookieStore()) + XCTAssertTrue(User.shared.hasAnalyticsCookieConsent) + + // Test values that should NOT indicate analytics consent + let eCookie = HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: "e"])! + handler.received(eCookie, in: MockHTTPCookieStore()) + XCTAssertFalse(User.shared.hasAnalyticsCookieConsent) + + let empgCookie = HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: "empg"])! + handler.received(empgCookie, in: MockHTTPCookieStore()) + XCTAssertFalse(User.shared.hasAnalyticsCookieConsent) + + let emptyCookie = HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: ""])! + handler.received(emptyCookie, in: MockHTTPCookieStore()) + XCTAssertFalse(User.shared.hasAnalyticsCookieConsent) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Cookie/CookieTests.swift b/firefox-ios/EcosiaTests/Core/Cookie/CookieTests.swift new file mode 100644 index 0000000000000..9ab5892eff21b --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Cookie/CookieTests.swift @@ -0,0 +1,167 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest +import WebKit + +final class CookieTests: XCTestCase { + + var urlProvider: URLProvider = .production + + override func setUp() { + super.setUp() + try? FileManager.default.removeItem(at: FileManager.user) + Cookie.setURLProvider(urlProvider) + } + + override func tearDown() { + super.tearDown() + try? FileManager.default.removeItem(at: FileManager.user) + Cookie.resetURLProvider() + } + + // MARK: - Cookie Initialization Tests + + func testCookieInitFromHTTPCookie() { + let validCookie = HTTPCookie(properties: [.name: "ECFG", .domain: ".ecosia.org", .path: "/", .value: "test"])! + let invalidDomainCookie = HTTPCookie(properties: [.name: "ECFG", .domain: ".google.com", .path: "/", .value: "test"])! + let invalidNameCookie = HTTPCookie(properties: [.name: "INVALID", .domain: ".ecosia.org", .path: "/", .value: "test"])! + + XCTAssertNotNil(Cookie(validCookie)) + XCTAssertEqual(Cookie(validCookie), .main) + + XCTAssertNil(Cookie(invalidDomainCookie)) + XCTAssertNil(Cookie(invalidNameCookie)) + } + + func testCookieInitFromString() { + XCTAssertEqual(Cookie("ECFG"), .main) + XCTAssertEqual(Cookie("ECCC"), .consent) + XCTAssertEqual(Cookie("ECUNL"), .unleash) + XCTAssertEqual(Cookie("ECAIO"), .aiOverviews) + XCTAssertNil(Cookie("INVALID")) + } + + func testCookieName() { + XCTAssertEqual(Cookie.main.name, "ECFG") + XCTAssertEqual(Cookie.consent.name, "ECCC") + XCTAssertEqual(Cookie.unleash.name, "ECUNL") + XCTAssertEqual(Cookie.aiOverviews.name, "ECAIO") + } + + // MARK: - Cookie Processing Tests + + func testReceivedCookiesProcessing() { + // Setup initial state + User.shared.searchCount = 0 + User.shared.cookieConsentValue = nil + + let cookies = [ + HTTPCookie(properties: [.name: "ECFG", .domain: ".ecosia.org", .path: "/", .value: "t=100:cid=test-user"])!, + HTTPCookie(properties: [.name: "ECCC", .domain: ".ecosia.org", .path: "/", .value: "eampg"])!, + HTTPCookie(properties: [.name: "ECUNL", .domain: ".ecosia.org", .path: "/", .value: "test-unleash-id"])!, + HTTPCookie(properties: [.name: "ECAIO", .domain: ".ecosia.org", .path: "/", .value: "enabled"])! + ] + + Cookie.received(cookies, in: MockHTTPCookieStore()) + + // Verify effects through public observable state + XCTAssertEqual(User.shared.searchCount, 100) + XCTAssertEqual(User.shared.id, "test-user") + XCTAssertEqual(User.shared.cookieConsentValue, "eampg") + } + + func testReceivedCookiesIgnoresInvalidDomain() { + User.shared.searchCount = 5 + + let invalidDomainCookies = [ + HTTPCookie(properties: [.name: "ECFG", .domain: ".google.com", .path: "/", .value: "t=9999"])!, + HTTPCookie(properties: [.name: "ECCC", .domain: ".bing.com", .path: "/", .value: "invalid"])! + ] + + Cookie.received(invalidDomainCookies, in: MockHTTPCookieStore()) + + // State should remain unchanged + XCTAssertEqual(User.shared.searchCount, 5) + } + + func testReceivedCookiesIgnoresInvalidNames() { + User.shared.searchCount = 5 + + let invalidNameCookies = [ + HTTPCookie(properties: [.name: "UNKNOWN", .domain: ".ecosia.org", .path: "/", .value: "t=9999"])!, + HTTPCookie(properties: [.name: "INVALID", .domain: ".ecosia.org", .path: "/", .value: "test"])! + ] + + Cookie.received(invalidNameCookies, in: MockHTTPCookieStore()) + + // State should remain unchanged + XCTAssertEqual(User.shared.searchCount, 5) + } + + // MARK: - Cookie Creation Tests + + func testMakeRequiredCookies() async { + // Setup prerequisites + _ = try? await Unleash.start(appVersion: "1.0.0") + User.shared.cookieConsentValue = "eampg" + + let standardCookies = Cookie.makeRequiredCookies(isPrivate: false) + let privateCookies = Cookie.makeRequiredCookies(isPrivate: true) + + // Verify all expected cookie types are present + let expectedCookieNames = [Cookie.main.name, Cookie.consent.name, Cookie.unleash.name, Cookie.aiOverviews.name] + + for cookieName in expectedCookieNames { + XCTAssertTrue(standardCookies.contains { $0.name == cookieName }, "Standard cookies missing \(cookieName)") + XCTAssertTrue(privateCookies.contains { $0.name == cookieName }, "Private cookies missing \(cookieName)") + } + + // Verify domains are correct + for cookie in standardCookies + privateCookies { + XCTAssertEqual(cookie.domain, ".ecosia.org") + } + } + + func testMakeSearchSettingsObserverCookies() async { + // Setup prerequisites + _ = try? await Unleash.start(appVersion: "1.0.0") + User.shared.cookieConsentValue = "eampg" + + let standardCookies = Cookie.makeSearchSettingsObserverCookies(isPrivate: false) + let privateCookies = Cookie.makeSearchSettingsObserverCookies(isPrivate: true) + + // Should only contain main and aiOverviews cookies + let expectedCookieNames = [Cookie.main.name, Cookie.aiOverviews.name] + let unexpectedCookieNames = [Cookie.consent.name, Cookie.unleash.name] + + for cookieName in expectedCookieNames { + XCTAssertTrue(standardCookies.contains { $0.name == cookieName }, "Standard search settings cookies missing \(cookieName)") + XCTAssertTrue(privateCookies.contains { $0.name == cookieName }, "Private search settings cookies missing \(cookieName)") + } + + for cookieName in unexpectedCookieNames { + XCTAssertFalse(standardCookies.contains { $0.name == cookieName }, "Standard search settings cookies should not contain \(cookieName)") + XCTAssertFalse(privateCookies.contains { $0.name == cookieName }, "Private search settings cookies should not contain \(cookieName)") + } + + // Verify domains are correct + for cookie in standardCookies + privateCookies { + XCTAssertEqual(cookie.domain, ".ecosia.org") + } + } +} + +// MARK: - Helper Methods +extension CookieTests { + + /// Helper method to parse main cookie value into dictionary + private func parseMainCookieValue(_ value: String) -> [String: String] { + return value.components(separatedBy: ":").map { $0.components(separatedBy: "=") }.reduce(into: [String: String]()) { + guard $1.count == 2 else { return } + $0[$1[0]] = $1[1] + } + } +} diff --git a/firefox-ios/EcosiaTests/Core/Cookie/MainCookieHandlerTests.swift b/firefox-ios/EcosiaTests/Core/Cookie/MainCookieHandlerTests.swift new file mode 100644 index 0000000000000..94e9a6775c37c --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Cookie/MainCookieHandlerTests.swift @@ -0,0 +1,226 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest +import WebKit + +final class MainCookieHandlerTests: XCTestCase { + + override func setUp() { + super.setUp() + Cookie.setURLProvider(.production) + + User.shared.searchCount = 0 + User.shared.id = nil + User.shared.marketCode = .en_us + User.shared.adultFilter = .off + User.shared.autoComplete = true + User.shared.personalized = false + } + + override func tearDown() { + super.tearDown() + try? FileManager.default.removeItem(at: FileManager.user) + Cookie.resetURLProvider() + } + + // MARK: - Standard Mode Tests + + func testStandardModeIncludesUserData() { + User.shared.id = "test-user-id" + User.shared.searchCount = 42 + + let handler = MainCookieHandler(mode: .standard) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create standard cookie") + return + } + + let values = parseMainCookieValue(cookie.value) + XCTAssertEqual(values["cid"], "test-user-id") + XCTAssertEqual(values["t"], "42") + } + + func testStandardModeWithoutUserData() { + User.shared.id = nil + User.shared.searchCount = 0 + + let handler = MainCookieHandler(mode: .standard) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create standard cookie") + return + } + + let values = parseMainCookieValue(cookie.value) + XCTAssertNil(values["cid"]) + XCTAssertEqual(values["t"], "0") + } + + // MARK: - Incognito Mode Tests + + func testIncognitoModeExcludesUserData() { + User.shared.id = "test-user-id" + User.shared.searchCount = 42 + + let handler = MainCookieHandler(mode: .incognito) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create incognito cookie") + return + } + + let values = parseMainCookieValue(cookie.value) + XCTAssertNil(values["cid"]) + XCTAssertNil(values["t"]) + } + + func testIncognitoModeIncludesBaseValues() { + User.shared.marketCode = .de_de + User.shared.adultFilter = .strict + User.shared.autoComplete = false + User.shared.personalized = true + + let handler = MainCookieHandler(mode: .incognito) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create incognito cookie") + return + } + + let values = parseMainCookieValue(cookie.value) + XCTAssertEqual(values["mc"], "de-de") + XCTAssertEqual(values["f"], "y") + XCTAssertEqual(values["as"], "0") + XCTAssertEqual(values["pz"], "1") + } + + // MARK: - Cookie Properties Tests + + func testCookieBasicProperties() { + let handler = MainCookieHandler(mode: .standard) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create cookie") + return + } + + XCTAssertEqual(cookie.name, "ECFG") + XCTAssertEqual(cookie.domain, ".ecosia.org") + XCTAssertEqual(cookie.path, "/") + } + + // MARK: - Base Values Tests + + func testBaseValuesWithUserSettings() { + User.shared.marketCode = .es_cl + User.shared.adultFilter = .moderate + User.shared.autoComplete = false + User.shared.personalized = true + + let handler = MainCookieHandler(mode: .standard) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create cookie") + return + } + + let values = parseMainCookieValue(cookie.value) + XCTAssertEqual(values["mc"], "es-cl") + XCTAssertEqual(values["f"], "i") + XCTAssertEqual(values["as"], "0") + XCTAssertEqual(values["pz"], "1") + XCTAssertEqual(values["l"], Language.current.rawValue) + XCTAssertEqual(values["ma"], "1") + XCTAssertEqual(values["mr"], "1") + XCTAssertEqual(values["dt"], "mobile") + XCTAssertEqual(values["fs"], "0") + XCTAssertEqual(values["a"], "1") + } + + // MARK: - Extract Value Tests + + func testReceivedMethodUpdatesUser() { + let handler = MainCookieHandler() + let cookie = HTTPCookie(properties: [ + .name: "ECFG", + .domain: ".ecosia.org", + .path: "/", + .value: "cid=new-user-id:t=100:mc=fr-fr:f=y:as=1:pz=0" + ])! + + handler.received(cookie, in: MockHTTPCookieStore()) + + XCTAssertEqual(User.shared.id, "new-user-id") + XCTAssertEqual(User.shared.searchCount, 100) + XCTAssertEqual(User.shared.marketCode, .fr_fr) + XCTAssertEqual(User.shared.adultFilter, .strict) + XCTAssertTrue(User.shared.autoComplete) + XCTAssertFalse(User.shared.personalized) + } + + func testReceivedMethodWithInvalidValues() { + User.shared.searchCount = 50 + User.shared.marketCode = .en_us + User.shared.adultFilter = .off + + let handler = MainCookieHandler() + let cookie = HTTPCookie(properties: [ + .name: "ECFG", + .domain: ".ecosia.org", + .path: "/", + .value: "t=invalid:mc=invalid-market:f=invalid-filter" + ])! + + handler.received(cookie, in: MockHTTPCookieStore()) + + // Should maintain existing values when invalid data is received + XCTAssertEqual(User.shared.searchCount, 50) + XCTAssertEqual(User.shared.marketCode, .en_us) + XCTAssertEqual(User.shared.adultFilter, .off) + } + + func testReceivedMethodTreeCountOnlyIncreasesOrResetsToZero() { + User.shared.searchCount = 50 + + let handler = MainCookieHandler() + + // Should not decrease + let decreaseCookie = HTTPCookie(properties: [ + .name: "ECFG", + .domain: ".ecosia.org", + .path: "/", + .value: "t=30" + ])! + handler.received(decreaseCookie, in: MockHTTPCookieStore()) + XCTAssertEqual(User.shared.searchCount, 50) + + // Should increase + let increaseCookie = HTTPCookie(properties: [ + .name: "ECFG", + .domain: ".ecosia.org", + .path: "/", + .value: "t=75" + ])! + handler.received(increaseCookie, in: MockHTTPCookieStore()) + XCTAssertEqual(User.shared.searchCount, 75) + + // Should reset to zero + let resetCookie = HTTPCookie(properties: [ + .name: "ECFG", + .domain: ".ecosia.org", + .path: "/", + .value: "t=0" + ])! + handler.received(resetCookie, in: MockHTTPCookieStore()) + XCTAssertEqual(User.shared.searchCount, 0) + } +} + +// MARK: - Helper Methods +extension MainCookieHandlerTests { + + private func parseMainCookieValue(_ value: String) -> [String: String] { + return value.components(separatedBy: ":").map { $0.components(separatedBy: "=") }.reduce(into: [String: String]()) { + guard $1.count == 2 else { return } + $0[$1[0]] = $1[1] + } + } +} diff --git a/firefox-ios/EcosiaTests/Core/Cookie/UnleashCookieHandlerTests.swift b/firefox-ios/EcosiaTests/Core/Cookie/UnleashCookieHandlerTests.swift new file mode 100644 index 0000000000000..421ef0aaff538 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Cookie/UnleashCookieHandlerTests.swift @@ -0,0 +1,141 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest +import WebKit + +final class UnleashCookieHandlerTests: XCTestCase { + + var mockCookieStore: MockHTTPCookieStore! + + override func setUp() { + super.setUp() + Cookie.setURLProvider(.production) + MockUnleash.setLoaded(true) + + mockCookieStore = MockHTTPCookieStore() + } + + override func tearDown() { + super.tearDown() + Cookie.resetURLProvider() + MockUnleash.reset() + } + + func testNoCookieWithoutLoadingUnleash() { + MockUnleash.setLoaded(false) + let handler = UnleashCookieHandler(unleash: MockUnleash.self) + XCTAssertNil(handler.makeCookie()) + } + + func testMakeCookieCreatesValidCookie() { + let handler = UnleashCookieHandler(unleash: MockUnleash.self) + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create unleash cookie when mock is loaded") + return + } + + XCTAssertEqual(cookie.name, "ECUNL") + XCTAssertEqual(cookie.domain, ".ecosia.org") + XCTAssertEqual(cookie.path, "/") + + XCTAssertFalse(cookie.value.isEmpty) + XCTAssertEqual(cookie.value, cookie.value.lowercased()) + } + + func testMultipleCookiesHaveSameId() { + let handler = UnleashCookieHandler(unleash: MockUnleash.self) + + let cookie1 = handler.makeCookie() + let cookie2 = handler.makeCookie() + + XCTAssertNotNil(cookie1) + XCTAssertNotNil(cookie2) + XCTAssertEqual(cookie1?.value, cookie2?.value) + } + + // MARK: - Received Value Tests + + func testReceivedMethodOverridesCookieButOnlyIfDifferent() async { + // First case: Received random web value but keep native one + let handler = UnleashCookieHandler(unleash: MockUnleash.self) + guard let existingCookie = handler.makeCookie() else { + XCTFail("Failed to create unleash cookie when mock is loaded") + return + } + await mockCookieStore.setCookie(existingCookie) + + let webCookie = HTTPCookie(properties: [.name: "ECUNL", .domain: ".ecosia.org", .path: "/", .value: "some-random-value"])! + handler.received(webCookie, in: mockCookieStore) + try? await Task.sleep(nanoseconds: 100_000_000) // Make sure aync setCookie inside received is done + + var cookies = await mockCookieStore.allCookies() + var receivedWebCookie = cookies.first { $0.name == "ECUNL" } + XCTAssertEqual(receivedWebCookie?.value, existingCookie.value) + + // Second case: Received native web value so did not change cookie store + let fakeCookie = HTTPCookie(properties: [.name: "ECUNL", .domain: ".ecosia.org", .path: "/", .value: "some-unchanged-value"])! + await mockCookieStore.setCookie(fakeCookie) + + guard let nativeIdCookie = handler.makeCookie() else { + XCTFail("Failed to create unleash cookie when mock is loaded") + return + } + handler.received(nativeIdCookie, in: mockCookieStore) + try? await Task.sleep(nanoseconds: 100_000_000) // Make sure aync setCookie inside received is done + + cookies = await mockCookieStore.allCookies() + receivedWebCookie = cookies.first { $0.name == "ECUNL" } + XCTAssertEqual(receivedWebCookie?.value, fakeCookie.value, "When received cookie is the same as the native one, no cookie should be changed on store") + } + + // MARK: - Cookie Properties Tests + + func testCookieNameIsCorrect() { + let handler = UnleashCookieHandler(unleash: MockUnleash.self) + XCTAssertEqual(handler.cookieName, "ECUNL") + } +} + +// MARK: - Integration Tests + +extension UnleashCookieHandlerTests { + + func testMakeCookieCreatesValidCookieAfterUnleashStart() async { + _ = try? await Unleash.start(appVersion: "1.0.0") + + let handler = UnleashCookieHandler() + guard let cookie = handler.makeCookie() else { + XCTFail("Failed to create unleash cookie") + return + } + + XCTAssertEqual(cookie.name, "ECUNL") + XCTAssertEqual(cookie.domain, ".ecosia.org") + XCTAssertEqual(cookie.path, "/") + + XCTAssertFalse(cookie.value.isEmpty) + XCTAssertEqual(cookie.value, cookie.value.lowercased()) + } + + func testMultipleCookiesHaveSameIdAcrossSessions() async { + // Simulate Unleash being loaded + _ = try? await Unleash.start(appVersion: "1.0.0") + + let handler = UnleashCookieHandler() + + let cookie1 = handler.makeCookie() + XCTAssertNotNil(cookie1) + + // Force unloaded state and start again + Unleash.clearInstanceModel() + _ = try? await Unleash.start(appVersion: "1.0.0") + + let cookie2 = handler.makeCookie() + + XCTAssertNotNil(cookie2) + XCTAssertEqual(cookie1?.value, cookie2?.value) + } +} diff --git a/firefox-ios/EcosiaTests/Core/EcosiaLoggerSimpleTests.swift b/firefox-ios/EcosiaTests/Core/EcosiaLoggerSimpleTests.swift new file mode 100644 index 0000000000000..0d124f47669bd --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/EcosiaLoggerSimpleTests.swift @@ -0,0 +1,67 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class EcosiaLoggerSimpleTests: XCTestCase { + + func testDebugLogsAreConditional() { + // This test verifies that debug logging methods exist and can be called + // In DEBUG builds, they will print; in release builds, they won't + + EcosiaLogger.debug("Test debug message") + EcosiaLogger.info("Test info message") + + // These should always work regardless of build type + EcosiaLogger.warning("Test warning message") + EcosiaLogger.error("Test error message") + + XCTAssertTrue(true, "All logging methods should be callable") + } + + func testGenericLogMethod() { + EcosiaLogger.log("Debug test", level: .debug) + EcosiaLogger.log("Info test", level: .info) + EcosiaLogger.log("Warning test", level: .warning) + EcosiaLogger.log("Error test", level: .error) + + XCTAssertTrue(true, "Generic log method should handle all levels") + } + + func testCategoryLogging() { + EcosiaLogger.auth.debug("Auth debug") + EcosiaLogger.auth.error("Auth error") + + EcosiaLogger.invisibleTabs.debug("Tabs debug") + EcosiaLogger.invisibleTabs.error("Tabs error") + + EcosiaLogger.general.debug("General debug") + EcosiaLogger.general.error("General error") + + XCTAssertTrue(true, "Category logging should work") + } + + func testPerformanceOptimization() { + // Test that repeated logging calls don't significantly impact performance + let iterations = 1000 + let startTime = CFAbsoluteTimeGetCurrent() + + for i in 0..() async throws -> T? where T: Decodable { + initializeSessionRequestSent = true + return DummyResponse(value: 0) as? T + } + } + + struct DummyResponse: Decodable { + let value: Int + } +} diff --git a/firefox-ios/EcosiaTests/Core/FinancialReportsTests.swift b/firefox-ios/EcosiaTests/Core/FinancialReportsTests.swift new file mode 100644 index 0000000000000..02cd64471ee25 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/FinancialReportsTests.swift @@ -0,0 +1,44 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class FinancialReportsTests: XCTestCase { + private var financialReports: FinancialReports! + private var mockURLSession: MockURLSessionProtocol! + + override func setUp() { + financialReports = FinancialReports.shared + mockURLSession = MockURLSessionProtocol() + } + + func testFetchAndUpdate() async throws { + mockURLSession.data = Data(""" + { + "2023-8": { "totalIncome": 456, "numberOfTreesFinanced": 11 }, + "2023-7": { "totalIncome": 123, "numberOfTreesFinanced": 10 } + } + """.utf8) + + try await financialReports.fetchAndUpdate(urlSession: mockURLSession) + + XCTAssertEqual(financialReports.latestMonth, Date(timeIntervalSince1970: 1690848000)) + XCTAssertEqual(financialReports.latestReport, + FinancialReports.Report(totalIncome: 456, + numberOfTreesFinanced: 11)) + } + + func testLocalizedMonthAndYear() async throws { + mockURLSession.data = Data(""" + { + "2021-5": { "totalIncome": 1, "numberOfTreesFinanced": 1 } + } + """.utf8) + + try await financialReports.fetchAndUpdate(urlSession: mockURLSession) + + XCTAssertEqual(financialReports.localizedMonthAndYear, "May 2021") + } +} diff --git a/firefox-ios/EcosiaTests/Core/HistoryTests.swift b/firefox-ios/EcosiaTests/Core/HistoryTests.swift new file mode 100644 index 0000000000000..072cd8164d89e --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/HistoryTests.swift @@ -0,0 +1,76 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class HistoryTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.pages) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.pages) + } + + func testSave() { + let expect = expectation(description: "") + let history = History() + history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world")) + PageStore.queue.async { + XCTAssertTrue(FileManager.default.fileExists(atPath: FileManager.history.path)) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testLoad() { + let expect = expectation(description: "") + var history = History() + history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world")) + history.add(.init(url: URL(string: "https://guacamole.com")!, title: "lorem ipsum")) + PageStore.queue.async { + history = .init() + PageStore.queue.async { + XCTAssertGreaterThan(history.items.first!.0, Date(timeIntervalSince1970: 1)) + XCTAssertGreaterThan(history.items.last!.0, Date(timeIntervalSince1970: 1)) + XCTAssertEqual("hello world", history.items.first?.1.title) + XCTAssertEqual("lorem ipsum", history.items.last?.1.title) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testDelete() { + let expect = expectation(description: "") + var history = History() + history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world")) + PageStore.queue.async { + history = .init() + history.delete(history.items.first!.0) + PageStore.queue.async { + XCTAssertTrue(History().items.isEmpty) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testDeleteAll() { + let expect = expectation(description: "") + var history = History() + history.add(.init(url: URL(string: "https://avocado.com")!, title: "hello world")) + history.add(.init(url: URL(string: "https://guacamlo.com")!, title: "lorem ipsum")) + PageStore.queue.async { + history = .init() + history.deleteAll() + PageStore.queue.async { + XCTAssertTrue(History().items.isEmpty) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } +} diff --git a/firefox-ios/EcosiaTests/Core/ImagesTests.swift b/firefox-ios/EcosiaTests/Core/ImagesTests.swift new file mode 100644 index 0000000000000..a7549a68fb0b6 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/ImagesTests.swift @@ -0,0 +1,42 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class ImagesTests: XCTestCase { + private var session: MockURLSession! + private var images: Images! + + override func setUp() { + session = .init() + images = .init(session) + } + + func testDownload() { + let expect = expectation(description: "") + let url = URL(string: "avocado.com")! + session.data = [.init("hello".utf8)] + images.load(self, url: url) { + XCTAssertEqual(url, $0.url) + XCTAssertFalse($0.data.isEmpty) + XCTAssertEqual(.main, Thread.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCache() { + let expect = expectation(description: "") + let url = URL(string: "avocado.com")! + session.request = { + XCTFail() + } + images.items.insert(.init(url, .init("hello".utf8))) + images.load(self, url: url) { _ in + expect.fulfill() + } + waitForExpectations(timeout: 1) + } +} diff --git a/firefox-ios/EcosiaTests/Core/InvestmentsProjectionTests.swift b/firefox-ios/EcosiaTests/Core/InvestmentsProjectionTests.swift new file mode 100644 index 0000000000000..fdfb04b4c04ee --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/InvestmentsProjectionTests.swift @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class InvestmentsProjectionTests: XCTestCase { + private var investmentsProjection: InvestmentsProjection! + + override func setUp() { + investmentsProjection = InvestmentsProjection.shared + } + + func testTotalInvestedAt() { + let date = Date() + Statistics.shared.totalInvestments = 123456789 + Statistics.shared.totalInvestmentsLastUpdated = date.addingTimeInterval(-100) + Statistics.shared.investmentPerSecond = 0.5 + XCTAssertEqual(Int(100*0.5 + 123456789), investmentsProjection.totalInvestedAt(date)) + } + + func testTimerIsActive() { + let investmentPerSecond = 1.0 + Statistics.shared.investmentPerSecond = investmentPerSecond + + let exp = XCTestExpectation(description: "Wait for timer") + let projection = InvestmentsProjection() + var receivedAmount: Int? + projection.subscribe(self) { amount in + receivedAmount = amount + exp.fulfill() + } + wait(for: [exp], timeout: 1) + + XCTAssertNotNil(receivedAmount) + } +} diff --git a/firefox-ios/EcosiaTests/Core/LanguageTests.swift b/firefox-ios/EcosiaTests/Core/LanguageTests.swift new file mode 100644 index 0000000000000..9bbe344976df9 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/LanguageTests.swift @@ -0,0 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class LanguageTests: XCTestCase { + func testSettingLanguageCurrentToUnexpectedLanguageReturnsDefault() { + Language.current = Language.make(for: Locale(identifier: "nz")) + XCTAssertEqual(.en, Language.current) + } + + func testMake() { + XCTAssertEqual(.en, Language.make(for: .init(identifier: "en-DE"))) + XCTAssertEqual(.de, Language.make(for: .init(identifier: "de-MX"))) + XCTAssertEqual(.es, Language.make(for: .init(identifier: "es-ES"))) + XCTAssertEqual(.es, Language.make(for: .init(identifier: "es-MX"))) + XCTAssertEqual(.en, Language.make(for: .init(identifier: "en-US"))) + XCTAssertEqual(.es, Language.make(for: .init(identifier: "es-US"))) + XCTAssertEqual(.en, Language.make(for: .init(identifier: "Invalid"))) + } +} diff --git a/firefox-ios/EcosiaTests/Core/ListTests.swift b/firefox-ios/EcosiaTests/Core/ListTests.swift new file mode 100644 index 0000000000000..f187e7d693490 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/ListTests.swift @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class ListTests: XCTestCase { + func testIncomplete() { + let json = """ +[{ + "name": "test", + "id": 1 +}, +{ + "id": 2 +}] +""" + XCTAssertNil(try? JSONDecoder().decode([Model].self, from: .init(json.utf8))) + XCTAssertEqual("test", (try? JSONDecoder().decode(List.self, from: .init(json.utf8)).items.first?.name)) + } +} + +private struct Model: Decodable { + let name: String + let id: Int +} diff --git a/firefox-ios/EcosiaTests/Core/LocalTests.swift b/firefox-ios/EcosiaTests/Core/LocalTests.swift new file mode 100644 index 0000000000000..1970c3a48f2aa --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/LocalTests.swift @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class LocalTests: XCTestCase { + func testCurrent() { + Language.current = .en + XCTAssertEqual("en-us", Language.current.locale.rawValue) + } + + func testCountryCode() { + let locale = NSLocale(localeIdentifier: "it-DE") + XCTAssertEqual("DE", locale.countryCode) + XCTAssertEqual(.de_de, Local.make(for: locale as Locale)) + } + + func testRegion() { + XCTAssertEqual(.de_de, Local.make(for: .init(identifier: "en-DE"))) + XCTAssertEqual(.es_mx, Local.make(for: .init(identifier: "de-MX"))) + XCTAssertEqual(.es_es, Local.make(for: .init(identifier: "es-ES"))) + XCTAssertEqual(.es_mx, Local.make(for: .init(identifier: "es-MX"))) + XCTAssertEqual(.en_us, Local.make(for: .init(identifier: "en-US"))) + XCTAssertEqual(.es_us, Local.make(for: .init(identifier: "es-US"))) + XCTAssertEqual(.en_us, Local.make(for: .init(identifier: "Invalid"))) + } + + func testIdentifier() { + XCTAssertEqual("en-us", Local.en_us.rawValue) + XCTAssertEqual("de-de", Local.de_de.rawValue) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Navigation/EcosiaAuthRedirectorTests.swift b/firefox-ios/EcosiaTests/Core/Navigation/EcosiaAuthRedirectorTests.swift new file mode 100644 index 0000000000000..d387240b6a4e4 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Navigation/EcosiaAuthRedirectorTests.swift @@ -0,0 +1,106 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class EcosiaAuthRedirectorTests: XCTestCase { + func testRedirectURLForSignIn_appendsReturnToParameter() { + // Given + let signInURL = Environment.production.urlProvider.signInURL + let originalQuery = "https://www.ecosia.org/search?q=test" + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURLForSignIn(signInURL, redirectURLString: originalQuery, urlProvider: .production) + + // Then + XCTAssertNotNil(redirectedURL) + let components = URLComponents(url: redirectedURL!, resolvingAgainstBaseURL: false) + let returnToItem = components?.queryItems?.first(where: { $0.name == "returnTo" }) + XCTAssertEqual(returnToItem?.value, originalQuery) + } + + func testRedirectURLForSignIn_appendsReturnToWithNonSearchString() { + // Given + let signInURL = Environment.current.urlProvider.signInURL + let originalProfileURL = Environment.current.urlProvider.profileURL.absoluteString + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURLForSignIn(signInURL, redirectURLString: originalProfileURL) + + // Then + XCTAssertNotNil(redirectedURL) + let components = URLComponents(url: redirectedURL!, resolvingAgainstBaseURL: false) + let returnToItem = components?.queryItems?.first(where: { $0.name == "returnTo" }) + XCTAssertEqual(returnToItem?.value, originalProfileURL) + } + + func testRedirectURLForSignIn_returnsNilWhenURLIsNotSignIn() { + // Given + let nonSignInURL = Environment.production.urlProvider.profileURL + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURLForSignIn(nonSignInURL, redirectURLString: "https://www.ecosia.org/search?q=test", urlProvider: .production) + + // Then + XCTAssertNil(redirectedURL) + } + + func testRedirectURLForSignIn_returnsNotNilForSignInWithStagingHost() { + // Given + let stagingSignInURL = URL(string: "https://www.ecosia-staging.xyz/accounts/sign-in")! + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURLForSignIn(stagingSignInURL, redirectURLString: "https://www.ecosia.org/search?q=test", urlProvider: .staging) + + // Then + XCTAssertNotNil(redirectedURL) + } + + func testRedirectURLForSignIn_returnsNilForExternalSignInHost() { + // Given + let externalSignInURL = URL(string: "https://www.example.com/sign-in")! + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURLForSignIn(externalSignInURL, redirectURLString: "https://www.ecosia.org/search?q=test", urlProvider: .production) + + // Then + XCTAssertNil(redirectedURL) + } + + func testRedirectURL_returnsNilWhenReturnToAlreadyPresent() { + // Given + var components = URLComponents(url: Environment.production.urlProvider.signInURL, resolvingAgainstBaseURL: false)! + components.queryItems = [ + URLQueryItem(name: "returnTo", value: "https://example.com") + ] + let urlWithReturnTo = components.url! + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURL(for: urlWithReturnTo, redirectURLString: "https://www.ecosia.org/search?q=test") + + // Then + XCTAssertNil(redirectedURL) + } + + func testRedirectURL_preservesExistingQueryItems() { + // Given + var components = URLComponents(url: Environment.production.urlProvider.signInURL, resolvingAgainstBaseURL: false)! + components.queryItems = [ + URLQueryItem(name: "existingKey", value: "existingValue") + ] + let baseURL = components.url! + + // When + let redirectedURL = EcosiaAuthRedirector.redirectURLForSignIn(baseURL, + redirectURLString: "https://www.ecosia.org/search?q=another", + urlProvider: .production) + + // Then + XCTAssertNotNil(redirectedURL) + let finalComponents = URLComponents(url: redirectedURL!, resolvingAgainstBaseURL: false) + XCTAssertTrue(finalComponents?.queryItems?.contains(where: { $0.name == "existingKey" && $0.value == "existingValue" }) ?? false) + XCTAssertTrue(finalComponents?.queryItems?.contains(where: { $0.name == "returnTo" && $0.value == "https://www.ecosia.org/search?q=another" }) ?? false) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Navigation/EcosiaURLInterceptorTests.swift b/firefox-ios/EcosiaTests/Core/Navigation/EcosiaURLInterceptorTests.swift new file mode 100644 index 0000000000000..910ad17fd6111 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Navigation/EcosiaURLInterceptorTests.swift @@ -0,0 +1,284 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class EcosiaURLInterceptorTests: XCTestCase { + private var sut: EcosiaURLInterceptor! + + override func setUp() { + super.setUp() + sut = EcosiaURLInterceptor(urlProvider: .production) + } + + override func tearDown() { + sut = nil + super.tearDown() + } + + // MARK: - Sign Up Detection Tests + + func testInterceptedType_whenSignUpURL_returnsSignUp() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-up")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signUp) + } + + func testInterceptedType_whenSignUpURLWithQueryParams_returnsSignUp() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-up?redirect=/search")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signUp) + } + + func testInterceptedType_whenSignUpURLWithMixedCase_returnsSignUp() { + // Given + let url = URL(string: "https://www.ecosia.org/Accounts/Sign-Up")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signUp) + } + + func testShouldIntercept_whenSignUpURL_returnsTrue() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-up")! + // MARK: - Sign In Detection Tests + + func testInterceptedType_whenSignInURL_returnsSignIn() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-in")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signIn) + } + + func testInterceptedType_whenSignInURLWithQueryParams_returnsSignIn() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-in?redirect=/")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signIn) + } + + func testInterceptedType_whenSignInURLWithMixedCase_returnsSignIn() { + // Given + let url = URL(string: "https://www.ecosia.org/Accounts/Sign-In")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signIn) + } + + func testShouldIntercept_whenSignInURL_returnsTrue() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-in")! + + // When + let result = sut.shouldIntercept(url) + + // Then + XCTAssertTrue(result) + } + + // When + let result = sut.shouldIntercept(url) + + // Then + XCTAssertTrue(result) + } + + // MARK: - Sign Out Detection Tests + + func testInterceptedType_whenSignOutURL_returnsSignOut() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-out")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signOut) + } + + func testInterceptedType_whenSignOutURLWithQueryParams_returnsSignOut() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-out?redirect=/")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signOut) + } + + func testShouldIntercept_whenSignOutURL_returnsTrue() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/sign-out")! + + // When + let result = sut.shouldIntercept(url) + + // Then + XCTAssertTrue(result) + } + + // MARK: - Profile Detection Tests + + func testInterceptedType_whenProfileURL_returnsProfile() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/profile")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .profile) + } + + func testInterceptedType_whenProfileURLWithQueryParams_returnsProfile() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/profile?tab=settings")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .profile) + } + + func testInterceptedType_whenProfileURLWithMixedCase_returnsProfile() { + // Given + let url = URL(string: "https://www.ecosia.org/Accounts/Profile")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .profile) + } + + func testShouldIntercept_whenProfileURL_returnsTrue() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/profile")! + + // When + let result = sut.shouldIntercept(url) + + // Then + XCTAssertTrue(result) + } + + // MARK: - Non-Intercepted URL Tests + + func testInterceptedType_whenNonEcosiaURL_returnsNone() { + // Given + let url = URL(string: "https://www.google.com/search")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .none) + } + + func testInterceptedType_whenEcosiaSearchURL_returnsNone() { + // Given + let url = URL(string: "https://www.ecosia.org/search?q=test")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .none) + } + + func testInterceptedType_whenEcosiaHomeURL_returnsNone() { + // Given + let url = URL(string: "https://www.ecosia.org/")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .none) + } + + func testShouldIntercept_whenNonInterceptedURL_returnsFalse() { + // Given + let url = URL(string: "https://www.ecosia.org/search?q=test")! + + // When + let result = sut.shouldIntercept(url) + + // Then + XCTAssertFalse(result) + } + + func testShouldIntercept_whenNonEcosiaURL_returnsFalse() { + // Given + let url = URL(string: "https://www.google.com")! + + // When + let result = sut.shouldIntercept(url) + + // Then + XCTAssertFalse(result) + } + + // MARK: - Edge Cases + + func testInterceptedType_whenURLWithFragment_detectsCorrectly() { + // Given + let url = URL(string: "https://www.ecosia.org/accounts/profile#section")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .profile) + } + + func testInterceptedType_whenURLWithPort_detectsCorrectly() { + // Given + let url = URL(string: "https://www.ecosia.org:443/accounts/sign-up")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .signUp) + } + + func testInterceptedType_whenSubdomainEcosiaURL_detectsCorrectly() { + // Given + let url = URL(string: "https://test.ecosia.org/accounts/profile")! + + // When + let result = sut.interceptedType(for: url) + + // Then + XCTAssertEqual(result, .profile) + } +} diff --git a/firefox-ios/EcosiaTests/Core/NewsTests.swift b/firefox-ios/EcosiaTests/Core/NewsTests.swift new file mode 100644 index 0000000000000..956c29441380c --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/NewsTests.swift @@ -0,0 +1,203 @@ +// swiftlint:disable force_try +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class NewsTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.news) + try? FileManager.default.removeItem(at: FileManager.user) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.news) + try? FileManager.default.removeItem(at: FileManager.user) + } + + private func mockSavedItems() { + let items = [ + NewsModel( + id: 1, + text: "", + language: .en, + publishDate: .distantPast, + imageUrl: URL(string: "https://avocade.com")!, + targetUrl: URL(string: "https://avocadoe.com")!, + trackingName: "" + ), + NewsModel( + id: 2, + text: "hello", + language: .en, + publishDate: .distantFuture, + imageUrl: URL(string: "https://guacamole.com")!, + targetUrl: URL(string: "https://guaca.com")!, + trackingName: "" + ), + NewsModel( + id: 3, + text: "hello", + language: .de, + publishDate: .distantFuture, + imageUrl: URL(string: "https://guacamole.com")!, + targetUrl: URL(string: "https://guaca.com")!, + trackingName: "" + ) + ] + try? JSONEncoder().encode(items).write(to: FileManager.news, options: .atomic) + } + + func testPublishOnMainThread() { + let expect = expectation(description: "") + + mockSavedItems() + let notifications = News() + notifications.subscribe(self) { _ in + XCTAssertEqual(.main, Thread.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testLoadFromDisk() { + let expect = expectation(description: "") + mockSavedItems() + + let notifications = News() + notifications.subscribe(self) { + XCTAssertEqual(2, $0.count) + $0.forEach { + XCTAssertEqual(.en, $0.language) + } + XCTAssertGreaterThan($0.first!.publishDate, $0.last!.publishDate) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testAvoidDuplication() { + var set = Set([ + NewsModel( + id: 1, + text: "Great headline ", + language: .en, + publishDate: .distantPast, + imageUrl: URL(string: "https://avocade.com")!, + targetUrl: URL(string: "https://avocadoe.com")!, + trackingName: "" + ) + ]) + set.insert( + NewsModel( + id: 1, + text: "hello", + language: .de, + publishDate: .distantFuture, + imageUrl: URL(string: "https://guacamole.com")!, + targetUrl: URL(string: "https://guaca.com")!, + trackingName: "" + ) + ) + XCTAssertEqual(1, set.count) + } + + func testLoadNewForced() { + User.shared.news = Date() + + let expect = expectation(description: "") + let session = MockURLSession() + session.data = [try! .init(contentsOf: Bundle.ecosiaTests.url(forResource: "notifications", withExtension: "json")!)] + + let notifications = News() + notifications.subscribe(self) { + XCTAssertEqual(10, $0.count) + XCTAssertGreaterThan($0.first!.publishDate, $0.last!.publishDate) + + expect.fulfill() + } + notifications.load(session: session) + waitForExpectations(timeout: 1) + } + + func testNeedsUpdateOnEmptyNews() { + let news = News() + XCTAssertTrue(news.needsUpdate) + } + + func testNeedsUpdateAfterLoading() { + let expect = expectation(description: "") + mockSavedItems() + User.shared.news = .distantPast + let news = News() + + news.subscribe(self) { _ in + XCTAssertTrue(news.needsUpdate) + + User.shared.news = Date() + XCTAssertFalse(news.needsUpdate) + + User.shared.news = Date().advanced(by: -25 * 60 * 60) + XCTAssertTrue(news.needsUpdate) + + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testSubscribeAndReceive() { + let expect = expectation(description: "") + let news = News() + + news.subscribeAndReceive(self) { items in + XCTAssert(news.state?.count == items.count) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCallOnFailed() { + let expect = expectation(description: "") + expect.isInverted = true // we expect no callback + let session = MockURLSession() + let notifications = News() + notifications.subscribe(self) { _ in + expect.fulfill() + } + notifications.load(session: session) + waitForExpectations(timeout: 1) + } + + func testCleanTextFromBundle() { + let expect = expectation(description: "") + mockSavedItems() + let notifications = News() + notifications.subscribe(self) { + $0.forEach { + XCTAssertFalse($0.text.contains("'")) + XCTAssertFalse($0.text.contains("")) + } + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCleanTextFromNetwork() { + let expect = expectation(description: "") + let session = MockURLSession() + session.data = [try! .init(contentsOf: Bundle.ecosiaTests.url(forResource: "notifications", withExtension: "json")!)] + let notifications = News() + notifications.subscribe(self) { + $0.forEach { + XCTAssertFalse($0.text.contains("'")) + XCTAssertFalse($0.text.contains("")) + } + expect.fulfill() + } + notifications.load(session: session) + waitForExpectations(timeout: 1) + } +} +// swiftlint:enable force_try diff --git a/firefox-ios/EcosiaTests/Core/PublishersTests.swift b/firefox-ios/EcosiaTests/Core/PublishersTests.swift new file mode 100644 index 0000000000000..0b3517594e248 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/PublishersTests.swift @@ -0,0 +1,63 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class PublishersTests: XCTestCase { + func testNotifySubscriber() { + let publisher = ExamplePublisher() + let subscriber = ExampleSubscriber(expectation(description: ""), publisher: publisher) + subscriber.shouldReceive = ["hello", "world"] + publisher.eventHappened(["hello", "world"]) + waitForExpectations(timeout: 1) + } + + func testNotRetainingSubscriber() { + let publisher = ExamplePublisher() + var subscriber: ExampleSubscriber? = ExampleSubscriber(expectation(description: ""), publisher: publisher) + subscriber!.shouldReceive = ["hello", "world"] + publisher.eventHappened(["hello", "world"]) + subscriber = nil + publisher.eventHappened(["hello", "world"]) + waitForExpectations(timeout: 1) { _ in + XCTAssertTrue(publisher.subscriptions.isEmpty) + } + } + + func testUnsubscribe() { + let publisher = ExamplePublisher() + publisher.subscribe(self) { _ in } + publisher.unsubscribe(self) + XCTAssertTrue(publisher.subscriptions.isEmpty) + } + + func testSubscribeMultipleTimes() { + let publisher = ExamplePublisher() + publisher.subscribe(self) { _ in } + publisher.subscribe(self) { _ in } + XCTAssertEqual(1, publisher.subscriptions.count) + } +} + +private final class ExamplePublisher: Publisher { + var subscriptions = [Subscription<[String]>]() + + func eventHappened(_ messages: [String]) { + send(messages) + } +} + +private final class ExampleSubscriber { + var shouldReceive = [String]() + private let expect: XCTestExpectation + + init(_ expect: XCTestExpectation, publisher: ExamplePublisher) { + self.expect = expect + publisher.subscribe(self) { [weak self] in + XCTAssertEqual(self?.shouldReceive, $0) + self?.expect.fulfill() + } + } +} diff --git a/firefox-ios/EcosiaTests/Core/ReferralsModelTests.swift b/firefox-ios/EcosiaTests/Core/ReferralsModelTests.swift new file mode 100644 index 0000000000000..cfc1026354ae6 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/ReferralsModelTests.swift @@ -0,0 +1,60 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class ReferralsModelTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + func testInitWithCode() { + let expect = expectation(description: "") + User.shared.referrals = .init(code: "avocado1234") + User.queue.async { + let user = User() + XCTAssertEqual(0, user.referrals.claims) + XCTAssertEqual("avocado1234", user.referrals.code) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testUpdateReferred() { + let expect = expectation(description: "") + User.shared.referrals = .init(code: "avocado12") + User.queue.async { + User.shared.referrals.claims += 1 + User.queue.async { + let user = User() + XCTAssertEqual(1, user.referrals.claims) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testCount() { + var referrals = Referrals.Model() + XCTAssertEqual(0, referrals.count) + referrals.claims = 1 + XCTAssertEqual(1, referrals.count) + referrals.isClaimed = true + XCTAssertEqual(2, referrals.count) + } + + func testClaims() { + var referrals = Referrals.Model() + XCTAssertEqual(0, referrals.newClaims) + referrals.claims = 2 + XCTAssertEqual(2, referrals.newClaims) + referrals.accept() + XCTAssertEqual(0, referrals.newClaims) + } +} diff --git a/firefox-ios/EcosiaTests/Core/ReferralsTests.swift b/firefox-ios/EcosiaTests/Core/ReferralsTests.swift new file mode 100644 index 0000000000000..48445ae369776 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/ReferralsTests.swift @@ -0,0 +1,182 @@ +// swiftlint:disable force_try +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class ReferralsTests: XCTestCase { + + var httpClientMock: HTTPClientMock! + var referrals: Referrals! + let mockURL = URL(string: "https://www.example.com")! + let okResponse = HTTPURLResponse(url: URL(string: "https://example.com")!, statusCode: 200, httpVersion: nil, headerFields: nil) + let createdResponse = HTTPURLResponse(url: URL(string: "https://www.example.com")!, statusCode: 201, httpVersion: nil, headerFields: nil) + let failureResponse = HTTPURLResponse(url: URL(string: "https://www.example.com")!, statusCode: 500, httpVersion: nil, headerFields: nil) + let notFoundResponse = HTTPURLResponse(url: URL(string: "https://www.example.com")!, statusCode: 404, httpVersion: nil, headerFields: nil) + + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.user) + + httpClientMock = HTTPClientMock() + httpClientMock.data = try! Data(contentsOf: Bundle.ecosiaTests.url(forResource: "referrals", withExtension: "json")!) + httpClientMock.response = failureResponse + + // Force clean state + var user = User() + user.referrals = .init() + User.shared = user + + referrals = Referrals(client: httpClientMock) + } + + func testFetchCodeNotCreated() async throws { + XCTAssertNil(User.shared.referrals.code) + httpClientMock.response = okResponse + + try await referrals.refresh(createCode: false) + + XCTAssertNil(User.shared.referrals.code) + } + + func testFetchCodeCreate() async throws { + XCTAssertNil(User.shared.referrals.code) + httpClientMock.response = okResponse + + let expect = expectation(description: "") + referrals.subscribe(self) { model in + self.referrals.unsubscribe(self) + XCTAssertEqual(model.code, "MANGO-2UGicG") + XCTAssertEqual(User.shared.referrals.code, "MANGO-2UGicG") + XCTAssertEqual(model.claims, 1) + expect.fulfill() + } + try await referrals.refresh(createCode: true) + await fulfillment(of: [expect], timeout: 1) + } + + func testFetchCodeWithCodeInPlace() async throws { + User.shared.referrals.code = "Avocado" + + let codeInfo = try await referrals.fetchCode() + + XCTAssertEqual(codeInfo.code, "Avocado") + } + + func testRefreshHandlesNotFound() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + XCTAssertEqual(User.shared.referrals.claims, 0) + httpClientMock.response = notFoundResponse + httpClientMock.executeBeforeResponse = { + XCTAssertEqual(self.httpClientMock.response, self.notFoundResponse) + } + try await referrals.refresh() + } + + func testRefreshHandlesNotFound_CreatesNewCode() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + XCTAssertEqual(User.shared.referrals.claims, 0) + httpClientMock.response = notFoundResponse + httpClientMock.data = """ + { + "code": "NEW-CODE", + "claims": 0 + } + """.data(using: .utf8)! + try await referrals.refresh() + XCTAssertEqual(User.shared.referrals.code, "NEW-CODE") + XCTAssertEqual(User.shared.referrals.claims, 0) + } + + func testRefresh() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + XCTAssertEqual(User.shared.referrals.claims, 0) + httpClientMock.response = okResponse + + let expect = expectation(description: "") + referrals.subscribe(self) { model in + self.referrals.unsubscribe(self) + XCTAssertEqual(model.code, User.shared.referrals.code) + XCTAssertEqual(User.shared.referrals.claims, 1) + expect.fulfill() + } + try await referrals.refresh() + await fulfillment(of: [expect], timeout: 1) + } + + func testClaim() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + XCTAssertFalse(User.shared.referrals.isClaimed) + XCTAssertFalse(User.shared.referrals.isNewClaim) + httpClientMock.response = createdResponse + + try await referrals.claim(referrer: "MANGO-1XrUBl") + + XCTAssertTrue(User.shared.referrals.isClaimed) + XCTAssertTrue(User.shared.referrals.isNewClaim) + } + + func testFetchCodeBeforeClaim() async throws { + XCTAssertNil(User.shared.referrals.code) + httpClientMock.response = createdResponse + + try await referrals.claim(referrer: "MANGO-1XrUBl") + + XCTAssertEqual(User.shared.referrals.code, "MANGO-2UGicG") + XCTAssertTrue(User.shared.referrals.isClaimed) + XCTAssertTrue(User.shared.referrals.isNewClaim) + } + + func testCooldown() async throws { + let lastUpdate = User.shared.referrals.updated + httpClientMock.response = okResponse + + try await referrals.refresh(createCode: true) + let recentUpdate = User.shared.referrals.updated + XCTAssert(recentUpdate > lastUpdate) + + // run into cooldown + try await self.referrals.refresh(force: false) + XCTAssert(User.shared.referrals.updated == recentUpdate) + + // update with force + try await self.referrals.refresh(force: true) + XCTAssert(User.shared.referrals.updated > recentUpdate) + } + + func testRefreshCooldownOnError() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + httpClientMock.response = failureResponse + let lastUpdate = User.shared.referrals.updated + + try? await referrals.refresh(force: false) + + XCTAssert(User.shared.referrals.updated > lastUpdate) + } + + func testIsRefreshingTrueWhileRefreshing() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + XCTAssertEqual(User.shared.referrals.claims, 0) + httpClientMock.response = okResponse + referrals.isRefreshing = false + + httpClientMock.executeBeforeResponse = { + XCTAssertTrue(self.referrals.isRefreshing) + } + try await referrals.refresh() + + XCTAssertFalse(referrals.isRefreshing) + } + + func testRefreshCodeIsNotCalledWhenRefreshing() async throws { + User.shared.referrals.code = "MANGO-2UGicG" + XCTAssertEqual(User.shared.referrals.claims, 0) + // Set failure so that it throws if unwanted request is made + httpClientMock.response = failureResponse + + referrals.isRefreshing = true + try await referrals.refresh() + } +} +// swiftlint:enable force_try diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/export_bookmark_ecosia.html b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/export_bookmark_ecosia.html new file mode 100644 index 0000000000000..d75531e77f53b --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/export_bookmark_ecosia.html @@ -0,0 +1,24 @@ + + + Bookmarks +

Bookmarks

+

+

Favorites

+

+

One +
Two +

My Folder #1

+

+

Three +

+

My Folder #2

+

+

Four +

My Subfolder #1

+

+

Five +

+

+

+

+ diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_chrome.html b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_chrome.html new file mode 100644 index 0000000000000..cc50838908902 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_chrome.html @@ -0,0 +1,21 @@ + + + +Bookmarks +

Bookmarks

+

+

Lesezeichenleiste

+

+

ecosia/ios-browser: The iOS Browser that plants trees 🌱 +
Bookmark import/export – Figma +

Ecosia

+

+

Test1

+

+

test +

+

+

+

diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_firefox.html b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_firefox.html new file mode 100644 index 0000000000000..19b069fdf7081 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_firefox.html @@ -0,0 +1,20 @@ + + + + +Bookmarks +

Lesezeichen-Menü

+ +

+

Mozilla Firefox

+

+

Hilfe erhalten +
Firefox anpassen +
Machen Sie mit +
Über uns +
Hilfe und Anleitungen +

+

diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_safari.html b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_safari.html new file mode 100644 index 0000000000000..4d9cc72f0efd0 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_input_bookmark_safari.html @@ -0,0 +1,38 @@ + + + + Lesezeichen +

Lesezeichen

+

Favoriten

+

+

Home

+

+

Pi-hole - pihole +
Free dynamic DNS for IPv6 +
Munzinger Online – Startseite +
GENIOS +

+

Apple

+

+

M1

+

+

[Fix] Issue while installing ruby with rbenv in M1 Mac - Prabin Poudel - Rails & Web App Developer | Freelancer +
Is Apple silicon ready? +
Übertragen von Dateien zwischen einem Mac mit Apple Chips und einem anderen Mac - Apple Support +

+

Home: Apple Support Communities +

+

Development

+

+

Kubernetes

+

+

Kubernetes Ingress using NGINX | Nic Williams +
timescale/tobs: tobs - The Observability Stack for Kubernetes. Easy install of a full observability stack into a k8s cluster with a CLI tool or Helm charts. +
Upgrading kubeadm clusters | Kubernetes +
How to Set Up an Nginx Ingress with Cert-Manager on DigitalOcean Kubernetes | DigitalOcean +
Kubernetes on Hetzner with Kubermatic KubeOne in 2021 +
Kubernetes and RBAC: Restrict user access to one namespace · Jeremie Vallee +

+

+

restic · Backups done right! + diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_chrome.txt b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_chrome.txt new file mode 100644 index 0000000000000..fbd7f30292c17 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_chrome.txt @@ -0,0 +1 @@ +[Ecosia.BookmarkItem.folder("Lesezeichenleiste", [Ecosia.BookmarkItem.bookmark("ecosia/ios-browser: The iOS Browser that plants trees 🌱", "https://github.com/ecosia/ios-browser", Ecosia.BookmarkMetadata(addedAt: Optional(2023-03-15 08:44:44 +0000), modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Bookmark import/export – Figma", "https://www.figma.com/file/uy3Bb5k5b3JVGvKi49P7RT/Bookmark-import%2Fexport?node-id=2%3A115787&t=zansqVg8w5nbf5vD-0", Ecosia.BookmarkMetadata(addedAt: Optional(2023-03-15 12:28:09 +0000), modifiedAt: nil)), Ecosia.BookmarkItem.folder("Ecosia", [Ecosia.BookmarkItem.folder("Test1", [Ecosia.BookmarkItem.bookmark("test", "https://test.tst/", Ecosia.BookmarkMetadata(addedAt: Optional(2023-03-15 12:34:43 +0000), modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: Optional(2023-03-15 12:34:32 +0000), modifiedAt: Optional(2023-03-15 12:34:43 +0000)))], Ecosia.BookmarkMetadata(addedAt: Optional(2023-03-15 12:33:18 +0000), modifiedAt: Optional(2023-03-15 12:34:32 +0000)))], Ecosia.BookmarkMetadata(addedAt: Optional(2023-03-09 14:18:52 +0000), modifiedAt: Optional(2023-03-15 12:33:18 +0000)))] diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_firefox.txt b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_firefox.txt new file mode 100644 index 0000000000000..8c596813d3bac --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_firefox.txt @@ -0,0 +1 @@ +[Ecosia.BookmarkItem.folder("Mozilla Firefox", [Ecosia.BookmarkItem.bookmark("Hilfe erhalten", "https://support.mozilla.org/de/products/firefox", Ecosia.BookmarkMetadata(addedAt: Optional(2021-12-02 10:41:13 +0000), modifiedAt: Optional(2021-12-02 10:41:13 +0000))), Ecosia.BookmarkItem.bookmark("Firefox anpassen", "https://support.mozilla.org/de/kb/customize-firefox-controls-buttons-and-toolbars?utm_source=firefox-browser&utm_medium=default-bookmarks&utm_campaign=customize", Ecosia.BookmarkMetadata(addedAt: Optional(2020-05-31 09:45:59 +0000), modifiedAt: Optional(2021-12-11 10:25:06 +0000))), Ecosia.BookmarkItem.bookmark("Machen Sie mit", "https://www.mozilla.org/de/contribute/", Ecosia.BookmarkMetadata(addedAt: Optional(2020-05-31 09:45:59 +0000), modifiedAt: Optional(2021-12-11 10:25:06 +0000))), Ecosia.BookmarkItem.bookmark("Über uns", "https://www.mozilla.org/de/about/", Ecosia.BookmarkMetadata(addedAt: Optional(2020-05-31 09:45:59 +0000), modifiedAt: Optional(2021-12-11 10:25:06 +0000))), Ecosia.BookmarkItem.bookmark("Hilfe und Anleitungen", "https://support.mozilla.org/de/products/firefox", Ecosia.BookmarkMetadata(addedAt: Optional(2020-05-31 09:45:59 +0000), modifiedAt: Optional(2021-12-09 17:13:46 +0000)))], Ecosia.BookmarkMetadata(addedAt: Optional(2020-05-31 09:45:59 +0000), modifiedAt: Optional(2021-12-11 10:25:06 +0000)))] diff --git a/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_safari.txt b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_safari.txt new file mode 100644 index 0000000000000..915b3b849f6e3 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/Bookmarks/import_output_bookmark_safari.txt @@ -0,0 +1 @@ +[Ecosia.BookmarkItem.folder("Favoriten", [Ecosia.BookmarkItem.folder("Home", [Ecosia.BookmarkItem.bookmark("Pi-hole - pihole", "http://pihole.lan/admin/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Free dynamic DNS for IPv6", "https://dynv6.com/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Munzinger Online – Startseite", "https://online.munzinger.de/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("GENIOS", "https://bib-voebb.genios.de/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.folder("Apple", [Ecosia.BookmarkItem.folder("M1", [Ecosia.BookmarkItem.bookmark("[Fix] Issue while installing ruby with rbenv in M1 Mac - Prabin Poudel - Rails & Web App Developer | Freelancer", "https://prabinpoudel.com.np/articles/fix-issue-while-installing-ruby-with-rbenv-in-m1-mac/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Is Apple silicon ready?", "https://isapplesiliconready.com/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Übertragen von Dateien zwischen einem Mac mit Apple Chips und einem anderen Mac - Apple Support", "https://support.apple.com/de-de/guide/mac-help/mchlb37e8ca7/12.0/mac/12.0", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Home: Apple Support Communities", "https://discussions.apple.com/index.jspa", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.folder("Development", [Ecosia.BookmarkItem.folder("Kubernetes", [Ecosia.BookmarkItem.bookmark("Kubernetes Ingress using NGINX | Nic Williams", "https://www.tiredpixel.com/2021/12/06/kubernetes-ingress-using-nginx/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("timescale/tobs: tobs - The Observability Stack for Kubernetes. Easy install of a full observability stack into a k8s cluster with a CLI tool or Helm charts.", "https://github.com/timescale/tobs", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Upgrading kubeadm clusters | Kubernetes", "https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("How to Set Up an Nginx Ingress with Cert-Manager on DigitalOcean Kubernetes | DigitalOcean", "https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nginx-ingress-with-cert-manager-on-digitalocean-kubernetes", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Kubernetes on Hetzner with Kubermatic KubeOne in 2021", "https://www.kubermatic.com/blog/kubernetes-on-hetzner-with-kubermatic-kubeone-in-2021/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("Kubernetes and RBAC: Restrict user access to one namespace · Jeremie Vallee", "https://jeremievallee.com/2018/05/28/kubernetes-rbac-namespace-user.html", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil)), Ecosia.BookmarkItem.bookmark("restic · Backups done right!", "https://restic.net/", Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))], Ecosia.BookmarkMetadata(addedAt: nil, modifiedAt: nil))] diff --git a/firefox-ios/EcosiaTests/Core/Resources/notifications.json b/firefox-ios/EcosiaTests/Core/Resources/notifications.json new file mode 100644 index 0000000000000..e882a339af9b7 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/notifications.json @@ -0,0 +1 @@ +[{"id":357,"text":"Nouveau bilan des plantations tout droit du Brésil ! À visionner ici.","tracking_name":"TU22_FR","publish_date":"2020-02-03T12:44:18Z","target_url":"https://youtu.be/MpAWfmOkuWg","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/TreeUpdate_ThumbnailBaner_x6PYaqr.jpg","language":"fr","level":3},{"id":356,"text":"Neues Baumpflanz-Update aus Brasilien! Für Video hier klicken","tracking_name":"TU22_DE","publish_date":"2020-02-03T12:42:49Z","target_url":"https://youtu.be/MpAWfmOkuWg","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/TreeUpdate_ThumbnailBaner_girJyIZ.jpg","language":"de","level":3},{"id":355,"text":"New tree-planting update from Brazil! Watch here","tracking_name":"TU22_EN","publish_date":"2020-02-03T12:42:17Z","target_url":"https://youtu.be/MpAWfmOkuWg","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/TreeUpdate_ThumbnailBaner.jpg","language":"en","level":3},{"id":347,"text":"Tutte le ricerche effettuate giovedì contribuiranno a piantare alberi in Australia! Per saperne di più","tracking_name":"IT_australia1","publish_date":"2020-01-20T16:03:10Z","target_url":"https://blog.ecosia.org/trees-for-australia/","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/ausnotif_Cl5D6vA.jpg","language":"it","level":3},{"id":346,"text":"Alla sökningar som görs pÃ¥ torsdag planterar träd i Australien! Läs mer","tracking_name":"SW_australia1","publish_date":"2020-01-20T16:02:32Z","target_url":"https://blog.ecosia.org/trees-for-australia/","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/ausnotif_YjEUtXg.jpg","language":"sv","level":3},{"id":345,"text":"Este jueves, ¡todas tus búsquedas ayudarán a plantar árboles en Australia!","tracking_name":"ES_australia1","publish_date":"2020-01-20T16:01:40Z","target_url":"https://blog.ecosia.org/trees-for-australia/","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/ausnotif_smHbNe4.jpg","language":"es","level":3},{"id":344,"text":"Alle zoekopdrachten op donderdag zullen ervoor zorgen dat er bomen geplant worden in Australië! Meer informatie","tracking_name":"NL_australia1","publish_date":"2020-01-20T16:00:41Z","target_url":"https://blog.ecosia.org/trees-for-australia/","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/ausnotif_avSY0Ke.jpg","language":"nl","level":3},{"id":360,"text":"L’importance des forêts et 3 manières de les protéger : découvrez notre vidéo.","tracking_name":"FR_IntlForestDay","publish_date":"2020-03-20T16:31:58Z","target_url":"https://youtu.be/_dWJVHIE9S8","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/Notification_jsl5H96.jpg","language":"fr","level":3},{"id":359,"text":"Die Bedeutung von Wäldern und drei Wege, sie zu schützen: Video anschauen","tracking_name":"DE_IntlForestDay","publish_date":"2020-03-20T14:12:24Z","target_url":"https://youtu.be/_dWJVHIE9S8","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/Notification_QVLTM7l.jpg","language":"de","level":3},{"id":358,"text":"The ' importance of forests and three ways you can protect them: watch video","tracking_name":"EN_IntlForestDay","publish_date":"2020-03-20T14:12:15Z","target_url":"https://youtu.be/_dWJVHIE9S8","image_url":"https://s3.amazonaws.com/static.ecosia.org/images/Notification_Rt0jeAh.jpg","language":"en","level":3}] diff --git a/firefox-ios/EcosiaTests/Core/Resources/referrals.json b/firefox-ios/EcosiaTests/Core/Resources/referrals.json new file mode 100644 index 0000000000000..075c534b6aa71 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Resources/referrals.json @@ -0,0 +1,4 @@ +{ + "code": "MANGO-2UGicG", + "claims_count": 1 +} diff --git a/firefox-ios/EcosiaTests/Core/SearchesCounterTests.swift b/firefox-ios/EcosiaTests/Core/SearchesCounterTests.swift new file mode 100644 index 0000000000000..356857ef76448 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/SearchesCounterTests.swift @@ -0,0 +1,41 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class SearchesCounterTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + func testSubscribeAndReceive() { + let expect = expectation(description: "") + let counter = SearchesCounter() + + counter.subscribeAndReceive(self) { items in + XCTAssertEqual(counter.state, User.shared.searchCount) + counter.unsubscribe(self) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testSubscribe() { + let expect = expectation(description: "") + let counter = SearchesCounter() + + counter.subscribe(self) { items in + XCTAssertEqual(counter.state, 2) + counter.unsubscribe(self) + expect.fulfill() + } + User.shared.searchCount = 2 + waitForExpectations(timeout: 1) + } +} diff --git a/firefox-ios/EcosiaTests/Core/SingularAdNetworkHelperTests.swift b/firefox-ios/EcosiaTests/Core/SingularAdNetworkHelperTests.swift new file mode 100644 index 0000000000000..6dbe3113729b4 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/SingularAdNetworkHelperTests.swift @@ -0,0 +1,329 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +#if os(iOS) + +class SingularAdNetworkHelperTests: XCTestCase { + let mockAppDeviceInfo = AppDeviceInfo(platform: "test", + bundleId: "com.test", + osVersion: "16.1", + deviceManufacturer: "d", + deviceModel: "e", + locale: "f", + appVersion: "0.0", + installReceipt: "h") + let mockAppDeviceInfoParameters = [ + "p": "test", + "i": "com.test", + "ve": "16.1", + "app_v": "0.0", + ] + var objectPersister: MockObjectPersister! + var timestampProvider: MockTimestampProvider! + private var singularService: MockSingularService! + var helper: SingularAdNetworkHelper! + + override func setUpWithError() throws { + objectPersister = MockObjectPersister() + timestampProvider = MockTimestampProvider(currentTimestamp: 1234567890) + singularService = MockSingularService() + helper = SingularAdNetworkHelper(skan: MockSkan.self, + objectPersister: objectPersister, + timestampProvider: timestampProvider, + singularService: singularService) + MockSkan.shouldThrowError = false + } + + override func tearDownWithError() throws { + objectPersister = nil + timestampProvider = nil + singularService = nil + helper = nil + } + + // MARK: Register for ad network + func testRegisterForAdNetwork() async throws { + try await helper.registerAppForAdNetworkAttribution() + + XCTAssertEqual(MockSkan.conversionValue, 0) + XCTAssertEqual(objectPersister.getValueFor(.firstSkanCallTimestamp) as? Int, 1234567890) + XCTAssertEqual(objectPersister.getValueFor(.lastSkanCallTimestamp) as? Int, 1234567890) + XCTAssertEqual(objectPersister.getValueFor(.conversionValue) as? Int, 0) + + XCTAssertNil(objectPersister.getValueFor(.previousFineValue)) + XCTAssertNil(objectPersister.getValueFor(.coarseValue(window: .first))) + XCTAssertNil(objectPersister.getValueFor(.previousCoarseValue(window: .first))) + XCTAssertNil(objectPersister.getValueFor(.windowLockTimestamp(window: .first))) + } + + func testDoesNotRegisterForAdNetworkGivenAleradyDonePreviously() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1) + XCTAssertNil(MockSkan.conversionValue) + XCTAssertNil(objectPersister.getValueFor(.lastSkanCallTimestamp)) + XCTAssertNil(objectPersister.getValueFor(.conversionValue)) + + try await helper.registerAppForAdNetworkAttribution() + + XCTAssertNil(MockSkan.conversionValue) + XCTAssertNil(objectPersister.getValueFor(.lastSkanCallTimestamp)) + XCTAssertNil(objectPersister.getValueFor(.conversionValue)) + XCTAssertNil(objectPersister.getValueFor(.previousFineValue)) + XCTAssertNil(objectPersister.getValueFor(.coarseValue(window: .first))) + XCTAssertNil(objectPersister.getValueFor(.previousCoarseValue(window: .first))) + XCTAssertNil(objectPersister.getValueFor(.windowLockTimestamp(window: .first))) + } + + func testRegisterForAdNetworkHandlesError() async throws { + MockSkan.shouldThrowError = true + + try await helper.registerAppForAdNetworkAttribution() + + XCTAssertEqual(objectPersister.getValueFor(.errorCode) as? Int, 1234) + } + + // MARK: Get persisted values + func testGetPersistedValuesDictionary() { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 123) + objectPersister.setValueFor(.lastSkanCallTimestamp, value: 456) + objectPersister.setValueFor(.conversionValue, value: 2) + objectPersister.setValueFor(.coarseValue(window: .first), value: 3) + objectPersister.setValueFor(.coarseValue(window: .second), value: 4) + objectPersister.setValueFor(.coarseValue(window: .third), value: 5) + objectPersister.setValueFor(.previousFineValue, value: 6) + objectPersister.setValueFor(.previousCoarseValue(window: .first), value: 7) + objectPersister.setValueFor(.previousCoarseValue(window: .second), value: 8) + objectPersister.setValueFor(.previousCoarseValue(window: .third), value: 9) + objectPersister.setValueFor(.windowLockTimestamp(window: .first), value: 10) + objectPersister.setValueFor(.windowLockTimestamp(window: .second), value: 11) + objectPersister.setValueFor(.windowLockTimestamp(window: .third), value: 12) + objectPersister.setValueFor(.errorCode, value: 1234) + + let dictionary = helper.persistedValuesDictionary + + XCTAssertEqual(dictionary["skan_first_call_to_skadnetwork_timestamp"], "123") + XCTAssertEqual(dictionary["skan_last_call_to_skadnetwork_timestamp"], "456") + XCTAssertEqual(dictionary["skan_current_conversion_value"], "2") + XCTAssertEqual(dictionary["p0_coarse"], "3") + XCTAssertEqual(dictionary["p1_coarse"], "4") + XCTAssertEqual(dictionary["p2_coarse"], "5") + XCTAssertEqual(dictionary["prev_fine_value"], "6") + XCTAssertEqual(dictionary["p0_prev_coarse_value"], "7") + XCTAssertEqual(dictionary["p1_prev_coarse_value"], "8") + XCTAssertEqual(dictionary["p2_prev_coarse_value"], "9") + XCTAssertEqual(dictionary["p0_window_lock"], "10") + XCTAssertEqual(dictionary["p1_window_lock"], "11") + XCTAssertEqual(dictionary["p2_window_lock"], "12") + XCTAssertEqual(dictionary["_skerror"], "1234") + } + + // MARK: Fetch and update from server + func testUpdateFetchedConversionValuesFromServerOnFirstWindow() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1234567880) + objectPersister.setValueFor(.lastSkanCallTimestamp, value: 123) + objectPersister.setValueFor(.conversionValue, value: 2) + objectPersister.setValueFor(.coarseValue(window: .first), value: 3) + objectPersister.setValueFor(.previousFineValue, value: 6) + objectPersister.setValueFor(.previousCoarseValue(window: .first), value: 7) + objectPersister.setValueFor(.windowLockTimestamp(window: .first), value: 10) + singularService.responseStub = SingularConversionValueResponse(conversionValue: 50, + coarseValue: nil, + lockWindow: true) + + try await helper.fetchFromSingularServerAndUpdate(sessionIdentifier: "123", appDeviceInfo: mockAppDeviceInfo) + + XCTAssertEqual(singularService.receivedParameters, [ + "n": "__SESSION__", + "sing": "123", + "p0_coarse": "3", + "p0_prev_coarse_value": "7", + "p0_window_lock": "10", + "prev_fine_value": "6", + "skan_current_conversion_value": "2", + "skan_first_call_to_skadnetwork_timestamp": "1234567880", + "skan_last_call_to_skadnetwork_timestamp": "123", + ].merging(mockAppDeviceInfoParameters) { (current, _) in current }) + XCTAssertEqual(MockSkan.conversionValue, 50) + XCTAssertNil(MockSkan.coarseValue) + XCTAssertEqual(MockSkan.lockWindow, true) + XCTAssertEqual(objectPersister.getValueFor(.conversionValue) as? Int, 50) + XCTAssertEqual(objectPersister.getValueFor(.previousFineValue) as? Int, 2) + XCTAssertNil(objectPersister.getValueFor(.coarseValue(window: .first))) + XCTAssertEqual(objectPersister.getValueFor(.previousCoarseValue(window: .first)) as? Int, 3) + XCTAssertEqual(objectPersister.getValueFor(.windowLockTimestamp(window: .first)) as? Int, 1234567890) + } + + func testUpdateFetchedConversionValuesFromServerOnSecondWindow() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1234387890) + objectPersister.setValueFor(.lastSkanCallTimestamp, value: 456) + objectPersister.setValueFor(.conversionValue, value: 7) + objectPersister.setValueFor(.coarseValue(window: .second), value: 5) + objectPersister.setValueFor(.previousCoarseValue(window: .second), value: 8) + objectPersister.setValueFor(.windowLockTimestamp(window: .second), value: 15) + singularService.responseStub = SingularConversionValueResponse(conversionValue: 30, + coarseValue: 2, + lockWindow: nil) + + try await helper.fetchFromSingularServerAndUpdate(sessionIdentifier: "123", appDeviceInfo: mockAppDeviceInfo) + + XCTAssertEqual(singularService.receivedParameters, [ + "n": "__SESSION__", + "sing": "123", + "p1_coarse": "5", + "p1_prev_coarse_value": "8", + "p1_window_lock": "15", + "skan_current_conversion_value": "7", + "skan_first_call_to_skadnetwork_timestamp": "1234387890", + "skan_last_call_to_skadnetwork_timestamp": "456", + ].merging(mockAppDeviceInfoParameters) { (current, _) in current }) + XCTAssertEqual(MockSkan.conversionValue, 30) + XCTAssertEqual(MockSkan.coarseValue, 2) + XCTAssertEqual(MockSkan.lockWindow, false) + XCTAssertEqual(objectPersister.getValueFor(.coarseValue(window: .second)) as? Int, 2) + XCTAssertEqual(objectPersister.getValueFor(.previousCoarseValue(window: .second)) as? Int, 5) + XCTAssertEqual(objectPersister.getValueFor(.windowLockTimestamp(window: .second)) as? Int, 15) + } + + func testUpdateFetchedConversionValuesFromServerOnThirdWindow() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1233567890) + objectPersister.setValueFor(.lastSkanCallTimestamp, value: 789) + objectPersister.setValueFor(.conversionValue, value: 11) + objectPersister.setValueFor(.coarseValue(window: .third), value: 20) + objectPersister.setValueFor(.previousCoarseValue(window: .third), value: 23) + objectPersister.setValueFor(.windowLockTimestamp(window: .third), value: 25) + singularService.responseStub = SingularConversionValueResponse(conversionValue: 44, + coarseValue: 0, + lockWindow: false) + + try await helper.fetchFromSingularServerAndUpdate(sessionIdentifier: "123", appDeviceInfo: mockAppDeviceInfo) + + XCTAssertEqual(singularService.receivedParameters, [ + "n": "__SESSION__", + "sing": "123", + "p2_coarse": "20", + "p2_prev_coarse_value": "23", + "p2_window_lock": "25", + "skan_current_conversion_value": "11", + "skan_first_call_to_skadnetwork_timestamp": "1233567890", + "skan_last_call_to_skadnetwork_timestamp": "789", + ].merging(mockAppDeviceInfoParameters) { (current, _) in current }) + XCTAssertEqual(MockSkan.conversionValue, 44) + XCTAssertEqual(MockSkan.coarseValue, 0) + XCTAssertEqual(MockSkan.lockWindow, false) + XCTAssertEqual(objectPersister.getValueFor(.coarseValue(window: .third)) as? Int, 0) + XCTAssertEqual(objectPersister.getValueFor(.previousCoarseValue(window: .third)) as? Int, 20) + XCTAssertEqual(objectPersister.getValueFor(.windowLockTimestamp(window: .third)) as? Int, 25) + } + + func testDoesNotFetchConversionValuesFromServerOnOverWindow() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1231542890) + + try await helper.fetchFromSingularServerAndUpdate(sessionIdentifier: "123", appDeviceInfo: mockAppDeviceInfo) + + XCTAssertNil(singularService.receivedParameters) + XCTAssertNil(MockSkan.conversionValue) + XCTAssertNil(MockSkan.coarseValue) + } + + func testErrorWhenInvalidFetchedConversionValue() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1234567880) + singularService.responseStub = SingularConversionValueResponse(conversionValue: 64, + coarseValue: 1, + lockWindow: false) + + do { + try await helper.fetchFromSingularServerAndUpdate(sessionIdentifier: "123", appDeviceInfo: mockAppDeviceInfo) + XCTFail("Did not throw error when it should") + } catch SingularAdNetworkHelper.Error.invalidConversionValues { + // expected + } catch { + XCTFail("Received unexpected error \(error)") + } + } + + func testErrorWhenInvalidFetchedCoarseValue() async throws { + objectPersister.setValueFor(.firstSkanCallTimestamp, value: 1234567880) + singularService.responseStub = SingularConversionValueResponse(conversionValue: 1, + coarseValue: 3, + lockWindow: false) + + do { + try await helper.fetchFromSingularServerAndUpdate(sessionIdentifier: "123", appDeviceInfo: mockAppDeviceInfo) + XCTFail("Did not throw error when it should") + } catch SingularAdNetworkHelper.Error.invalidConversionValues { + // expected + } catch { + XCTFail("Received unexpected error \(error)") + } + } +} + +// MARK: SKAdNetwork Mock +class MockSkan: SKAdNetworkProtocol { + + static var conversionValue: Int? + static var coarseValue: Int? + static var lockWindow: Bool? + static var shouldThrowError = false + + static func updatePostbackConversionValue(_ conversionValue: Int) async throws { + guard !shouldThrowError else { + throw NSError(domain: "MockSkanError", code: 1234, userInfo: nil) + } + self.conversionValue = conversionValue + } + + static func updatePostbackConversionValue(_ fineValue: Int, coarseValue: Int?, lockWindow: Bool) async throws { + self.conversionValue = fineValue + self.coarseValue = coarseValue + self.lockWindow = lockWindow + } + + // Should never call this method on the latest iOS version + static func registerAppForAdNetworkAttribution() {} + + // Should never call this method on the latest iOS version + static func updateConversionValue(_ conversionValue: Int) {} +} + +// MARK: ObjectPersister Mock +class MockObjectPersister: ObjectPersister { + + var values = [String: Any]() + func setValueFor(_ object: SingularAdNetworkHelper.PersistedObject, value: Any) { + values[object.key] = value + } + func getValueFor(_ object: SingularAdNetworkHelper.PersistedObject) -> Any? { + return values[object.key] + } + + func set(_ value: Any?, forKey key: String) { + values[key] = value + } + + func object(forKey key: String) -> Any? { + return values[key] + } +} + +// MARK: SingularService Mock +private class MockSingularService: SingularServiceProtocol { + var responseStub: SingularConversionValueResponse? + + var receivedParameters: [String: String]? + func getConversionValue(request: SingularConversionValueRequest) async throws -> SingularConversionValueResponse { + receivedParameters = request.queryParameters + return responseStub! + } + + func sendNotification(request: SingularNotificationRequest) async throws { + throw NSError(domain: "SingularAdNetworkHelperTests", + code: 0, + userInfo: [NSLocalizedDescriptionKey: "Should never get called in this context"]) + } +} + +#endif diff --git a/firefox-ios/EcosiaTests/Core/SingularServiceTests.swift b/firefox-ios/EcosiaTests/Core/SingularServiceTests.swift new file mode 100644 index 0000000000000..f53ed88f03615 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/SingularServiceTests.swift @@ -0,0 +1,232 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +#if os(iOS) + +class SingularServiceTests: XCTestCase { + + var httpClientMock: HTTPClientMock! + let failureResponseMock = HTTPURLResponse( + url: URL(string: "https://www.example.com")!, + statusCode: 404, + httpVersion: nil, + headerFields: nil + ) + let successResponseMock = HTTPURLResponse( + url: URL(string: "https://www.example.com")!, + statusCode: 200, + httpVersion: nil, + headerFields: nil + ) + var sessionInfoRequestMock: SingularSessionInfoSendRequest! + var eventRequestMock: SingularEventRequest! + var conversionValueRequestMock: SingularConversionValueRequest! + var service: SingularService! + + override func setUpWithError() throws { + httpClientMock = HTTPClientMock() + let deviceInfo = AppDeviceInfo( + platform: "a", + bundleId: "b", + osVersion: "c", + deviceManufacturer: "d", + deviceModel: "e", + locale: "f", + country: "g", + appVersion: "h" + ) + sessionInfoRequestMock = SingularSessionInfoSendRequest( + identifier: "123", + info: deviceInfo, + skanParameters: ["something": "test"] + ) + eventRequestMock = SingularEventRequest( + identifier: "123", + name: "some-event", + info: deviceInfo + ) + conversionValueRequestMock = SingularConversionValueRequest( + .init(identifier: "123", eventName: "c", appDeviceInfo: deviceInfo), + skanParameters: ["something": "test"] + ) + service = SingularService(client: httpClientMock) + } + + // Since this is a generic method that supports multiple requests, we re-use the tests + // MARK: Send Notification Info + func testSessionInfoFailsOnReceivingErrorResult() async throws { + try await genericFailsOnReceivingErrorResult(sessionInfoRequestMock) + } + func testEventFailsOnReceivingErrorResult() async throws { + try await genericFailsOnReceivingErrorResult(eventRequestMock) + } + func genericFailsOnReceivingErrorResult(_ mockRequest: SingularNotificationRequest) async throws { + // given + httpClientMock.response = successResponseMock + let singularResult = SingularResponse(status: "error", errorReason: "a reason") + httpClientMock.data = try JSONEncoder().encode(singularResult) + + // when + do { + try await service.sendNotification(request: mockRequest) + } catch SingularService.Error.dataReturnedError(let reason) { + XCTAssertEqual(reason, "a reason") + } catch { + XCTFail("Received unexpected error \(error)") + } + + // then + XCTAssertEqual(httpClientMock.requests.count, 1) + XCTAssertEqual(try? httpClientMock.requests[0].makeURLRequest(), try? mockRequest.makeURLRequest()) + } + + func testSessionInfoFailsOnReceivingNoDataInResponse() async throws { + try await genericFailsOnReceivingNoDataInResponse(sessionInfoRequestMock) + } + func testEventFailsOnReceivingNoDataInResponse() async throws { + try await genericFailsOnReceivingNoDataInResponse(eventRequestMock) + } + func genericFailsOnReceivingNoDataInResponse(_ mockRequest: SingularNotificationRequest) async throws { + // given + httpClientMock.response = successResponseMock + + // when + do { + try await service.sendNotification(request: mockRequest) + } catch DecodingError.dataCorrupted { + // expected + } catch { + XCTFail("Received unexpected error \(error)") + } + + // then + XCTAssertEqual(httpClientMock.requests.count, 1) + XCTAssertEqual(try? httpClientMock.requests[0].makeURLRequest(), try? mockRequest.makeURLRequest()) + } + + func testSessionInfoReturnsDataResultOK() async throws { + try await genericReturnsDataResultOK(sessionInfoRequestMock) + } + func testEventReturnsDataResultOK() async throws { + try await genericReturnsDataResultOK(eventRequestMock) + } + func genericReturnsDataResultOK(_ mockRequest: SingularNotificationRequest) async throws { + // given + httpClientMock.response = successResponseMock + httpClientMock.data = try JSONEncoder().encode(SingularResponse(status: "ok", errorReason: nil)) + + // when + try await service.sendNotification(request: mockRequest) + + // then + XCTAssertEqual(httpClientMock.requests.count, 1) + XCTAssertEqual(try? httpClientMock.requests[0].makeURLRequest(), try? mockRequest.makeURLRequest()) + } + + func testSessionInfoNetworkError() async throws { + try await genericNetworkError(sessionInfoRequestMock) + } + func testEventNetworkError() async throws { + try await genericNetworkError(eventRequestMock) + } + func genericNetworkError(_ mockRequest: SingularNotificationRequest) async throws { + // given + httpClientMock.response = failureResponseMock + + // when + do { + // when + try await service.sendNotification(request: mockRequest) + + // then + XCTFail("Did not throw error when it should") + } catch SingularService.Error.network { + // expected + } catch { + XCTFail("Received unexpected error \(error)") + } + + // Then + XCTAssertEqual(httpClientMock.requests.count, 1) + XCTAssertEqual(try? httpClientMock.requests[0].makeURLRequest(), try? mockRequest.makeURLRequest()) + } + + // MARK: Get Conversion Value + func testGetConversionValueFailsOnFailureStatus() async throws { + // given + httpClientMock.response = failureResponseMock + + do { + // when + _ = try await service.getConversionValue(request: conversionValueRequestMock) + + // then + XCTFail("Did not throw error when it should") + } catch SingularService.Error.network { + // expected + } catch { + XCTFail("Received unexpected error \(error)") + } + } + + func testGetConversionValueReturnsResponse() async throws { + // given + httpClientMock.response = successResponseMock + let expectedResponse = SingularConversionValueResponse(conversionValue: 12, coarseValue: 1, lockWindow: true) + httpClientMock.data = try JSONEncoder().encode(expectedResponse) + + // when + let response = try await service.getConversionValue(request: conversionValueRequestMock) + + // then + XCTAssertEqual(httpClientMock.requests.count, 1) + XCTAssertEqual(try? httpClientMock.requests[0].makeURLRequest(), try? conversionValueRequestMock.makeURLRequest()) + XCTAssertEqual(response, expectedResponse) + } + + func testGetConversionDecodesFallbackResponseAndReturnsError() async throws { + // given + httpClientMock.response = successResponseMock + let expectedResponse = SingularResponse(status: "ok", errorReason: nil) + httpClientMock.data = try JSONEncoder().encode(expectedResponse) + + // when + do { + // when + _ = try await service.getConversionValue(request: conversionValueRequestMock) + + // then + XCTFail("Did not throw error when it should") + } catch SingularService.Error.noConversionValueReturned { + // expected + } catch { + XCTFail("Received unexpected error \(error)") + } + } + + func testGetConversionReturnsFallbackResponseError() async throws { + // given + httpClientMock.response = successResponseMock + let expectedResponse = SingularResponse(status: "error", errorReason: "any reason") + httpClientMock.data = try JSONEncoder().encode(expectedResponse) + + // when + do { + // when + _ = try await service.getConversionValue(request: conversionValueRequestMock) + + // then + XCTFail("Did not throw error when it should") + } catch SingularService.Error.dataReturnedError(let reason) { + XCTAssertEqual(reason, "any reason") + } catch { + XCTFail("Received unexpected error \(error)") + } + } +} + +#endif diff --git a/firefox-ios/EcosiaTests/Core/SingularTests.swift b/firefox-ios/EcosiaTests/Core/SingularTests.swift new file mode 100644 index 0000000000000..afb878253b3df --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/SingularTests.swift @@ -0,0 +1,299 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +#if os(iOS) + +class SingularTests: XCTestCase { + + private var singularService: MockSingularService! + private var skanHelper: MockSingularAdNetworkHelper! + var singular: Singular! + + override func setUpWithError() throws { + singularService = MockSingularService() + skanHelper = MockSingularAdNetworkHelper() + singular = Singular(singularService: singularService, skanHelper: skanHelper) + } + + override func tearDownWithError() throws { + singularService = nil + skanHelper = nil + singular = nil + } + + // MARK: Public init + func testPublicInitIncludingSKAN() { + // when + singular = Singular(includeSKAN: true) + + // then + XCTAssertNotNil(singular.skanHelper) + } + + func testPublicInitWithoutSKAN() { + // when + singular = Singular(includeSKAN: false) + + // then + XCTAssertNil(singular.skanHelper) + } + + // MARK: Send session info + func testShouldSendSessionInfoWithSKAN() async throws { + // given + skanHelper.persistedValuesDictionary = MockSessionParameters.mockPersistedSkanValues + + // when + try await singular.sendSessionInfo(appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + + // then + XCTAssertFalse(skanHelper.registerAppForAdNetworkAttributionCalled) + XCTAssertEqual(skanHelper.fetchFromSingularServerAndUpdateEvent, .session) + XCTAssertNotNil(skanHelper.fetchFromSingularServerAndUpdateIdentifier) + XCTAssertEqual(skanHelper.fetchFromSingularServerAndUpdateAppDeviceInfo, MockSessionParameters.mockAppDeviceInfo) + XCTAssert(singularService.sendNotificationReceivedRequest is SingularSessionInfoSendRequest) + MockSessionParameters.assertReceivedParametersEqualToExpected(singularService.sendNotificationReceivedParameters) + } + + func testFirstSessionInfoWithSKAN() async throws { + // given + skanHelper.isRegistered = false + skanHelper.persistedValuesDictionary = MockSessionParameters.mockPersistedSkanValues + + // when + try await singular.sendSessionInfo(appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + + // then + XCTAssertTrue(skanHelper.registerAppForAdNetworkAttributionCalled) + XCTAssertNil(skanHelper.fetchFromSingularServerAndUpdateEvent) + XCTAssertNil(skanHelper.fetchFromSingularServerAndUpdateIdentifier) + XCTAssertNil(skanHelper.fetchFromSingularServerAndUpdateAppDeviceInfo) + XCTAssert(singularService.sendNotificationReceivedRequest is SingularSessionInfoSendRequest) + MockSessionParameters.assertReceivedParametersEqualToExpected(singularService.sendNotificationReceivedParameters) + } + + func testShouldSendSessionInfoWithoutSKAN() async throws { + // given + singular = Singular(singularService: singularService, skanHelper: nil) + + // when + try await singular.sendSessionInfo(appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + + // then + XCTAssertFalse(skanHelper.registerAppForAdNetworkAttributionCalled) + XCTAssertNil(skanHelper.fetchFromSingularServerAndUpdateEvent) + XCTAssertNil(skanHelper.fetchFromSingularServerAndUpdateIdentifier) + XCTAssertNil(skanHelper.fetchFromSingularServerAndUpdateAppDeviceInfo) + XCTAssert(singularService.sendNotificationReceivedRequest is SingularSessionInfoSendRequest) + MockSessionParameters.assertReceivedParametersEqualToExpected(singularService.sendNotificationReceivedParameters, includeSkan: false) + } + + func testShouldThrowSendSessionInfoError() async throws { + // given + let expectedError = NSError(domain: "test", code: 0) + singularService.sendNotificationError = expectedError + + // when + do { + try await singular.sendSessionInfo(appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + XCTFail("Did not throw expected error") + } catch { + // then + XCTAssertEqual(error as NSError, expectedError) + } + } + + // MARK: Handle SKAN errors + func testShouldNotThrowSKANRegisterError() async throws { + // given + skanHelper.isRegistered = false + skanHelper.persistedValuesDictionary = MockSessionParameters.mockPersistedSkanValues + skanHelper.registerAppForAdNetworkAttributionError = NSError(domain: "test", code: 0) + + // when + do { + try await singular.sendSessionInfo(appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + } catch { + XCTFail("No error expected, but received \(error)") + } + + // then + MockSessionParameters.assertReceivedParametersEqualToExpected(singularService.sendNotificationReceivedParameters) + } + + func testShouldNotThrowSKANServerError() async throws { + // given + skanHelper.persistedValuesDictionary = MockSessionParameters.mockPersistedSkanValues + skanHelper.fetchFromSingularServerAndUpdateError = NSError(domain: "test", code: 0) + + // when + do { + try await singular.sendSessionInfo(appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + } catch { + XCTFail("No error expected, but received \(error)") + } + + // then + MockSessionParameters.assertReceivedParametersEqualToExpected(singularService.sendNotificationReceivedParameters) + } + + // MARK: Send event + func testShouldSendEvent() async throws { + // given + let expectedEvent = MMPEvent.firstSearch + + // when + try await singular.sendEvent(expectedEvent, appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + + // then + XCTAssert(singularService.sendNotificationReceivedRequest is SingularEventRequest) + MockSessionParameters.assertReceivedParametersEqualToExpected(singularService.sendNotificationReceivedParameters, includeSkan: false, event: expectedEvent.rawValue) + } + + func testShouldThrowSendEventError() async throws { + // given + let expectedError = NSError(domain: "test", code: 0) + singularService.sendNotificationError = expectedError + + // when + do { + try await singular.sendEvent(MMPEvent.firstSearch, appDeviceInfo: MockSessionParameters.mockAppDeviceInfo) + XCTFail("Did not throw expected error") + } catch { + // then + XCTAssertEqual(error as NSError, expectedError) + } + } +} + +// MARK: SingularService Mock +private class MockSingularService: SingularServiceProtocol { + var responseStub: SingularConversionValueResponse? + + func getConversionValue(request: SingularConversionValueRequest) async throws -> SingularConversionValueResponse { + throw NSError(domain: "SingularTests", + code: 0, + userInfo: [NSLocalizedDescriptionKey: "Should never call this method directly in this context"]) + } + + var sendNotificationError: Error? + var sendNotificationReceivedRequest: SingularNotificationRequest? + var sendNotificationReceivedParameters: [String: String]? + func sendNotification(request: SingularNotificationRequest) async throws { + if let error = sendNotificationError { + throw error + } + sendNotificationReceivedRequest = request + sendNotificationReceivedParameters = request.queryParameters + } +} + +// MARK: SingularAdNetworkHelper Mock +class MockSingularAdNetworkHelper: SingularAdNetworkHelperProtocol { + var persistedValuesDictionary: [String: String] = [:] + var isRegistered: Bool = true + + var registerAppForAdNetworkAttributionError: Error? + var registerAppForAdNetworkAttributionCalled = false + func registerAppForAdNetworkAttribution() async throws { + if let error = registerAppForAdNetworkAttributionError { + throw error + } + registerAppForAdNetworkAttributionCalled = true + } + + var fetchFromSingularServerAndUpdateError: Error? + var fetchFromSingularServerAndUpdateEvent: SingularEvent? + var fetchFromSingularServerAndUpdateIdentifier: String? + var fetchFromSingularServerAndUpdateAppDeviceInfo: AppDeviceInfo? + func fetchFromSingularServerAndUpdate(forEvent event: SingularEvent, sessionIdentifier: String, appDeviceInfo: AppDeviceInfo) async throws { + if let error = fetchFromSingularServerAndUpdateError { + throw error + } + fetchFromSingularServerAndUpdateEvent = event + fetchFromSingularServerAndUpdateIdentifier = sessionIdentifier + fetchFromSingularServerAndUpdateAppDeviceInfo = appDeviceInfo + } +} + +// MARK: Parameters Mock +enum MockSessionParameters { + static let mockAppDeviceInfo = AppDeviceInfo(platform: "a", + bundleId: "b", + osVersion: "c", + deviceManufacturer: "d", + deviceModel: "e", + locale: "f", + country: "g", + appVersion: "h", + installReceipt: "i", + adServicesAttributionToken: "j") + private static let mockExpectedDeviceInfoParameters = [ + "p": "a", + "i": "b", + "ve": "c", + "ma": "d", + "mo": "e", + "lc": "f", + "country": "g", + "app_v": "h", + "install_receipt": "i", + "attribution_token": "j" + ] + private static let mockExpectedReducedDeviceInfoParameters = [ + "p": "a", + "i": "b", + "ve": "c" + ] + static let mockPersistedSkanValues = [ + SingularAdNetworkHelper.PersistedObject.conversionValue.queryKey: "test", + SingularAdNetworkHelper.PersistedObject.firstSkanCallTimestamp.queryKey: "abc", + SingularAdNetworkHelper.PersistedObject.lastSkanCallTimestamp.queryKey: "def", + SingularAdNetworkHelper.PersistedObject.coarseValue(window: .first).queryKey: "ghi", + SingularAdNetworkHelper.PersistedObject.coarseValue(window: .second).queryKey: "jkl", + SingularAdNetworkHelper.PersistedObject.coarseValue(window: .third).queryKey: "mno", + SingularAdNetworkHelper.PersistedObject.previousFineValue.queryKey: "pqr", + SingularAdNetworkHelper.PersistedObject.previousCoarseValue(window: .first).queryKey: "stu", + SingularAdNetworkHelper.PersistedObject.previousCoarseValue(window: .second).queryKey: "vxw", + SingularAdNetworkHelper.PersistedObject.previousCoarseValue(window: .third).queryKey: "yza", + SingularAdNetworkHelper.PersistedObject.windowLockTimestamp(window: .first).queryKey: "bcd", + SingularAdNetworkHelper.PersistedObject.windowLockTimestamp(window: .second).queryKey: "efg", + SingularAdNetworkHelper.PersistedObject.windowLockTimestamp(window: .third).queryKey: "hij" + ] + private static let mockExpectedSkanParameters = [ + "skan_current_conversion_value": "test", + "skan_first_call_to_skadnetwork_timestamp": "abc", + "skan_last_call_to_skadnetwork_timestamp": "def", + "p0_coarse": "ghi", + "p1_coarse": "jkl", + "p2_coarse": "mno", + "prev_fine_value": "pqr", + "p0_prev_coarse_value": "stu", + "p1_prev_coarse_value": "vxw", + "p2_prev_coarse_value": "yza", + "p0_window_lock": "bcd", + "p1_window_lock": "efg", + "p2_window_lock": "hij" + ] + + static func assertReceivedParametersEqualToExpected(_ receivedParameters: [String: String]?, includeSkan: Bool = true, event: String? = nil) { + var parameters = receivedParameters + + // Assert and remove id since it is randomly generated + XCTAssertNotNil(parameters?.removeValue(forKey: "sing")) + + let isEvent = (event != nil) + let deviceInfo = isEvent ? mockExpectedReducedDeviceInfoParameters : mockExpectedDeviceInfoParameters + let expectedParameters = deviceInfo + .merging(includeSkan ? mockExpectedSkanParameters : [:]) { (current, _) in current } + .merging(isEvent ? ["n": event!] : [:]) { (current, _) in current } + + XCTAssertEqual(parameters, expectedParameters) + } +} + +#endif diff --git a/firefox-ios/EcosiaTests/Core/SnapshotsTests.swift b/firefox-ios/EcosiaTests/Core/SnapshotsTests.swift new file mode 100644 index 0000000000000..c74f9c360a1fc --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/SnapshotsTests.swift @@ -0,0 +1,98 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class SnapshotsTests: XCTestCase { + private var tabs: Tabs! + + override func setUp() { + tabs = .init() + try? FileManager.default.removeItem(at: FileManager.snapshots) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.snapshots) + } + + func testNoImage() { + let expect = expectation(description: "") + tabs.image(UUID()) { + XCTAssertNil($0) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testGetImage() { + let expect = expectation(description: "") + let id = UUID() + tabs.save(.init("hello world".utf8), with: id) + tabs.queue.async { + self.tabs.image(UUID()) { + XCTAssertNil($0) + } + self.tabs.image(id) { + XCTAssertEqual("hello world", String(decoding: $0 ?? Data(), as: UTF8.self)) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testReplaceImage() { + let expect = expectation(description: "") + let id = UUID() + tabs.save(.init("hello world".utf8), with: id) + tabs.save(.init("lorem ipsum".utf8), with: id) + tabs.queue.async { + self.tabs.image(id) { + XCTAssertEqual("lorem ipsum", String(decoding: $0 ?? Data(), as: UTF8.self)) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testClear() { + let expect = expectation(description: "") + let idA = UUID() + let idB = UUID() + let idC = UUID() + tabs.save(.init("hello world".utf8), with: idA) + tabs.save(.init("lorem ipsum".utf8), with: idB) + tabs.save(.init("avocado".utf8), with: idC) + tabs.queue.async { + self.tabs.image(idA) { + XCTAssertNotNil($0) + self.tabs.clear() + self.tabs.image(idA) { + XCTAssertNil($0) + expect.fulfill() + } + } + } + waitForExpectations(timeout: 1) + } + + func testDelete() { + let expect = expectation(description: "") + let idA = UUID() + let idB = UUID() + tabs.save(.init("hello world".utf8), with: idA) + tabs.save(.init("lorem ipsum".utf8), with: idB) + tabs.deleteSnapshot(idA) + tabs.queue.async { + self.tabs.image(idA) { + XCTAssertNil($0) + } + self.tabs.image(idB) { + XCTAssertNotNil($0) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } +} diff --git a/firefox-ios/EcosiaTests/Core/StatisticsTests.swift b/firefox-ios/EcosiaTests/Core/StatisticsTests.swift new file mode 100644 index 0000000000000..49b8aef716dd2 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/StatisticsTests.swift @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class StatisticsTests: XCTestCase { + private var statistics: Statistics! + private var mockURLSession: MockURLSessionProtocol! + + override func setUp() { + statistics = Statistics.shared + mockURLSession = MockURLSessionProtocol() + } + + func testFetchAndUpdate() async throws { + mockURLSession.data = Data(""" + { + "results": [ + {"name": "Total Trees Planted", "value": "123456789", "last_updated": "2023-08-01T11:40:00.000000Z"}, + {"name": "Time per tree (seconds)", "value": "0.8"}, + {"name": "Searches per tree", "value": "20"}, + {"name": "Active Users", "value": "80000000"}, + {"name": "EUR=>USD", "value": "1.5"}, + {"name": "Some other name", "value": "123"}, + {"name": "Investments amount per second", "value": "0.423", "last_updated": null}, + {"name": "Total investments amount", "value": "2345678", "last_updated": "2023-07-30T00:00:00.000000Z"} + ] + } + """.utf8) + + try await statistics.fetchAndUpdate(urlSession: mockURLSession) + + XCTAssertEqual(statistics.treesPlanted, 123456789) + XCTAssertEqual(statistics.treesPlantedLastUpdated, Date(timeIntervalSince1970: 1690890000)) + XCTAssertEqual(statistics.timePerTree, 0.8) + XCTAssertEqual(statistics.searchesPerTree, 20) + XCTAssertEqual(statistics.activeUsers, 80000000) + XCTAssertEqual(statistics.eurToUsdMultiplier, 1.5) + XCTAssertEqual(statistics.investmentPerSecond, 0.423) + XCTAssertEqual(statistics.totalInvestments, 2345678) + XCTAssertEqual(statistics.totalInvestmentsLastUpdated, Date(timeIntervalSince1970: 1690675200)) + } +} diff --git a/firefox-ios/EcosiaTests/Core/TabsTests.swift b/firefox-ios/EcosiaTests/Core/TabsTests.swift new file mode 100644 index 0000000000000..bb70679126224 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/TabsTests.swift @@ -0,0 +1,225 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class TabsTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.pages) + try? FileManager.default.removeItem(at: FileManager.snapshots) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.pages) + try? FileManager.default.removeItem(at: FileManager.snapshots) + } + + func testNew() { + let tabs = Tabs() + XCTAssertTrue(tabs.items.isEmpty) + XCTAssertNil(tabs.current) + } + + func testAdd() { + let expect = expectation(description: "") + let url = URL(string: "https://www.avocado.com")! + let tabs = Tabs() + tabs.new(url) + XCTAssertEqual(url, tabs.items.first?.page?.url) + XCTAssertEqual(0, tabs.current) + PageStore.queue.async { + XCTAssertFalse(((try? Data(contentsOf: FileManager.tabs)) ?? Data()).isEmpty) + XCTAssertFalse(((try? Data(contentsOf: FileManager.currentTab)) ?? Data()).isEmpty) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testLoad() { + let expect = expectation(description: "") + let urlFirst = URL(string: "https://www.avocado.com")! + let urlSecond = URL(string: "https://www.trees.com")! + var tabs = Tabs() + tabs.new(urlFirst) + tabs.new(urlSecond) + PageStore.queue.async { + tabs = .init() + XCTAssertEqual(urlFirst, tabs.items.first?.page?.url) + XCTAssertEqual(urlSecond, tabs.items.last?.page?.url) + XCTAssertEqual(1, tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCurrentOutOfBounds() { + let expect = expectation(description: "") + PageStore.save(currentTab: 0) + PageStore.queue.async { + let tabs = Tabs() + XCTAssertNil(tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testClose() { + let expect = expectation(description: "") + var tabs = Tabs() + tabs.new(URL(string: "https://www.avocado.com")!) + tabs.close(tabs.items.first!.id) + PageStore.queue.async { + tabs = .init() + XCTAssertTrue(tabs.items.isEmpty) + XCTAssertNil(tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCloseNotCurrent() { + let expect = expectation(description: "") + var tabs = Tabs() + let url = URL(string: "https://www.avocado.com")! + tabs.new(URL(string: "https://www.trees.com")!) + tabs.new(url) + tabs.close(tabs.items.first!.id) + PageStore.queue.async { + tabs = .init() + XCTAssertEqual(url, tabs.items.first?.page?.url) + XCTAssertEqual(1, tabs.items.count) + XCTAssertEqual(0, tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCloseOffsetIndex() { + let expect = expectation(description: "") + var tabs = Tabs() + tabs.new(URL(string: "https://www.something.com")!) + tabs.new(URL(string: "https://www.else.com")!) + tabs.new(URL(string: "https://www.trees.com")!) + tabs.new(URL(string: "https://www.avocado.com")!) + tabs.close(tabs.items.first!.id) + PageStore.queue.async { + tabs = .init() + XCTAssertEqual(3, tabs.items.count) + XCTAssertEqual(2, tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testClearIndexOnClose() { + let expect = expectation(description: "") + var tabs = Tabs() + tabs.new(URL(string: "https://www.something.com")!) + tabs.new(URL(string: "https://www.else.com")!) + tabs.new(URL(string: "https://www.trees.com")!) + tabs.new(URL(string: "https://www.avocado.com")!) + tabs.close(tabs.items[3].id) + PageStore.queue.async { + tabs = .init() + XCTAssertEqual(3, tabs.items.count) + XCTAssertNil(tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCloseNoIndex() { + let expect = expectation(description: "") + var tabs = Tabs() + tabs.new(URL(string: "https://www.avocado.com")!) + tabs.new(URL(string: "https://www.trees.com")!) + tabs.current = nil + tabs.close(tabs.items.first!.id) + PageStore.queue.async { + tabs = .init() + XCTAssertEqual(1, tabs.items.count) + XCTAssertNil(tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testKeepIndexOnClose() { + let expect = expectation(description: "") + var tabs = Tabs() + tabs.new(URL(string: "https://www.something.com")!) + tabs.new(URL(string: "https://www.else.com")!) + tabs.new(URL(string: "https://www.trees.com")!) + tabs.new(URL(string: "https://www.avocado.com")!) + tabs.current = 1 + tabs.close(tabs.items[3].id) + PageStore.queue.async { + tabs = .init() + XCTAssertEqual(3, tabs.items.count) + XCTAssertEqual(1, tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testClear() { + let expect = expectation(description: "") + var tabs = Tabs() + tabs.new(URL(string: "https://www.something.com")!) + tabs.new(URL(string: "https://www.else.com")!) + tabs.clear() + PageStore.queue.async { + tabs = .init() + XCTAssertTrue(tabs.items.isEmpty) + XCTAssertNil(tabs.current) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testMaxOneWithoutPage() { + let expect = expectation(description: "") + let tabs = Tabs() + tabs.new(nil) + tabs.new(URL(string: "https://www.avocado.com")!) + tabs.new(nil) + tabs.new(nil) + PageStore.queue.async { + XCTAssertEqual(2, tabs.items.count) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testUpdatePage() { + let expect = expectation(description: "") + let url = URL(string: "https://www.avocado.com")! + let title = "hello world" + let tabs = Tabs() + tabs.new(URL(string: "https://www.some.com")!) + tabs.new(nil) + tabs.update(tabs.items.last!.id, page: .init(url: url, title: title)) + PageStore.queue.async { + XCTAssertEqual(url, tabs.items.last?.page?.url) + XCTAssertEqual(title, tabs.items.last?.page?.title) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testCloseRemoveSnapshot() { + let expect = expectation(description: "") + let tabs = Tabs() + tabs.new(URL(string: "https://www.avocado.com")!) + let id = tabs.items.first!.id + tabs.save(.init("hello world".utf8), with: id) + tabs.close(tabs.items.first!.id) + tabs.image(id) { + XCTAssertNil($0) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Tools/BookmarkFixtures.swift b/firefox-ios/EcosiaTests/Core/Tools/BookmarkFixtures.swift new file mode 100644 index 0000000000000..356b5cb5f40b0 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Tools/BookmarkFixtures.swift @@ -0,0 +1,38 @@ +// swiftlint:disable force_try +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Ecosia + +enum BookmarkFixtures { + enum Browser: String { + case chrome, firefox, safari + } + + case html(Browser), debugString(Browser) + + var value: String { + switch self { + case let .html(browser): + return String( + data: try! Data(contentsOf: Bundle.ecosiaTests.url(forResource: "import_input_bookmark_\(browser.rawValue)", withExtension: "html")!), + encoding: .utf8 + )!.trimmingCharacters(in: .newlines) + case let .debugString(browser): + return String( + data: try! Data(contentsOf: Bundle.ecosiaTests.url(forResource: "import_output_bookmark_\(browser.rawValue)", withExtension: "txt")!), + encoding: .utf8 + )!.trimmingCharacters(in: .newlines) + } + } + + static var ecosiaExportedHtml: String { + String( + data: try! Data(contentsOf: Bundle.ecosiaTests.url(forResource: "export_bookmark_ecosia", withExtension: "html")!), + encoding: .utf8 + )!.trimmingCharacters(in: .newlines) + } +} +// swiftlint:enable force_try diff --git a/firefox-ios/EcosiaTests/Core/Tools/HTTPClientMock.swift b/firefox-ios/EcosiaTests/Core/Tools/HTTPClientMock.swift new file mode 100644 index 0000000000000..bc7a2f99a2cce --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Tools/HTTPClientMock.swift @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import Foundation + +class HTTPClientMock: HTTPClient { + + var requests: [BaseRequest] = [] + var response: HTTPURLResponse? + var data = Data() + var executeBeforeResponse: (() -> Void)? + + func perform(_ request: BaseRequest) async throws -> (Data, HTTPURLResponse?) { + requests.append(request) + executeBeforeResponse?() + return (data, response) + } +} diff --git a/firefox-ios/EcosiaTests/Core/Tools/MockTimestampProvider.swift b/firefox-ios/EcosiaTests/Core/Tools/MockTimestampProvider.swift new file mode 100644 index 0000000000000..4b028303342a3 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Tools/MockTimestampProvider.swift @@ -0,0 +1,15 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Ecosia + +final class MockTimestampProvider: TimestampProvider { + + var currentTimestamp: TimeInterval + + init(currentTimestamp: TimeInterval) { + self.currentTimestamp = currentTimestamp + } +} diff --git a/firefox-ios/EcosiaTests/Core/Tools/MockURLSession.swift b/firefox-ios/EcosiaTests/Core/Tools/MockURLSession.swift new file mode 100644 index 0000000000000..50e7e96dc6223 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Tools/MockURLSession.swift @@ -0,0 +1,36 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +final class MockURLSession: URLSession, @unchecked Sendable { + var data = [Data]() + var request: (() -> Void)? + var response: HTTPURLResponse? + + override init() { + super.init() + } + + override func dataTask(with: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { + request?() + completionHandler(data.popLast(), response, nil) + return MockDataTask() + } + + override func dataTask(with: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { + request?() + completionHandler(data.popLast(), response, nil) + return MockDataTask() + } +} + +private class MockDataTask: URLSessionDataTask, @unchecked Sendable { + override init() { + super.init() + } + + override func resume() { + } +} diff --git a/firefox-ios/EcosiaTests/Core/Tools/MockURLSessionProtocol.swift b/firefox-ios/EcosiaTests/Core/Tools/MockURLSessionProtocol.swift new file mode 100644 index 0000000000000..c458dfc9cd248 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Tools/MockURLSessionProtocol.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Ecosia + +// Needed in spite of the already existing MockURLSession +// since URLSession's async methods are not open +class MockURLSessionProtocol: URLSessionProtocol { + var data: Data? + + func data(from url: URL) async throws -> (Data, URLResponse) { + let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)! + return (data!, response) + } +} diff --git a/firefox-ios/EcosiaTests/Core/TreesProjectionTests.swift b/firefox-ios/EcosiaTests/Core/TreesProjectionTests.swift new file mode 100644 index 0000000000000..26c2a97bab17c --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/TreesProjectionTests.swift @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class TreesProjectionTests: XCTestCase { + private var treesProjection: TreesProjection! + + override func setUp() { + treesProjection = TreesProjection.shared + } + + func testTreesAt() { + let date = Date() + Statistics.shared.treesPlanted = 10 + Statistics.shared.treesPlantedLastUpdated = date.addingTimeInterval(-100) + Statistics.shared.timePerTree = 2 + XCTAssertEqual(Int(100/2 + 10-1), treesProjection.treesAt(date)) + } + + func testTimerIsActive() { + let timePerTree = 0.1 + Statistics.shared.timePerTree = timePerTree + + let exp = XCTestExpectation(description: "Wait for timer") + let projection = TreesProjection() + var receivedCount: Int? + projection.subscribe(self) { count in + receivedCount = count + exp.fulfill() + } + wait(for: [exp], timeout: timePerTree) + + XCTAssertNotNil(receivedCount) + } +} diff --git a/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/ProductionURLProviderTests.swift b/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/ProductionURLProviderTests.swift new file mode 100644 index 0000000000000..2a7907019dc6d --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/ProductionURLProviderTests.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class ProductionURLProviderTests: XCTestCase { + + var urlProvider: URLProvider = .production + + func testProduction() { + XCTAssertEqual("https://www.ecosia.org", urlProvider.root.absoluteString) + } + + func testProductionURLsAreValid() { + XCTAssertNotNil(urlProvider.root) + XCTAssertNotNil(urlProvider.statistics) + XCTAssertNotNil(urlProvider.privacy) + XCTAssertNotNil(urlProvider.faq) + XCTAssertNotNil(urlProvider.terms) + XCTAssertNotNil(urlProvider.aboutCounter) + XCTAssertNotNil(urlProvider.snowplow) + XCTAssertNotNil(urlProvider.notifications) + } +} diff --git a/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/StagingURLProviderTests.swift b/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/StagingURLProviderTests.swift new file mode 100644 index 0000000000000..22fd15d99d29d --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/StagingURLProviderTests.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class StagingURLProviderTests: XCTestCase { + + var urlProvider: URLProvider = .staging + + func testStaging() { + XCTAssertEqual("https://www.ecosia-staging.xyz", urlProvider.root.absoluteString) + } + + func testStagingURLsAreValid() { + XCTAssertNotNil(urlProvider.root) + XCTAssertNotNil(urlProvider.statistics) + XCTAssertNotNil(urlProvider.privacy) + XCTAssertNotNil(urlProvider.faq) + XCTAssertNotNil(urlProvider.terms) + XCTAssertNotNil(urlProvider.aboutCounter) + XCTAssertNotNil(urlProvider.snowplow) + XCTAssertNotNil(urlProvider.notifications) + } +} diff --git a/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/URLProviderTests.swift b/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/URLProviderTests.swift new file mode 100644 index 0000000000000..5bddc8fbee738 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/URLProviderDependantTests/URLProviderTests.swift @@ -0,0 +1,191 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class URLProviderTests: XCTestCase { + + var urlProvider: URLProvider = .staging + + func testFinancialReports() { + let def = Language.current + Language.current = .en + XCTAssertNotNil(urlProvider.financialReports) + XCTAssertEqual(urlProvider.financialReports.pathComponents.last, "ecosia-financial-reports-tree-planting-receipts") + + Language.current = .fr + XCTAssertNotNil(urlProvider.financialReports) + XCTAssertEqual(urlProvider.financialReports.pathComponents.last, "rapports-financiers-recus-de-plantations-arbres") + + Language.current = .de + XCTAssertNotNil(urlProvider.financialReports) + XCTAssertEqual(urlProvider.financialReports.pathComponents.last, "ecosia-finanzberichte-baumplanzbelege") + + Language.current = def + } + + func testBlog() { + let def = Language.current + Language.current = .en + XCTAssertNotNil(urlProvider.blog) + XCTAssertEqual(urlProvider.blog.absoluteString, "https://blog.ecosia.org/") + + Language.current = .fr + XCTAssertNotNil(urlProvider.blog) + XCTAssertEqual(urlProvider.blog.absoluteString, "https://fr.blog.ecosia.org/") + + Language.current = .de + XCTAssertNotNil(urlProvider.blog) + XCTAssertEqual(urlProvider.blog.absoluteString, "https://de.blog.ecosia.org/") + + Language.current = def + } + + // MARK: - Auth0 Configuration Tests + + func testAuth0Domain_production() { + let provider = URLProvider.production + XCTAssertEqual(provider.auth0Domain, "login.ecosia.org") + } + + func testAuth0Domain_staging() { + let provider = URLProvider.staging + XCTAssertEqual(provider.auth0Domain, "login.ecosia-staging.xyz") + } + + func testAuth0Domain_debug() { + let provider = URLProvider.debug + XCTAssertEqual(provider.auth0Domain, "login.ecosia.org") + } + + func testAuth0CookieDomain_production() { + let provider = URLProvider.production + XCTAssertEqual(provider.auth0CookieDomain, "login.ecosia.org") + } + + func testAuth0CookieDomain_staging() { + let provider = URLProvider.staging + XCTAssertEqual(provider.auth0CookieDomain, "login.ecosia-staging.xyz") + } + + func testAuth0CookieDomain_debug() { + let provider = URLProvider.debug + XCTAssertEqual(provider.auth0CookieDomain, "login.ecosia.org") + } + + func testAuth0CookieDomainMatchesAuth0Domain() { + // Verify that cookie domain always matches auth0Domain for all environments + XCTAssertEqual(URLProvider.production.auth0CookieDomain, URLProvider.production.auth0Domain) + XCTAssertEqual(URLProvider.staging.auth0CookieDomain, URLProvider.staging.auth0Domain) + XCTAssertEqual(URLProvider.debug.auth0CookieDomain, URLProvider.debug.auth0Domain) + } + + // MARK: - Environment to URLProvider Mapping Tests + + func testEnvironmentDebugMapsToURLProviderDebug() { + let environment = Environment.debug + XCTAssertEqual(environment.urlProvider, URLProvider.debug) + } + + func testEnvironmentProductionMapsToURLProviderProduction() { + let environment = Environment.production + XCTAssertEqual(environment.urlProvider, URLProvider.production) + } + + func testEnvironmentStagingMapsToURLProviderStaging() { + let environment = Environment.staging + XCTAssertEqual(environment.urlProvider, URLProvider.staging) + } + + // MARK: - Debug Configuration Tests + + func testDebugFollowsProductionConfiguration() { + let debugProvider = URLProvider.debug + let productionProvider = URLProvider.production + + // Debug should follow production for these properties + XCTAssertEqual(debugProvider.root, productionProvider.root) + XCTAssertEqual(debugProvider.apiRoot, productionProvider.apiRoot) + XCTAssertEqual(debugProvider.snowplow, productionProvider.snowplow) + XCTAssertEqual(debugProvider.snowplowMicro, productionProvider.snowplowMicro) + XCTAssertEqual(debugProvider.unleash, productionProvider.unleash) + XCTAssertEqual(debugProvider.brazeEndpoint, productionProvider.brazeEndpoint) + XCTAssertEqual(debugProvider.statistics, productionProvider.statistics) + } + + func testSnowplowStaging() { + let provider = URLProvider.staging + XCTAssertEqual(provider.snowplow, "https://osc.ecosia-staging.xyz") + } + + func testTrees() { + let def = Language.current + Language.current = .en + XCTAssertNotNil(urlProvider.trees) + XCTAssertTrue(urlProvider.trees.absoluteString.hasSuffix("tag/where-does-ecosia-plant-trees/")) + + Language.current = .fr + XCTAssertNotNil(urlProvider.trees) + XCTAssertTrue(urlProvider.trees.absoluteString.hasSuffix("tag/projets/")) + + Language.current = .de + XCTAssertNotNil(urlProvider.trees) + XCTAssertTrue(urlProvider.trees.absoluteString.hasSuffix("tag/projekte/")) + + Language.current = def + } + + func testBetaProgram() { + let def = Language.current + Language.current = .en + XCTAssertNotNil(urlProvider.betaProgram) + XCTAssertEqual(urlProvider.betaProgram.absoluteString, "https://ecosia.typeform.com/to/EeMLqL3X") + + Language.current = .fr + XCTAssertNotNil(urlProvider.betaProgram) + XCTAssertEqual(urlProvider.betaProgram.absoluteString, "https://ecosia.typeform.com/to/oaFZzT0F") + + Language.current = .de + XCTAssertNotNil(urlProvider.betaProgram) + XCTAssertEqual(urlProvider.betaProgram.absoluteString, "https://ecosia.typeform.com/to/catmFLuA") + + Language.current = def + } + + func testBetaFeedback() { + let def = Language.current + Language.current = .en + XCTAssertNotNil(urlProvider.betaFeedback) + XCTAssertEqual(urlProvider.betaFeedback.absoluteString, "https://ecosia.typeform.com/to/LlUGlFT9") + + Language.current = .fr + XCTAssertNotNil(urlProvider.betaFeedback) + XCTAssertEqual(urlProvider.betaFeedback.absoluteString, "https://ecosia.typeform.com/to/PRw7550n") + + Language.current = .de + XCTAssertNotNil(urlProvider.betaFeedback) + XCTAssertEqual(urlProvider.betaFeedback.absoluteString, "https://ecosia.typeform.com/to/pIQ3uwp9") + + Language.current = def + } + + // MARK: - AI Search Tests + + func testAISearchWithoutOrigin() { + let url = urlProvider.aiSearch(origin: nil) + XCTAssertTrue(url.absoluteString.hasSuffix("/ai-search")) + XCTAssertNil(URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems) + } + + func testAISearchWithNTPOrigin() { + let url = urlProvider.aiSearch(origin: .ntp) + XCTAssertTrue(url.absoluteString.contains("/ai-search?origin=newtabbutton")) + } + + func testAISearchWithAutocompleteOrigin() { + let url = urlProvider.aiSearch(origin: .autocomplete) + XCTAssertTrue(url.absoluteString.contains("/ai-search?origin=autocomplete_app")) + } +} diff --git a/firefox-ios/EcosiaTests/Core/URLRequestTests.swift b/firefox-ios/EcosiaTests/Core/URLRequestTests.swift new file mode 100644 index 0000000000000..4437dbe6bfdce --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/URLRequestTests.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class URLRequestTests: XCTestCase { + + func testAddLanguageRegionHeader() { + var request = URLRequest(url: URL(string: "https://www.ecosia.org/search")!) + request.addLanguageRegionHeader() + + let dashedLanguageAndRegion = Locale.current.identifier.replacingOccurrences(of: "_", with: "-").lowercased() + XCTAssertEqual(request.value(forHTTPHeaderField: "x-ecosia-app-language-region"), dashedLanguageAndRegion) + } +} diff --git a/firefox-ios/EcosiaTests/Core/URLTests.swift b/firefox-ios/EcosiaTests/Core/URLTests.swift new file mode 100644 index 0000000000000..c6a29ad13a158 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/URLTests.swift @@ -0,0 +1,229 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class URLTests: XCTestCase { + + private var root: String! + var urlProvider: URLProvider = .production + + override func setUp() { + root = "\(urlProvider.root.scheme!)" + "://" + urlProvider.root.host! + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + // MARK: - `ecosiaSearchWithQuery` + + func testSearchUrl() { + let expect = expectation(description: "") + let suffix = "&tt=iosapp" + User.queue.async { + XCTAssertEqual(self.root + "/search?q=somefakesitecom" + suffix, URL.ecosiaSearchWithQuery("somefakesitecom", urlProvider: self.urlProvider).absoluteString) + XCTAssertEqual(self.root + "/search?q=some%20fakes%20ite.com" + suffix, URL.ecosiaSearchWithQuery("some fakes ite.com", urlProvider: self.urlProvider).absoluteString) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testEncodedQuery() { + let expect = expectation(description: "") + User.queue.async { + XCTAssertEqual(self.root + "/search?q=Paul+Coffee%3DGood,%20right?&tt=iosapp", URL.ecosiaSearchWithQuery("Paul+Coffee=Good, right?", urlProvider: self.urlProvider).absoluteString) + XCTAssertEqual(self.root + "/search?q=Hello%20THEre!&tt=iosapp", URL.ecosiaSearchWithQuery("Hello THEre!", urlProvider: self.urlProvider).absoluteString) + XCTAssertEqual(self.root + "/search?q=quinney%20for%20%20president&tt=iosapp", URL.ecosiaSearchWithQuery("quinney for president", urlProvider: self.urlProvider).absoluteString) + XCTAssertEqual(self.root + "/search?q=+?%25&tt=iosapp", URL.ecosiaSearchWithQuery("+?%", urlProvider: self.urlProvider).absoluteString) + XCTAssertEqual(self.root + "/search?q=Hello%2520THEre%2521&tt=iosapp", URL.ecosiaSearchWithQuery("Hello%20THEre%21", urlProvider: self.urlProvider).absoluteString) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + // MARK: - `isEcosiaSearchQuery` + + func testAssertIsNotEcosiaSearchURLOnNonEcosiaURL() { + let nonEcosiaURL = URL(string: "https://www.non-ecosia.com/search")! + XCTAssertFalse(nonEcosiaURL.isEcosiaSearchQuery(urlProvider)) + } + + func testAssertIsNotEcosiaSearchURLOnNonEcosiaSearchQueryURL() { + let nonSearchEcosiaURL = URL(string: "https://www.ecosia.org/example")! + XCTAssertFalse(nonSearchEcosiaURL.isEcosiaSearchQuery(urlProvider)) + } + + func testAssertIsEcosiaSearchURLOnEcosiaSearchQueryURL() { + let searchEcosiaURL = URL(string: "https://www.ecosia.org/search")! + XCTAssertTrue(searchEcosiaURL.isEcosiaSearchQuery(urlProvider)) + } + + // MARK: - `isEcosiaSearchVertical` & `getEcosiaSearchVerticalPath` + + func testAssertNotEcosiaSearchVerticalOnNonEcosiaURL() { + let nonEcosiaURL = URL(string: "https://www.non-ecosia.com/search")! + XCTAssertFalse(nonEcosiaURL.isEcosiaSearchVertical(urlProvider)) + XCTAssertNil(nonEcosiaURL.getEcosiaSearchVerticalPath(urlProvider)) + } + + func testAssertNotEcosiaSearchVerticalOnNonSearchPage() { + let settingsURL = URL(string: "https://www.ecosia.org/settings")! + XCTAssertFalse(settingsURL.isEcosiaSearchVertical(urlProvider)) + XCTAssertNil(settingsURL.getEcosiaSearchVerticalPath(urlProvider)) + } + + func testAssertEcosiaSearchVerticalOnEnumCases() { + for path in URL.EcosiaSearchVertical.allCases.map(\.rawValue) { + let url = URL(string: "https://www.ecosia.org/\(path)?q=test")! + XCTAssertTrue(url.isEcosiaSearchVertical(urlProvider)) + XCTAssertEqual(url.getEcosiaSearchVerticalPath(urlProvider), path) + } + } + + // MARK: - `getEcosiaSearchQuery` & `getEcosiaSearchPage` + + func testAssertEcosiaSearchQueryAndPageOnNonEcosiaURL() { + let nonEcosiaURL = URL(string: "https://www.non-ecosia.com/search?q=test&p=1")! + XCTAssertNil(nonEcosiaURL.getEcosiaSearchQuery(urlProvider)) + XCTAssertNil(nonEcosiaURL.getEcosiaSearchPage(urlProvider)) + } + + func testAssertEcosiaSearchQueryAndPageOnEcosiaURL() { + let ecosiaUrl = URL(string: "https://www.ecosia.org?q=test&p=1")! + XCTAssertEqual(ecosiaUrl.getEcosiaSearchQuery(urlProvider), "test") + XCTAssertEqual(ecosiaUrl.getEcosiaSearchPage(urlProvider), 1) + } + + // MARK: - `shouldEcosify` + + func testAssertShouldEcosifyOnNonEcosiaURL() { + let nonSearchEcosiaURL = URL(string: "https://www.google.com")! + XCTAssertFalse(nonSearchEcosiaURL.shouldEcosify(urlProvider)) + } + + func testAssertShouldEcosifyOnAllEcosiaURLs() { + let ecosiaURLs = [ + "https://ecosia.org", + "https://www.ecosia.org/search?q=foo", + "https://ecosia.org/image?q=test", + "https://ecosia.org/chat?q=test", + "https://ecosia.org/news?q=test", + "https://blog.ecosia.org/", + "https://www.ecosia.org/settings" + ] + ecosiaURLs.forEach { urlString in + XCTAssertTrue(URL(string: urlString)!.shouldEcosify(urlProvider)) + } + } + + // MARK: - `ecosified` + + func testAvoidEcosifyWrongScheme() { + let ecosified = URL(string: "gmsg://ecosia.org")!.ecosified(isIncognitoEnabled: false) + XCTAssertEqual(ecosified, URL(string: "gmsg://ecosia.org")) + } + + func testDontEcosifyIfNotEcosia() { + let ecosified = URL(string: "https://guacamole.org")!.ecosified(isIncognitoEnabled: false) + XCTAssertEqual(ecosified, URL(string: "https://guacamole.org")) + } + + func testPatchWithAnalyticsId() { + let id = UUID() + User.shared.sendAnonymousUsageData = true + User.shared.cookieConsentValue = "a" + User.shared.analyticsId = id + + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(id.uuidString)")) + + let search = URL(string: "https://www.www.ecosia.org/search?q=foo")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(search, URL(string: "https://www.www.ecosia.org/search?q=foo&_sp=\(id.uuidString)")) + + let alreadyPatched = URL(string: "https://www.www.ecosia.org/search?q=foo&_sp=12345")!.ecosified(isIncognitoEnabled: false) + XCTAssertEqual(alreadyPatched, URL(string: "https://www.www.ecosia.org/search?q=foo&_sp=12345")) + + let multiPatch = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(multiPatch, URL(string: "https://ecosia.org?_sp=\(id.uuidString)")) + } + + func testPatchWithNULLAnalyticsId() { + let id = UUID() + User.shared.sendAnonymousUsageData = false + User.shared.analyticsId = id + + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + + let search = URL(string: "https://www.www.ecosia.org/search?q=foo")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(search, URL(string: "https://www.www.ecosia.org/search?q=foo&_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + + let multiPatch = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(multiPatch, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + func testURLEcosifiedInIncognitoMode() { + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: true, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + func testURLEcosifiedWithAnonymousUsageDataToggleOFF() { + User.shared.sendAnonymousUsageData = false + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + func testURLEcosifiedWithRejectedAnalyticsCookies() { + User.shared.cookieConsentValue = "e" + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + func testURLEcosifiedWithAnonymousUsageDataToggleOFFButRejectedAnalyticsCookies() { + User.shared.sendAnonymousUsageData = false + User.shared.cookieConsentValue = "e" + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + func testURLEcosifiedWithAnonymousUsageDataToggleONAndRejectedAnalyticsCookies() { + User.shared.sendAnonymousUsageData = true + User.shared.cookieConsentValue = "e" + let domain = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(domain, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + func testEcosify() { + User.shared.sendAnonymousUsageData = true + User.shared.cookieConsentValue = "a" + let ecosified = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(ecosified, URL(string: "https://ecosia.org?_sp=\(User.shared.analyticsId.uuidString)")) + } + + func testEcosifyWithNULLAnalyticsID() { + User.shared.sendAnonymousUsageData = false + let ecosified = URL(string: "https://ecosia.org")!.ecosified(isIncognitoEnabled: false, urlProvider: self.urlProvider) + XCTAssertEqual(ecosified, URL(string: "https://ecosia.org?_sp=\(UUID(uuid: UUID_NULL).uuidString)")) + } + + // MARK: - `policy` + + func testPolicyAllow() { + XCTAssertEqual(.allow, URL(string: "ecosia.org")!.policy) + XCTAssertEqual(.allow, URL(string: "http://ecosia.org")!.policy) + XCTAssertEqual(.allow, URL(string: "http://www.ecosia.org")!.policy) + XCTAssertEqual(.allow, URL(string: "https://www.ecosia.org")!.policy) + XCTAssertEqual(.allow, URL(string: "ecosia://ecosia.org")!.policy) + XCTAssertEqual(.allow, URL(string: "maps://ecosia.org")!.policy) + } + + func testPolicyCancel() { + XCTAssertEqual(.cancel, URL(string: "gmsg://mobileads.google.com/appEvent?name=sitedefault&info=true&google.afma.Notify_dt=1625136586354")!.policy) + XCTAssertEqual(.cancel, URL(string: "gmsg://mobileads.google.com")!.policy) + XCTAssertEqual(.cancel, URL(string: "gmsg://something")!.policy) + XCTAssertEqual(.cancel, URL(string: "gmsg://")!.policy) + } +} diff --git a/firefox-ios/EcosiaTests/Core/UnleashFeatureManagementSessionInitializerTests.swift b/firefox-ios/EcosiaTests/Core/UnleashFeatureManagementSessionInitializerTests.swift new file mode 100644 index 0000000000000..4175ec8802dfa --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/UnleashFeatureManagementSessionInitializerTests.swift @@ -0,0 +1,88 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Foundation +@testable import Ecosia + +class UnleashFeatureManagementSessionInitializerTests: XCTestCase { + + // MARK: - Test Mocks + + class MockHTTPClient: HTTPClient { + + var performCalled = false + var performRequest: BaseRequest? + var performResult: HTTPClient.Result? + var performError: Error? + + func perform(_ request: BaseRequest) async throws -> HTTPClient.Result { + performCalled = true + performRequest = request + + if let error = performError { + throw error + } + + return performResult ?? (Data(), HTTPURLResponse()) + } + } + + // MARK: - Test Cases + + func testInitializeSession_WithValidData_ReturnsDecodedObject() async throws { + // Arrange + let client = MockHTTPClient() + let request = UnleashTests.stagingUnleashRequest + let expectedData = "{\"etag\": \"a-etag\"}".data(using: .utf8)! + let expectedResponse = HTTPURLResponse(url: URL(string: "https://ecosia.org")!, statusCode: 200, httpVersion: nil, headerFields: ["etag": "a-etag"])! + let model = try await UnleashTests.makeAvailableUnleashModel() + let initializer = UnleashFeatureManagementSessionInitializer(client: client, request: request, model: model) + + client.performResult = (expectedData, expectedResponse) + + // Act + let latestAvailableModel: Unleash.Model = try await initializer.startSession()! + + // Assert + XCTAssertTrue(client.performCalled, "The `perform` method should be called.") + XCTAssertEqual(try? client.performRequest?.makeURLRequest(), try? request.makeURLRequest(), "The request passed to the `perform` method should match the initialized request.") + XCTAssertEqual(latestAvailableModel.etag, "a-etag", "The decoded value should match the expected value.") + } + + func testInitializeSession_WithNoData_ThrowsNoDataError() async throws { + // Arrange + let client = MockHTTPClient() + let request = UnleashTests.stagingUnleashRequest + let model = try await UnleashTests.makeAvailableUnleashModel() + let initializer = UnleashFeatureManagementSessionInitializer(client: client, request: request, model: model) + + let expectedResponse = HTTPURLResponse(url: URL(string: "https://ecosia.org")!, statusCode: 200, httpVersion: nil, headerFields: nil)! + client.performResult = (Data(), expectedResponse) + + // Act & Assert + do { + let _: Unleash.Model = try await initializer.startSession()! + } catch { + XCTAssertEqual(error as? UnleashFeatureManagementSessionInitializer.Error, .noData, "The error should be `.noData`.") + } + } + + func testInitializeSession_WithNetworkError_ThrowsNetworkError() async throws { + // Arrange + let client = MockHTTPClient() + let model = try await UnleashTests.makeAvailableUnleashModel() + let initializer = UnleashFeatureManagementSessionInitializer(client: client, request: UnleashTests.stagingUnleashRequest, model: model) + + let expectedError = UnleashFeatureManagementSessionInitializer.Error.network + client.performError = expectedError + + // Act & Assert + do { + let _: Unleash.Model = try await initializer.startSession()! + } catch { + XCTAssertEqual(error as? UnleashFeatureManagementSessionInitializer.Error, expectedError, "The error should be `.network`.") + } + } +} diff --git a/firefox-ios/EcosiaTests/Core/UnleashRefreshConfiguratorTests.swift b/firefox-ios/EcosiaTests/Core/UnleashRefreshConfiguratorTests.swift new file mode 100644 index 0000000000000..60521c8b190ac --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/UnleashRefreshConfiguratorTests.swift @@ -0,0 +1,90 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class UnleashRefreshConfiguratorTests: XCTestCase { + + override func setUp() { + Unleash.rules = [] + } + + override func tearDown() { + Unleash.rules = [] + } + + func testAppUpdateRuleCheck() async { + // Given + let configurator = UnleashRefreshConfigurator() + configurator.withAppUpdateCheckRule(appVersion: "1.0.0") + + // Simulate App Version update + Unleash.model.appVersion = "1.1.0" + + // Then + XCTAssertTrue(Unleash.shouldRefresh, "Unleash should refresh after an app update.") + } + + func testAppUpdateRuleCheckNoVersionChange() async { + // Given + let configurator = UnleashRefreshConfigurator() + configurator.withAppUpdateCheckRule(appVersion: "1.0.0") + + // Simulate no App Version update (version stays the same) + Unleash.model.appVersion = "1.0.0" + + // Then + XCTAssertFalse(Unleash.shouldRefresh, "Unleash should not refresh when app version has not changed.") + } + + func testTwentyFourHourCacheExpirationRuleMoreThan24h() async { + // Given + let mockTimestampProvider = MockTimestampProvider(currentTimestamp: Date().addingTimeInterval(TimeInterval.twentyFourHoursTimeInterval + 1).timeIntervalSince1970) + let timeRule = TimeBasedRefreshingRule(interval: TimeInterval.twentyFourHoursTimeInterval, timestampProvider: mockTimestampProvider) + Unleash.addRule(timeRule) + + // When + Unleash.model.updated = Date() + + // Then + XCTAssertTrue(Unleash.shouldRefresh, "Unleash should refresh after 24 hours.") + } + + func testTwentyFourHourCacheExpirationRuleLessThan24h() async { + // Given + let configurator = UnleashRefreshConfigurator() + configurator.withTwentyFourHoursCacheExpirationRule() + + // When + Unleash.model.updated = Date().addingTimeInterval(TimeInterval.twentyFourHoursTimeInterval - 1) + + // Then + XCTAssertFalse(Unleash.shouldRefresh, "Unleash should not refresh if less than 24 hours have passed.") + } + + func testDeviceRegionUpdateCheckWithNoDeviceRegionChange() async { + // Given + let configurator = UnleashRefreshConfigurator() + configurator.withDeviceRegionUpdateCheckRule(localeProvider: MockLocale("us")) + + // When + Unleash.model.deviceRegion = "us" + + // Then + XCTAssertFalse(Unleash.shouldRefresh, "Unleash should not refresh when the device region has not changed.") + } + + func testDeviceRegionUpdateCheckWithDeviceRegionChange() async { + // Given + let configurator = UnleashRefreshConfigurator() + configurator.withDeviceRegionUpdateCheckRule(localeProvider: MockLocale("us")) + + // When + Unleash.model.deviceRegion = "uk" + + // Then + XCTAssertTrue(Unleash.shouldRefresh, "Unleash should refresh after a device region change.") + } +} diff --git a/firefox-ios/EcosiaTests/Core/UnleashTests.swift b/firefox-ios/EcosiaTests/Core/UnleashTests.swift new file mode 100644 index 0000000000000..bce41d44fafd2 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/UnleashTests.swift @@ -0,0 +1,321 @@ +// swiftlint:disable force_try +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class UnleashTests: XCTestCase { + + static let appVersion = "0.0.0" + + override func setUp() { + Unleash.clearInstanceModel() + Unleash.rules = [] + try? FileManager.default.removeItem(at: FileManager.unleash) + } + + override func tearDown() { + Unleash.clearInstanceModel() + Unleash.rules = [] + try? FileManager.default.removeItem(at: FileManager.unleash) + } + + func testSaveAndLoadFromCache() async throws { + var model = Unleash.Model() + let toggle = Unleash.Toggle(name: Unleash.Toggle.Name.configTest.rawValue, + enabled: true, + variant: .init(name: "control", enabled: true, payload: nil)) + model.toggles.insert(toggle) + try await Unleash.save(model) + + let loaded = await Unleash.load() + Unleash.model = loaded! + + XCTAssertTrue(Unleash.isEnabled(.configTest)) + XCTAssertTrue(Unleash.getVariant(.configTest).name == "control") + XCTAssertTrue(Unleash.getVariant(.configTest).enabled) + } + + func testReset() async throws { + let model = try await Unleash.start(env: .staging, appVersion: Self.appVersion) + let resetModel = try await Unleash.reset(env: .staging, appVersion: Self.appVersion) + + XCTAssertNotEqual(model.id, resetModel.id) + } + + func testMakeURL() { + let base = URL(string: "https://ecosia.org")! + let context = ["foo": "bar"] + var request = UnleashTests.stagingUnleashRequest + request.queryParameters = context + let url = request.baseURL + + XCTAssertTrue(url.absoluteString.hasPrefix(base.absoluteString)) + XCTAssertEqual(URLComponents(string: try request.makeURLRequest().url!.absoluteString)?.queryItems?.count, 1) + } + + func testMakeRequest() { + let stagingRequest = try! UnleashTests.stagingUnleashRequest.makeURLRequest() + XCTAssertNotNil(stagingRequest.value(forHTTPHeaderField: CloudflareKeyProvider.clientId)) + XCTAssertNotNil(stagingRequest.value(forHTTPHeaderField: CloudflareKeyProvider.clientSecret)) + XCTAssertNotNil(stagingRequest.value(forHTTPHeaderField: "If-None-Match")) + + let prodRequest = try! UnleashTests.prodUnleashRequest.makeURLRequest() + XCTAssertNil(prodRequest.value(forHTTPHeaderField: CloudflareKeyProvider.clientId)) + XCTAssertNil(prodRequest.value(forHTTPHeaderField: CloudflareKeyProvider.clientSecret)) + XCTAssertNotNil(stagingRequest.value(forHTTPHeaderField: "If-None-Match")) + } + + func testConfigTestEnabled() { + + let expectedEnabledStatus = true + let toggleName: Unleash.Toggle.Name = .configTest + let exampleToggle = Unleash.Toggle(name: toggleName.rawValue, + enabled: expectedEnabledStatus, + variant: Unleash.Variant(name: "", enabled: false, payload: nil)) + + let mockModel = Unleash.Model(toggles: Set([exampleToggle])) + + Unleash.model = mockModel + + let isEnabled = Unleash.isEnabled(toggleName) + + XCTAssertEqual(isEnabled, expectedEnabledStatus) + } + + func testIsLoadedFlagReflectsUnleashState() async { + XCTAssertFalse(Unleash.isLoaded) + + _ = try? await Unleash.start(appVersion: "1.0.0") + XCTAssertTrue(Unleash.isLoaded) + let firstId = Unleash.model.id + + Unleash.clearInstanceModel() + XCTAssertFalse(Unleash.isLoaded) + + _ = try? await Unleash.start(appVersion: "1.0.0") + XCTAssertEqual(Unleash.model.id, firstId, "Id should remain the same between sessions") + + _ = try? await Unleash.reset(env: .production, appVersion: "1.0.0") + XCTAssertTrue(Unleash.isLoaded) + XCTAssertNotEqual(Unleash.model.id, firstId, "Id should change after reset") + } + + func testQueryParametersWithMockedUser() { + let originalUser = User.shared + + var mockUser = User() + mockUser.versionOnInstall = "2.5.0" + mockUser.marketCode = Local.make(for: .init(identifier: "de-de")) + mockUser.searchCount = 150 + User.shared = mockUser + + let parameters = Unleash.queryParameters(appVersion: "3.0.0") + XCTAssertEqual(parameters["versionOnInstall"], "2.5.0") + XCTAssertEqual(parameters["market"], "de-de") + XCTAssertEqual(parameters["personalCounterSearches"], "150") + XCTAssertEqual(parameters["appVersion"], "3.0.0") + + User.shared = originalUser + } + + func testQueryParametersWithMockedLocale() { + let originalLocaleSource = Unleash.localeSource + + let mockLocale = MockLocale("fr", countryName: "France") + Unleash.localeSource = mockLocale + + let parameters = Unleash.queryParameters(appVersion: "2.0.0") + XCTAssertEqual(parameters["deviceRegion"], "fr") + XCTAssertEqual(parameters["country"], "France") + + Unleash.localeSource = originalLocaleSource + } + + func testQueryParametersWithMockedLocaleNilCountry() { + let originalLocaleSource = Unleash.localeSource + + let mockLocale = MockLocale("xx", countryName: nil) + Unleash.localeSource = mockLocale + + let parameters = Unleash.queryParameters(appVersion: "1.5.0") + XCTAssertEqual(parameters["deviceRegion"], "xx") + XCTAssertEqual(parameters["country"], "Unknown") + + Unleash.localeSource = originalLocaleSource + } +} + +extension UnleashTests { + + static func makeAvailableUnleashModel() async throws -> Unleash.Model { + try await Unleash.start(client: MockOKHTTPClient(), request: UnleashTests.stagingUnleashRequest, env: .staging, appVersion: Self.appVersion) + } +} + +extension UnleashTests { + + struct MockOKHTTPClient: HTTPClient { + + func perform(_ request: BaseRequest) async throws -> HTTPClient.Result { + let url = URL(string: "https://ecosia.org")! + let okResponse = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil) + let model = Unleash.Model(id: UUID(), toggles: [], updated: Date(), etag: "a-etag") + let modelData = try JSONEncoder().encode(model) + return (modelData, okResponse) + } + } +} + +extension UnleashTests { + + static var stagingUnleashRequest = MockStagingUnleashRequest(etag: "a-tag") + static var prodUnleashRequest = MockProdUnleashRequest(etag: "a-tag") + + static func mockMakeURLRequest(for url: URL, + path: String?, + queryParameters: [String: String]?, + etag: String, + method: HTTPMethod, + body: Data?, + environment: Environment) -> URLRequest { + + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) + + if let queryParameters { + urlComponents?.queryItems = queryParameters.map({ .init(name: $0.key, value: $0.value ) }) + } + + if let path { + urlComponents?.path = path + } + + var request = URLRequest(url: urlComponents?.url ?? url) + request.httpMethod = method.rawValue + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue(etag, forHTTPHeaderField: "If-None-Match") + request.cachePolicy = .reloadIgnoringLocalCacheData + request.httpBody = body + return request.withCloudFlareAuthParameters(environment: environment) + } + + struct MockStagingUnleashRequest: BaseRequest { + + var environment: Environment { + .staging + } + + var method: Ecosia.HTTPMethod { + .get + } + + var baseURL: URL { + URL(string: "https://ecosia.org")! + } + + var path: String { + "" + } + + var etag: String + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? { + ["If-None-Match": etag] + } + + var body: Data? + + func makeURLRequest() throws -> URLRequest { + UnleashTests.mockMakeURLRequest(for: baseURL, + path: path, + queryParameters: queryParameters, + etag: etag, + method: method, + body: body, + environment: environment) + } + } + + struct MockProdUnleashRequest: BaseRequest { + + var environment: Environment { + .production + } + + var method: Ecosia.HTTPMethod { + .get + } + + var baseURL: URL { + URL(string: "https://ecosia.org")! + } + + var path: String { + "" + } + + var etag: String + + var queryParameters: [String: String]? + + var additionalHeaders: [String: String]? { + ["If-None-Match": etag] + } + + var body: Data? + + func makeURLRequest() throws -> URLRequest { + UnleashTests.mockMakeURLRequest(for: baseURL, + path: path, + queryParameters: queryParameters, + etag: etag, + method: method, + body: body, + environment: environment) + } + } +} + +extension UnleashTests { + + // Need two types of mocks as the `Unleash.addRule` performs a type-safe addition + + struct MockAppUpdateRule: RefreshingRule { + let shouldRefresh: Bool + } + + struct MockDeviceRegionChangeRule: RefreshingRule { + let shouldRefresh: Bool + } + + func testShouldRefreshIfAnyRuleIsTrue() async { + // Given + let rule1 = MockAppUpdateRule(shouldRefresh: false) + let rule2 = MockDeviceRegionChangeRule(shouldRefresh: true) + + // When + Unleash.addRule(rule1) + Unleash.addRule(rule2) + + // Then + XCTAssertTrue(Unleash.shouldRefresh, "Unleash should refresh if any rule returns true.") + } + + func testShouldNotRefreshIfAllRulesAreFalse() async { + // Given + let rule1 = MockAppUpdateRule(shouldRefresh: false) + let rule2 = MockDeviceRegionChangeRule(shouldRefresh: false) + + // When + Unleash.addRule(rule1) + Unleash.addRule(rule2) + + // Then + XCTAssertFalse(Unleash.shouldRefresh, "Unleash should not refresh if all rules return false.") + } +} +// swiftlint:enable force_try diff --git a/firefox-ios/EcosiaTests/Core/UpgradeTests.swift b/firefox-ios/EcosiaTests/Core/UpgradeTests.swift new file mode 100644 index 0000000000000..830c1d5881893 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/UpgradeTests.swift @@ -0,0 +1,51 @@ +// swiftlint:disable force_try +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class UpgradeTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + func testFrom5_3To6() { + var old = User5_3() + old.install = .init(timeIntervalSince1970: 123) + old.news = .init(timeIntervalSince1970: 456) + old.analyticsId = .init() + old.marketCode = .bg_bg + old.adultFilter = .strict + old.autoComplete = false + old.firstTime = false + old.personalized = true + old.migrated = true + old.id = "hello world" + old.treeCount = 909 + old.state[User5_3.Key.welcomeScreen.rawValue] = "\(false)" + + try! JSONEncoder().encode(old).write(to: FileManager.user, options: .atomic) + + let upgraded = User() + XCTAssertEqual(old.install, upgraded.install) + XCTAssertEqual(old.news, upgraded.news) + XCTAssertEqual(old.analyticsId, upgraded.analyticsId) + XCTAssertEqual(old.marketCode, upgraded.marketCode) + XCTAssertEqual(old.adultFilter, upgraded.adultFilter) + XCTAssertEqual(old.autoComplete, upgraded.autoComplete) + XCTAssertEqual(old.firstTime, upgraded.firstTime) + XCTAssertEqual(old.personalized, upgraded.personalized) + XCTAssertEqual(old.migrated, upgraded.migrated) + XCTAssertEqual(old.id, upgraded.id) + XCTAssertEqual(old.treeCount, upgraded.searchCount) + XCTAssertEqual(old.state, upgraded.state) + XCTAssertEqual(Referrals.Model(), upgraded.referrals) + } +} +// swiftlint:enable force_try diff --git a/firefox-ios/EcosiaTests/Core/UserStateTests.swift b/firefox-ios/EcosiaTests/Core/UserStateTests.swift new file mode 100644 index 0000000000000..8a8656b74c017 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/UserStateTests.swift @@ -0,0 +1,55 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class UserStateTests: XCTestCase { + private var user: User! + + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.user) + user = .init() + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + func testStoredState() { + let storedState = ["test": "something_stored"] + let expect = expectation(description: "") + User.shared.state = storedState + User.queue.async { + let user = User() + XCTAssertEqual(user.state, storedState) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testShouldShowImpactIntro() { + XCTAssertTrue(user.shouldShowImpactIntro) + + user.state[User.Key.impactIntro.rawValue] = "\(false)" + + XCTAssertFalse(self.user.shouldShowImpactIntro) + } + + func testHideImpactIntro() { + user.state[User.Key.impactIntro.rawValue] = "\(true)" + + user.hideImpactIntro() + + XCTAssertEqual(user.state[User.Key.impactIntro.rawValue], "\(false)") + } + + func testShowImpactIntro() { + user.state[User.Key.impactIntro.rawValue] = "\(false)" + + user.showImpactIntro() + + XCTAssertEqual(user.state[User.Key.impactIntro.rawValue], "\(true)") + } +} diff --git a/firefox-ios/EcosiaTests/Core/UserTests.swift b/firefox-ios/EcosiaTests/Core/UserTests.swift new file mode 100644 index 0000000000000..ca418b0968745 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/UserTests.swift @@ -0,0 +1,364 @@ +// swiftlint:disable force_try +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import XCTest + +final class UserTests: XCTestCase { + override func setUp() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + override func tearDown() { + try? FileManager.default.removeItem(at: FileManager.user) + } + + func testFirstTime() { + let expect = expectation(description: "") + XCTAssertTrue(User.shared.firstTime) + let analyticsId = User.shared.analyticsId + User.shared.firstTime = false + User.queue.async { + let user = User() + XCTAssertEqual(analyticsId, user.analyticsId) + XCTAssertFalse(user.firstTime) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testInstall() { + let date = Date() + try? FileManager.default.removeItem(at: FileManager.user) + XCTAssertGreaterThanOrEqual(Int(User().install.timeIntervalSince1970), Int(date.timeIntervalSince1970)) + } + + func testInstallSavesAfterFirst() { + let expect = expectation(description: "") + let user = User() + User.queue.async { + XCTAssertEqual(Int(user.install.timeIntervalSince1970), Int(User().install.timeIntervalSince1970)) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testNotSavingOnLoad() { + let expect = expectation(description: "") + var user = User() + user.firstTime = false + User.shared = user + User.queue.async { + user = User() + try! FileManager.default.removeItem(at: FileManager.user) + XCTAssertFalse(user.firstTime) + User.queue.async { + XCTAssertNotNil(user) + DispatchQueue.main.async { + XCTAssertFalse(FileManager.default.fileExists(atPath: FileManager.user.path)) + expect.fulfill() + } + } + } + waitForExpectations(timeout: 1) + } + + func testAnalyticsId() { + let expect = expectation(description: "") + let id = UUID() + XCTAssertNotEqual(id, User.shared.analyticsId) + User.shared.analyticsId = id + User.queue.async { + let user = User() + XCTAssertEqual(id, user.analyticsId) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testTreeCount() { + let expect = expectation(description: "") + User.shared.searchCount = 123 + User.queue.async { + let user = User() + XCTAssertEqual(123, user.searchCount) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testAdultFilter() { + let expect = expectation(description: "") + User.shared.adultFilter = .off + User.queue.async { + let user = User() + XCTAssertEqual(.off, user.adultFilter) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testMarketCode() { + let expect = expectation(description: "") + User.shared.marketCode = .ar_sa + User.queue.async { + let user = User() + XCTAssertEqual(.ar_sa, user.marketCode) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testAutoComplete() { + let expect = expectation(description: "") + User.shared.autoComplete = false + User.queue.async { + let user = User() + XCTAssertFalse(user.autoComplete) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testState() { + let expect = expectation(description: "") + User.shared.state["lorem"] = "hello" + User.queue.async { + let user = User() + XCTAssertEqual("hello", user.state["lorem"]) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testId() { + let expect = expectation(description: "") + User.shared.id = "hello world" + User.queue.async { + let user = User() + XCTAssertEqual("hello world", user.id) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testPersonalized() { + let expect = expectation(description: "") + User.shared.personalized = true + User.queue.async { + let user = User() + XCTAssertEqual(true, user.personalized) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testSendAnonymousUsageData() { + let expect = expectation(description: "") + User.shared.sendAnonymousUsageData = false + User.queue.async { + let user = User() + XCTAssertEqual(false, user.personalized) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testSendAnonymousUsageDataDefaultsToTrue() { + let user = User() + XCTAssertEqual(true, user.sendAnonymousUsageData) + } + + func testMigrated() { + let expect = expectation(description: "") + XCTAssert(User.shared.migrated == false) + User.shared.migrated = true + User.queue.async { + let user = User() + XCTAssertEqual(true, user.migrated) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testNews() { + let expect = expectation(description: "") + XCTAssertEqual(.distantPast, User.shared.news) + User.shared.news = Date() + User.queue.async { + let user = User() + XCTAssertNotNil(user.news) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testShowTopSites() { + let expect = expectation(description: "") + XCTAssertEqual(true, User.shared.showTopSites) + User.shared.showTopSites = false + User.queue.async { + let user = User() + XCTAssertEqual(false, user.showTopSites) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testTopSitesRows() { + let expect = expectation(description: "") + XCTAssertEqual(4, User.shared.topSitesRows) + User.shared.topSitesRows = 2 + User.queue.async { + let user = User() + XCTAssertEqual(2, user.topSitesRows) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testShowClimateImpact() { + let expect = expectation(description: "") + XCTAssertEqual(true, User.shared.showClimateImpact) + User.shared.showClimateImpact = false + User.queue.async { + let user = User() + XCTAssertEqual(false, user.showClimateImpact) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testShowEcosiaNews() { + let expect = expectation(description: "") + XCTAssertEqual(true, User.shared.showEcosiaNews) + User.shared.showEcosiaNews = false + User.queue.async { + let user = User() + XCTAssertEqual(false, user.showEcosiaNews) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testWhatsNewItemsVersions() { + let expect = expectation(description: "") + XCTAssertTrue(User.shared.whatsNewItemsVersionsShown.isEmpty) + User.shared.whatsNewItemsVersionsShown.formUnion(["test", "test1", "test"]) + User.queue.async { + let user = User() + XCTAssertEqual(user.whatsNewItemsVersionsShown, ["test", "test1"]) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } + + func testShowsReferralSpotlight() { + let expect = expectation(description: "") + XCTAssertFalse(User.shared.showsReferralSpotlight) + + // set install to 4 days ago + User.shared.install = Calendar.current.date(byAdding: .day, value: -4, to: .init())! + + User.queue.async { + let user = User() + XCTAssertTrue(user.showsReferralSpotlight) + + User.shared.hideReferralSpotlight() + User.queue.async { + let user = User() + XCTAssertFalse(user.showsReferralSpotlight) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testShowsInactiveTabsTooltip() { + let expect = expectation(description: "") + XCTAssertTrue(User.shared.showsInactiveTabsTooltip) + + User.queue.async { + let user = User() + XCTAssertTrue(user.showsInactiveTabsTooltip) + + User.shared.hideInactiveTabsTooltip() + User.queue.async { + let user = User() + XCTAssertFalse(user.showsInactiveTabsTooltip) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testShowsBookmarksImportExportTooltip() { + let expect = expectation(description: "") + User.shared.state = [:] + + XCTAssertTrue(User.shared.showsBookmarksImportExportTooltip) + + User.queue.async { + let user = User() + XCTAssertTrue(user.showsBookmarksImportExportTooltip) + + User.shared.hideBookmarksImportExportTooltip() + User.queue.async { + let user = User() + XCTAssertFalse(user.showsBookmarksImportExportTooltip) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testCookieCounterConsent() { + let expect = expectation(description: "") + User.queue.async { + let user = User() + XCTAssertNil(user.cookieConsentValue) + User.shared.cookieConsentValue = "eamp" + User.queue.async { + let user = User() + XCTAssertEqual("eamp", user.cookieConsentValue) + expect.fulfill() + } + } + waitForExpectations(timeout: 1) + } + + func testSearchSettingChangeNotifiaction() { + let expect = expectation(description: "") + var count = 0 + + NotificationCenter.default.addObserver(forName: .searchSettingsChanged, object: nil, queue: .main) { _ in + + count += 1 + + if count == 4 { + expect.fulfill() + } + } + + User.shared.personalized = !User.shared.personalized + User.shared.marketCode = .en_ww + User.shared.autoComplete = !User.shared.autoComplete + User.shared.adultFilter = .off + + wait(for: [expect], timeout: 1) + } + + func testAnalyticsUserState() { + let expect = expectation(description: "") + User.shared.analyticsUserState = User.AnalyticsStateContext() + User.queue.async { + let user = User() + XCTAssertEqual(User.PushNotificationState.notDetermined, user.analyticsUserState.pushNotificationState) + expect.fulfill() + } + waitForExpectations(timeout: 1) + } +} +// swiftlint:enable force_try diff --git a/firefox-ios/EcosiaTests/Core/Versions/User5_3.swift b/firefox-ios/EcosiaTests/Core/Versions/User5_3.swift new file mode 100644 index 0000000000000..dbe9ab10900a8 --- /dev/null +++ b/firefox-ios/EcosiaTests/Core/Versions/User5_3.swift @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Ecosia + +struct User5_3: Codable { + var install: Date? + var news: Date? + var analyticsId = UUID() + var marketCode = Local.make(for: .current) + var adultFilter = AdultFilter.moderate + var autoComplete = true + var firstTime = true + var personalized: Bool? = false + var topSites: Bool? = true + var migrated: Bool? = false + var id: String? + var treeCount = 0 + var state = [String: String]() + + init() { + install = .init() + } + + enum Key: String { + case + welcomeScreen + } +} diff --git a/firefox-ios/EcosiaTests/EcosiaHomeViewModelTests.swift b/firefox-ios/EcosiaTests/EcosiaHomeViewModelTests.swift new file mode 100644 index 0000000000000..60feea145ea45 --- /dev/null +++ b/firefox-ios/EcosiaTests/EcosiaHomeViewModelTests.swift @@ -0,0 +1,58 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Common +@testable import Client +@testable import Ecosia + +class EcosiaHomeViewModelTests: XCTestCase { + + var profile: MockProfile! + var tabManager: MockTabManager! + var referrals: Referrals! + var theme: Theme! + + override func setUp() { + super.setUp() + + profile = MockProfile() + tabManager = MockTabManager() + referrals = Referrals() + theme = LightTheme() + + User.shared = User() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) + // Clean user defaults to avoid having flaky test changing the section count + // because message card reach max amount of impressions + if let bundleID = Bundle.main.bundleIdentifier { + UserDefaults.standard.removePersistentDomain(forName: bundleID) + } + DependencyHelperMock().bootstrapDependencies() + } + + override func tearDown() { + super.tearDown() + profile = nil + } + + // MARK: Number of sections + + func testNumberOfSection_withoutUpdatingData_has5Sections() { + let viewModel = HomepageViewModel(profile: profile, + isPrivate: false, + tabManager: tabManager, + referrals: referrals, + theme: theme, + auth: EcosiaAuth(browserViewController: BrowserViewController(profile: profile, tabManager: tabManager))) + User.shared.showClimateImpact = true + + XCTAssertEqual(viewModel.shownSections.count, 5) + XCTAssertEqual(viewModel.shownSections[0], HomepageSectionType.header) + XCTAssertEqual(viewModel.shownSections[1], HomepageSectionType.homepageHeader) + XCTAssertEqual(viewModel.shownSections[2], HomepageSectionType.libraryShortcuts) + XCTAssertEqual(viewModel.shownSections[3], HomepageSectionType.impact) + XCTAssertEqual(viewModel.shownSections[4], HomepageSectionType.ntpCustomization) + } +} diff --git a/firefox-ios/EcosiaTests/EcosiaInstallTypeTests.swift b/firefox-ios/EcosiaTests/EcosiaInstallTypeTests.swift new file mode 100644 index 0000000000000..91e14a271c7c0 --- /dev/null +++ b/firefox-ios/EcosiaTests/EcosiaInstallTypeTests.swift @@ -0,0 +1,126 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client +@testable import Ecosia + +final class EcosiaInstallTypeTests: XCTestCase { + + override func setUpWithError() throws { + UserDefaults.standard.removeObject(forKey: EcosiaInstallType.installTypeKey) + UserDefaults.standard.removeObject(forKey: EcosiaInstallType.currentInstalledVersionKey) + } + + func testGetInstallType_WhenUnknown_ShouldReturnUnknown() { + let type = EcosiaInstallType.get() + XCTAssertEqual(type, .unknown) + } + + func testSetInstallType_ShouldPersistType() { + EcosiaInstallType.set(type: .fresh) + let persistedType = EcosiaInstallType.get() + XCTAssertEqual(persistedType, .fresh) + } + + func testPersistedCurrentVersion_WhenNotSet_ShouldReturnEmptyString() { + let version = EcosiaInstallType.persistedCurrentVersion() + XCTAssertEqual(version, "") + } + + func testUpdateCurrentVersion_ShouldPersistVersion() { + let testVersion = "1.0.0" + EcosiaInstallType.updateCurrentVersion(version: testVersion) + let persistedVersion = EcosiaInstallType.persistedCurrentVersion() + XCTAssertEqual(persistedVersion, testVersion) + } + + func testEvaluateCurrentEcosiaInstallType_WhenUnknown_ShouldSetToFresh() { + User.shared.firstTime = true + let mockVersion = MockAppVersion(version: "1.0.0") + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: mockVersion) + let type = EcosiaInstallType.get() + XCTAssertEqual(type, .fresh) + } + + func testEvaluateCurrentEcosiaInstallType_WhenVersionDiffers_ShouldSetToUpgrade() { + let mockVersion = MockAppVersion(version: "1.0.0") + EcosiaInstallType.set(type: .fresh) + EcosiaInstallType.updateCurrentVersion(version: "0.9.0") + + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: mockVersion) + let type = EcosiaInstallType.get() + XCTAssertEqual(type, .upgrade) + } + + func testEvaluateCurrentEcosiaInstallType_WhenVersionSame_ShouldNotChangeType() { + let mockVersion = MockAppVersion(version: "1.0.0") + EcosiaInstallType.set(type: .fresh) + EcosiaInstallType.updateCurrentVersion(version: "1.0.0") + + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: mockVersion) + let type = EcosiaInstallType.get() + XCTAssertEqual(type, .fresh) + } +} + +extension EcosiaInstallTypeTests { + + func testEvaluateFreshInstallType_WithFirstTimeTrue_And_VersionProvider() { + User.shared.firstTime = true + let versionProvider = MockAppVersionInfoProvider(mockedAppVersion: "1.0.0") + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: versionProvider) + + XCTAssertEqual(EcosiaInstallType.get(), .fresh) + XCTAssertEqual(EcosiaInstallType.persistedCurrentVersion(), "1.0.0") + } + + func testEvaluateUpgradeInstallType_WithFirstTimeFalse_NotStoringNewVersion_And_VersionProvider() { + User.shared.firstTime = false + UserDefaults.standard.set("0.9.0", forKey: EcosiaInstallType.currentInstalledVersionKey) + + let versionProvider = MockAppVersionInfoProvider(mockedAppVersion: "1.0.0") + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: versionProvider) + + XCTAssertEqual(EcosiaInstallType.get(), .upgrade) + XCTAssertEqual(EcosiaInstallType.persistedCurrentVersion(), "0.9.0") + } + + func testEvaluateUpgradeInstallType_WithFirstTimeFalse_StoringNewVersion_And_VersionProvider() { + User.shared.firstTime = false + UserDefaults.standard.set("0.9.0", forKey: EcosiaInstallType.currentInstalledVersionKey) + + let versionProvider = MockAppVersionInfoProvider(mockedAppVersion: "1.0.0") + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: versionProvider, storeUpgradeVersion: true) + + XCTAssertEqual(EcosiaInstallType.get(), .upgrade) + XCTAssertEqual(EcosiaInstallType.persistedCurrentVersion(), "1.0.0") + } + + func testEvaluateUpgradeInstallType_UpdatesVersionOnInstall_WhenFirstTime() { + User.shared.firstTime = true + User.shared.versionOnInstall = "0.0.1" + + let versionProvider = MockAppVersionInfoProvider(mockedAppVersion: "1.0.0") + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: versionProvider) + + XCTAssertEqual(User.shared.versionOnInstall, "1.0.0") + } + + func testEvaluateUpgradeInstallType_DoesNotUpdatesVersionOnInstall_WhenNotFirstTime() { + User.shared.firstTime = false + User.shared.versionOnInstall = "0.0.1" + + let versionProvider = MockAppVersionInfoProvider(mockedAppVersion: "1.0.0") + EcosiaInstallType.evaluateCurrentEcosiaInstallType(withVersionProvider: versionProvider) + + XCTAssertEqual(User.shared.versionOnInstall, "0.0.1") + } +} + +extension EcosiaInstallTypeTests { + struct MockAppVersion: AppVersionInfoProvider { + var version: String + } +} diff --git a/firefox-ios/EcosiaTests/EcosiaNTPTooltipHighlightTests.swift b/firefox-ios/EcosiaTests/EcosiaNTPTooltipHighlightTests.swift new file mode 100644 index 0000000000000..4eb148a1742f5 --- /dev/null +++ b/firefox-ios/EcosiaTests/EcosiaNTPTooltipHighlightTests.swift @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia +@testable import Client + +class EcosiaNTPTooltipHighlightTests: XCTestCase { + + var user: Ecosia.User! + + override func setUpWithError() throws { + try? FileManager().removeItem(at: FileManager.user) + user = .init() + user.firstTime = false + } + + func testFirstTimeReturnsNil() throws { + user.firstTime = true + XCTAssertNil(NTPTooltip.highlight(for: user)) + } + + func testGotClaimed() throws { + user.referrals.isNewClaim = true + XCTAssert(NTPTooltip.highlight(for: user) == .gotClaimed) + } + + func testSuccessfulInvite() throws { + user.referrals.claims = 1 + XCTAssert(NTPTooltip.highlight(for: user) == .successfulInvite) + } + + func testReferralSpotlight() throws { + user.install = Calendar.current.date(byAdding: .day, value: -4, to: .init())! + XCTAssert(NTPTooltip.highlight(for: user) == .referralSpotlight) + } + + func testImpactIntro() throws { + user.showImpactIntro() + XCTAssert(NTPTooltip.highlight(for: user) == .collectiveImpactIntro) + } + + func testFallthrough() throws { + user.hideImpactIntro() + XCTAssertNil(NTPTooltip.highlight(for: user)) + } +} diff --git a/firefox-ios/EcosiaTests/EcosiaOverlayModeManagerTests.swift b/firefox-ios/EcosiaTests/EcosiaOverlayModeManagerTests.swift new file mode 100644 index 0000000000000..82d2c47f46e66 --- /dev/null +++ b/firefox-ios/EcosiaTests/EcosiaOverlayModeManagerTests.swift @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +final class EcosiaOverlayModeManagerTests: XCTestCase { + + private var urlBar: MockURLBarView! + private var subject: MockOverlayModeManager! + + override func setUp() { + super.setUp() + urlBar = MockURLBarView() + subject = MockOverlayModeManager() + } + + override func tearDown() { + super.tearDown() + urlBar = nil + subject = nil + } + + // MARK: - Test URLBarView Overlay override nil + func testOverridesToEnterOverlayMode_EntersOverlayMode_ForNewTabHome_WithNilURL() { + subject.setURLBar(urlBarView: urlBar) + subject.openNewTab(url: nil, newTabSettings: .topSites) + XCTAssertFalse(subject.inOverlayMode) + subject.overrideShouldEnterOverlayMode = true + subject.openNewTab(url: nil, newTabSettings: .topSites) + XCTAssertTrue(subject.inOverlayMode) + XCTAssertEqual(subject.enterOverlayModeCallCount, 2) + } + + func testOverridesToEnterOverlayMode_EntersOverlayMode_ForNewTabHome_WithHomeURL() { + subject.setURLBar(urlBarView: urlBar) + subject.openNewTab(url: URL(string: "internal://local/about/home"), + newTabSettings: .topSites) + XCTAssertFalse(subject.inOverlayMode) + subject.overrideShouldEnterOverlayMode = true + subject.openNewTab(url: nil, newTabSettings: .topSites) + XCTAssertTrue(subject.inOverlayMode) + XCTAssertEqual(subject.enterOverlayModeCallCount, 2) + } +} diff --git a/firefox-ios/EcosiaTests/EcosiaPageActionMenuCellTests.swift b/firefox-ios/EcosiaTests/EcosiaPageActionMenuCellTests.swift new file mode 100644 index 0000000000000..3ca0c59b098d5 --- /dev/null +++ b/firefox-ios/EcosiaTests/EcosiaPageActionMenuCellTests.swift @@ -0,0 +1,87 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +class EcosiaPageActionMenuCellTests: XCTestCase { + + func testDetermineTableViewCellPositionAtReturnsSoloAsPositionForASignleItemArray() { + // Arrange + let actions = [[anyPhotonRowAction(), anyPhotonRowAction()], + [anyPhotonRowAction()], + [anyPhotonRowAction(), anyPhotonRowAction(), anyPhotonRowAction()]] + + let indexPath = IndexPath(row: 1, section: 1) + let cell = makeSUT(at: indexPath) + + // Act + let position = cell.determineTableViewCellPositionAt(indexPath, forActions: actions) + + // Assert + XCTAssertEqual(position, .solo) + } + + func testDetermineTableViewCellPositionAtReturnsFirstAsPositionForMoreThanAnItemInFirstArray() { + // Arrange + let actions = [[anyPhotonRowAction(), anyPhotonRowAction()], + [anyPhotonRowAction()]] + + let indexPath = IndexPath(row: 0, section: 0) + let cell = makeSUT(at: indexPath) + + // Act + let position = cell.determineTableViewCellPositionAt(indexPath, forActions: actions) + + // Assert + XCTAssertEqual(position, .first) + } + + func testDetermineTableViewCellPositionAtReturnsLastAsPositionForMoreThanAnItemInThirdArray() { + // Arrange + let actions = [[anyPhotonRowAction(), anyPhotonRowAction()], + [anyPhotonRowAction()], + [anyPhotonRowAction(), anyPhotonRowAction(), anyPhotonRowAction()]] + + let indexPath = IndexPath(row: 2, section: 2) + let cell = makeSUT(at: indexPath) + + // Act + let position = cell.determineTableViewCellPositionAt(indexPath, forActions: actions) + + // Assert + XCTAssertEqual(position, .last) + } + + func testDetermineTableViewCellPositionAtReturnsMiddleAsPositionForAnItemBetweenFirstAndLastItemInSecondArray() { + // Arrange + let actions = [[anyPhotonRowAction(), anyPhotonRowAction()], + [anyPhotonRowAction(), anyPhotonRowAction(), anyPhotonRowAction(), anyPhotonRowAction()], + [anyPhotonRowAction()]] + + let indexPath = IndexPath(row: 2, section: 1) + let cell = makeSUT(at: indexPath) + + // Act + let position = cell.determineTableViewCellPositionAt(indexPath, forActions: actions) + + // Assert + XCTAssertEqual(position, .middle) + } +} + +extension EcosiaPageActionMenuCellTests { + + // MARK: - Helpers + + func makeSUT(at indexPath: IndexPath) -> PageActionMenuCell { + let tableView = UITableView() + tableView.register(PageActionMenuCell.self, forCellReuseIdentifier: PageActionMenuCell.UX.cellIdentifier) + return tableView.dequeueReusableCell(withIdentifier: PageActionMenuCell.UX.cellIdentifier, for: indexPath) as! PageActionMenuCell + } + + private func anyPhotonRowAction() -> PhotonRowActions { + PhotonRowActions(SingleActionViewModel(title: "any string")) + } +} diff --git a/firefox-ios/EcosiaTests/EcosiaPerformanceTestHistory.swift b/firefox-ios/EcosiaTests/EcosiaPerformanceTestHistory.swift new file mode 100644 index 0000000000000..0a37c7bd280d7 --- /dev/null +++ b/firefox-ios/EcosiaTests/EcosiaPerformanceTestHistory.swift @@ -0,0 +1,117 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +/** + * EcosiaPerformanceTestHistory + * + * Context: + * As part of PR #731, we addressed an issue with blank migrated tabs during an upgrade. While the issue was fixed for new upgrades, users who had already upgraded continued to experience this problem. This class was created to ensure that tabs without URLs can be restored by fetching the URLs from an alternative source, specifically the places database (`places.db`). + * + * This class extends `ProfileTest` and is designed to test the performance of the `getSitesWithBound` function + * in the `RustPlaces` API. It performs a series of performance tests with different limits and numbers of entries + * in the database to measure and analyze execution times. + * + * Approach inspired by `TestHistory.swift`: + * The class contains helper methods gotten from the above mentioned class: + * - It can add either individual or multiple site entries into the database, facilitating bulk data insertion for testing purposes. + * - It includes a method to clear the database, ensuring a clean state before each test run. + * - The core testing function measures and logs the performance of the `getSitesWithBound` function under varying conditions. + * + * Each test method is tailored to a specific combination of limit and entry count. + */ + +@testable import Client +import Foundation +import Storage +import MozillaAppServices +import XCTest + +final class EcosiaPerformanceTestHistory: ProfileTest { + + private func addSite(_ places: RustPlaces, url: String, title: String, bool: Bool = true, visitType: VisitType = .link) { + _ = places.reopenIfClosed() + let site = Site(url: url, title: title) + let visit = VisitObservation(url: site.url, title: site.title, visitType: visitType) + let res = places.applyObservation(visitObservation: visit).value + XCTAssertEqual(bool, res.isSuccess, "Site added: \(url)., error value: \(res.failureValue ?? "wow")") + } + + private func addSites(_ places: RustPlaces, count: Int) { + for index in 0.. Theme { + getCurrentThemeCallCount += 1 + return currentThemeStorage + } + + var window: UIWindow? + var windowUUID: WindowUUID = .XCTestDefaultUUID + + var systemThemeIsOn = true + + var automaticBrightnessIsOn: Bool { return false} + + var automaticBrightnessValue: Float { return 0.4} + + func getInterfaceStyle() -> UIUserInterfaceStyle { + return .light + } + + func setManualTheme(to newTheme: ThemeType) { + switch newTheme { + case .light: + currentThemeStorage = EcosiaLightTheme() + case .dark: + currentThemeStorage = EcosiaDarkTheme() + case .nightMode: + currentThemeStorage = EcosiaDarkTheme() + case .privateMode: + currentThemeStorage = EcosiaLightTheme() + } + } + + func applyThemeUpdatesToWindows() { } + + func systemThemeChanged() {} + + func setSystemTheme(isOn: Bool) { + systemThemeIsOn = isOn + } + + func setPrivateTheme(isOn: Bool, for window: UUID) {} + + func getPrivateThemeIsOn(for window: UUID) -> Bool { return false } + + func setAutomaticBrightness(isOn: Bool) {} + + func setAutomaticBrightnessValue(_ value: Float) { + let screenLessThanPref = Float(UIScreen.main.brightness) < value + + if screenLessThanPref, getCurrentTheme(for: windowUUID).type == .light { + setManualTheme(to: .dark) + } else if !screenLessThanPref, getCurrentTheme(for: windowUUID).type == .dark { + setManualTheme(to: .light) + } + } + + func updateThemeBasedOnBrightess() { } + + func getUserManualTheme() -> ThemeType { return currentThemeStorage.type } + + func reloadTheme(for window: UUID) { } + + func setWindow(_ window: UIWindow, for uuid: UUID) { } + + func windowDidClose(uuid: UUID) { } + + func windowNonspecificTheme() -> Theme { return currentThemeStorage } +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockAppVersionInfoProvider.swift b/firefox-ios/EcosiaTests/Mocks/MockAppVersionInfoProvider.swift new file mode 100644 index 0000000000000..4743c9f90e10d --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockAppVersionInfoProvider.swift @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Client +@testable import Ecosia + +struct MockAppVersionInfoProvider: AppVersionInfoProvider { + + var mockedAppVersion: String + + init(mockedAppVersion: String) { + self.mockedAppVersion = mockedAppVersion + } + + var version: String { + mockedAppVersion + } +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockHTTPCookieStore.swift b/firefox-ios/EcosiaTests/Mocks/MockHTTPCookieStore.swift new file mode 100644 index 0000000000000..dcfaec687e16b --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockHTTPCookieStore.swift @@ -0,0 +1,20 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia +import WebKit + +class MockHTTPCookieStore: CookieStoreProtocol { + private var cookies: [HTTPCookie] = [] + + func allCookies() async -> [HTTPCookie] { + return cookies + } + + func setCookie(_ cookie: HTTPCookie) async { + // Remove existing cookie with same name/domain/path + cookies.removeAll { $0.name == cookie.name && $0.domain == cookie.domain && $0.path == cookie.path } + cookies.append(cookie) + } +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockLocale.swift b/firefox-ios/EcosiaTests/Mocks/MockLocale.swift new file mode 100644 index 0000000000000..edb0e8b0959e1 --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockLocale.swift @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Ecosia + +struct MockLocale: RegionLocatable { + var regionIdentifierLowercasedWithFallbackValue: String + var englishLocalizedCountryName: String? + + init(_ countryIdentier: String, countryName: String? = nil) { + self.regionIdentifierLowercasedWithFallbackValue = countryIdentier + self.englishLocalizedCountryName = countryName + } +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockNewsModel.swift b/firefox-ios/EcosiaTests/Mocks/MockNewsModel.swift new file mode 100644 index 0000000000000..24ead6d0516a2 --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockNewsModel.swift @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Ecosia + +func createMockNewsModel() throws -> NewsModel? { + let currentTimestamp = Date().timeIntervalSince1970 + let jsonString = """ + { + "id": 123, + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "language": "en", + "publishDate": \(currentTimestamp), + "imageUrl": "https://example.com/image.jpg", + "targetUrl": "https://example.com/news", + "trackingName": "example_news_tracking" + } + """ + let jsonData = Data(jsonString.utf8) + let decoder = JSONDecoder() + + // Custom date decoding strategy if needed + decoder.dateDecodingStrategy = .secondsSince1970 + return try decoder.decode(NewsModel.self, from: jsonData) +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockUNNotificationSettings.swift b/firefox-ios/EcosiaTests/Mocks/MockUNNotificationSettings.swift new file mode 100644 index 0000000000000..530cf3a654006 --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockUNNotificationSettings.swift @@ -0,0 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Client +@testable import Ecosia + +struct MockUNNotificationSettings: AnalyticsUNNotificationSettingsProtocol { + var authorizationStatus: UNAuthorizationStatus +} + +class MockAnalyticsUserNotificationCenter: AnalyticsUserNotificationCenterProtocol { + private let mockSettings: AnalyticsUNNotificationSettingsProtocol + + init(mockSettings: AnalyticsUNNotificationSettingsProtocol) { + self.mockSettings = mockSettings + } + + func getNotificationSettingsProtocol(completionHandler: @escaping (AnalyticsUNNotificationSettingsProtocol) -> Void) { + completionHandler(mockSettings) + } +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockUnleash.swift b/firefox-ios/EcosiaTests/Mocks/MockUnleash.swift new file mode 100644 index 0000000000000..baef7b91a5b5d --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockUnleash.swift @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia + +/// Mock implementation of UnleashProtocol for testing +public enum MockUnleash: UnleashProtocol { + + private static var _isLoaded = false + private static var _enabledFlags: Set = [] + + /// Indicates whether Unleash has been loaded from filesystem or network + public static var isLoaded: Bool { + get { _isLoaded } + set { _isLoaded = newValue } + } + + /// Checks if a toggle with the given name exists and is enabled. + /// - Parameter name: The name of the toggle. + /// - Returns: `true` if the toggle is enabled, `false` otherwise. + public static func isEnabled(_ flag: Unleash.Toggle.Name) -> Bool { + return _enabledFlags.contains(flag) + } + + // MARK: - Test Helper Methods + + /// Sets the loaded state for testing + /// - Parameter loaded: Whether Unleash should be considered loaded + public static func setLoaded(_ loaded: Bool) { + _isLoaded = loaded + } + + /// Sets a flag as enabled for testing + /// - Parameter flag: The flag to enable + public static func setEnabled(_ flag: Unleash.Toggle.Name) { + _enabledFlags.insert(flag) + } + + /// Sets a flag as disabled for testing + /// - Parameter flag: The flag to disable + public static func setDisabled(_ flag: Unleash.Toggle.Name) { + _enabledFlags.remove(flag) + } + + /// Sets multiple flags as enabled for testing + /// - Parameter flags: The flags to enable + public static func setEnabled(_ flags: [Unleash.Toggle.Name]) { + _enabledFlags.formUnion(flags) + } + + /// Resets all mock state to default (not loaded, no flags enabled) + public static func reset() { + _isLoaded = false + _enabledFlags.removeAll() + } +} diff --git a/firefox-ios/EcosiaTests/Mocks/MockWelcomeDelegate.swift b/firefox-ios/EcosiaTests/Mocks/MockWelcomeDelegate.swift new file mode 100644 index 0000000000000..212876e719fb8 --- /dev/null +++ b/firefox-ios/EcosiaTests/Mocks/MockWelcomeDelegate.swift @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Ecosia + +final class MockWelcomeDelegate: WelcomeDelegate { + func welcomeDidRequestSignIn(_ welcome: Ecosia.WelcomeViewController) {} + + func welcomeDidFinish(_ welcome: WelcomeViewController) {} +} diff --git a/firefox-ios/EcosiaTests/PrivateModeButtonTests.swift b/firefox-ios/EcosiaTests/PrivateModeButtonTests.swift new file mode 100644 index 0000000000000..283c3419cae41 --- /dev/null +++ b/firefox-ios/EcosiaTests/PrivateModeButtonTests.swift @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +final class PrivateModeButtonTests: XCTestCase { + + var button: PrivateModeButton! + var lightTheme = EcosiaLightTheme() + var darkTheme = EcosiaDarkTheme() + + override func setUp() { + super.setUp() + button = PrivateModeButton(frame: .zero) + } + + func testApplyUIMode_Private_LightMode() { + button.applyUIMode(isPrivate: true, theme: lightTheme) + + XCTAssertEqual(button.tintColor, lightTheme.colors.ecosia.backgroundPrimary) + XCTAssertEqual(button.imageView?.tintColor, lightTheme.colors.ecosia.backgroundPrimary) + XCTAssertEqual(button.accessibilityValue, .TabTrayToggleAccessibilityValueOn) + } + + func testApplyUIMode_NotPrivate_LightMode() { + button.applyUIMode(isPrivate: false, theme: lightTheme) + + XCTAssertEqual(button.tintColor, lightTheme.colors.textPrimary) + XCTAssertEqual(button.imageView?.tintColor, lightTheme.colors.textPrimary) + XCTAssertEqual(button.accessibilityValue, .TabTrayToggleAccessibilityValueOff) + } + + func testApplyUIMode_Private_DarkMode() { + button.applyUIMode(isPrivate: true, theme: darkTheme) + + XCTAssertEqual(button.tintColor, darkTheme.colors.layer1) + XCTAssertEqual(button.imageView?.tintColor, darkTheme.colors.layer1) + XCTAssertEqual(button.accessibilityValue, .TabTrayToggleAccessibilityValueOn) + } + + func testApplyUIMode_NotPrivate_DarkMode() { + button.applyUIMode(isPrivate: false, theme: darkTheme) + + XCTAssertEqual(button.tintColor, darkTheme.colors.textPrimary) + XCTAssertEqual(button.imageView?.tintColor, darkTheme.colors.textPrimary) + XCTAssertEqual(button.accessibilityValue, .TabTrayToggleAccessibilityValueOff) + } + + func testApplyTheme_Selected_LightMode() { + button.isSelected = true + button.applyTheme(theme: lightTheme) + + XCTAssertEqual(button.tintColor, lightTheme.colors.iconOnColor) + XCTAssertEqual(button.imageView?.tintColor, lightTheme.colors.iconOnColor) + } + + func testApplyTheme_NotSelected_LightMode() { + button.isSelected = false + button.applyTheme(theme: lightTheme) + + XCTAssertEqual(button.tintColor, lightTheme.colors.iconPrimary) + XCTAssertEqual(button.imageView?.tintColor, lightTheme.colors.iconPrimary) + } + + func testApplyTheme_Selected_DarkMode() { + button.isSelected = true + button.applyTheme(theme: darkTheme) + + XCTAssertEqual(button.tintColor, darkTheme.colors.iconOnColor) + XCTAssertEqual(button.imageView?.tintColor, darkTheme.colors.iconOnColor) + } + + func testApplyTheme_NotSelected_DarkMode() { + button.isSelected = false + button.applyTheme(theme: darkTheme) + + XCTAssertEqual(button.tintColor, darkTheme.colors.iconPrimary) + XCTAssertEqual(button.imageView?.tintColor, darkTheme.colors.iconPrimary) + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/DeviceType.swift b/firefox-ios/EcosiaTests/SnapshotTests/DeviceType.swift new file mode 100644 index 0000000000000..f97728681768c --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/DeviceType.swift @@ -0,0 +1,258 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapshotTesting +import UIKit + +/// An enum representing different device types and their corresponding `ViewImageConfig` configurations. +enum DeviceType: String, CaseIterable { + case iPhoneSE_Portrait + case iPhoneSE_Landscape + case iPhone16Pro_Portrait + case iPhone16Pro_Landscape + case iPhone16ProMax_Portrait + case iPhone16ProMax_Landscape + case iPadPro_Portrait + case iPadPro_Landscape + + var config: ViewImageConfig { + switch self { + case .iPhoneSE_Portrait: + return ViewImageConfig.iPhone8(.portrait) + case .iPhoneSE_Landscape: + return ViewImageConfig.iPhone8(.landscape) + case .iPhone16Pro_Portrait: + return ViewImageConfig.iPhone13Pro(.portrait) + case .iPhone16Pro_Landscape: + return ViewImageConfig.iPhone13Pro(.landscape) + case .iPhone16ProMax_Portrait: + return ViewImageConfig.iPhone13ProMax(.portrait) + case .iPhone16ProMax_Landscape: + return ViewImageConfig.iPhone13ProMax(.landscape) + case .iPadPro_Portrait: + return ViewImageConfig.iPadPro11(.portrait) + case .iPadPro_Landscape: + return ViewImageConfig.iPadPro11(.landscape) + } + } + + var name: String { + switch self { + case .iPhoneSE_Portrait, .iPhoneSE_Landscape: + return "iPhone SE (3rd generation)" + case .iPhone16Pro_Portrait, .iPhone16Pro_Landscape: + return "iPhone 16 Pro" + case .iPhone16ProMax_Portrait, .iPhone16ProMax_Landscape: + return "iPhone 16 Pro Max" + case .iPadPro_Portrait, .iPadPro_Landscape: + return "iPad Pro 11-inch (M4)" + } + } + + /// Returns a `DeviceType` based on the provided device name and orientation. + /// + /// - Parameters: + /// - deviceName: The name of the device (e.g., "iPhone 8"). + /// - orientation: The orientation of the device (e.g., "portrait"). + /// - Returns: The corresponding `DeviceType` or crashes 💥 if the combination is not supported. + static func from(deviceName: String, orientation: String) -> DeviceType { + switch (deviceName, orientation) { + case ("iPhone SE (3rd generation)", "portrait"): + return .iPhoneSE_Portrait + case ("iPhone SE (3rd generation)", "landscape"): + return .iPhoneSE_Landscape + case ("iPhone 16 Pro", "portrait"): + return .iPhone16Pro_Portrait + case ("iPhone 16 Pro", "landscape"): + return .iPhone16Pro_Landscape + case ("iPhone 16 Pro Max", "portrait"): + return .iPhone16ProMax_Portrait + case ("iPhone 16 Pro Max", "landscape"): + return .iPhone16ProMax_Landscape + case ("iPad Pro 11-inch (M4)", "portrait"): + return .iPadPro_Portrait + case ("iPad Pro 11-inch (M4)", "landscape"): + return .iPadPro_Landscape + default: + fatalError("Device Name \(deviceName) and Orientation \(orientation) not found. Please add them correctly.") + } + } +} + +// From: https://github.com/pointfreeco/swift-snapshot-testing/pull/839 + +extension ViewImageConfig { + + // https://useyourloaf.com/blog/iphone-15-screen-sizes/ + public static let iPhone15 = ViewImageConfig.iPhone15(.portrait) + + public static func iPhone15(_ orientation: Orientation) -> ViewImageConfig { + let safeArea: UIEdgeInsets + let size: CGSize + switch orientation { + case .landscape: + safeArea = .init(top: 0, left: 59, bottom: 21, right: 59) + size = .init(width: 852, height: 393) + case .portrait: + safeArea = .init(top: 59, left: 0, bottom: 34, right: 0) + size = .init(width: 393, height: 852) + } + + return .init(safeArea: safeArea, size: size, traits: .iPhone15(orientation)) + } + + public static let iPhone15Plus = ViewImageConfig.iPhone15Plus(.portrait) + + public static func iPhone15Plus(_ orientation: Orientation) -> ViewImageConfig { + let safeArea: UIEdgeInsets + let size: CGSize + switch orientation { + case .landscape: + safeArea = .init(top: 0, left: 59, bottom: 21, right: 59) + size = .init(width: 932, height: 430) + case .portrait: + safeArea = .init(top: 59, left: 0, bottom: 34, right: 0) + size = .init(width: 430, height: 932) + } + + return .init(safeArea: safeArea, size: size, traits: .iPhone15Plus(orientation)) + } + + public static let iPhone15Pro = ViewImageConfig.iPhone15Pro(.portrait) + + public static func iPhone15Pro(_ orientation: Orientation) -> ViewImageConfig { + let safeArea: UIEdgeInsets + let size: CGSize + switch orientation { + case .landscape: + safeArea = .init(top: 0, left: 59, bottom: 21, right: 59) + size = .init(width: 852, height: 393) + case .portrait: + safeArea = .init(top: 59, left: 0, bottom: 34, right: 0) + size = .init(width: 393, height: 852) + } + + return .init(safeArea: safeArea, size: size, traits: .iPhone15Pro(orientation)) + } + + public static let iPhone15ProMax = ViewImageConfig.iPhone15ProMax(.portrait) + + public static func iPhone15ProMax(_ orientation: Orientation) -> ViewImageConfig { + let safeArea: UIEdgeInsets + let size: CGSize + switch orientation { + case .landscape: + safeArea = .init(top: 0, left: 59, bottom: 21, right: 59) + size = .init(width: 932, height: 430) + case .portrait: + safeArea = .init(top: 59, left: 0, bottom: 34, right: 0) + size = .init(width: 430, height: 932) + } + + return .init(safeArea: safeArea, size: size, traits: .iPhone15ProMax(orientation)) + } +} + +extension UITraitCollection { + + public static func iPhone15(_ orientation: ViewImageConfig.Orientation) -> UITraitCollection { + let base: [UITraitCollection] = [ + .init(forceTouchCapability: .unavailable), + .init(layoutDirection: .leftToRight), + .init(preferredContentSizeCategory: .medium), + .init(userInterfaceIdiom: .phone) + ] + switch orientation { + case .landscape: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .regular), + .init(verticalSizeClass: .compact) + ] + ) + case .portrait: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .compact), + .init(verticalSizeClass: .regular) + ] + ) + } + } + + public static func iPhone15Plus(_ orientation: ViewImageConfig.Orientation) -> UITraitCollection { + let base: [UITraitCollection] = [ + .init(forceTouchCapability: .unavailable), + .init(layoutDirection: .leftToRight), + .init(preferredContentSizeCategory: .medium), + .init(userInterfaceIdiom: .phone) + ] + switch orientation { + case .landscape: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .regular), + .init(verticalSizeClass: .compact) + ] + ) + case .portrait: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .compact), + .init(verticalSizeClass: .regular) + ] + ) + } + } + + public static func iPhone15Pro(_ orientation: ViewImageConfig.Orientation) -> UITraitCollection { + let base: [UITraitCollection] = [ + .init(forceTouchCapability: .unavailable), + .init(layoutDirection: .leftToRight), + .init(preferredContentSizeCategory: .medium), + .init(userInterfaceIdiom: .phone) + ] + switch orientation { + case .landscape: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .regular), + .init(verticalSizeClass: .compact) + ] + ) + case .portrait: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .compact), + .init(verticalSizeClass: .regular) + ] + ) + } + } + + public static func iPhone15ProMax(_ orientation: ViewImageConfig.Orientation) -> UITraitCollection { + let base: [UITraitCollection] = [ + .init(forceTouchCapability: .unavailable), + .init(layoutDirection: .leftToRight), + .init(preferredContentSizeCategory: .medium), + .init(userInterfaceIdiom: .phone) + ] + switch orientation { + case .landscape: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .regular), + .init(verticalSizeClass: .compact) + ] + ) + case .portrait: + return .init( + traitsFrom: base + [ + .init(horizontalSizeClass: .compact), + .init(verticalSizeClass: .regular) + ] + ) + } + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/LocaleRetriever.swift b/firefox-ios/EcosiaTests/SnapshotTests/LocaleRetriever.swift new file mode 100644 index 0000000000000..48a0070ce97a2 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/LocaleRetriever.swift @@ -0,0 +1,26 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct LocaleRetriever { + + private init() {} + + // Function to get locales from the JSON file + static func getLocales() -> [Locale] { + // Load the JSON file from the app bundle + guard let testBundle = Bundle(identifier: "com.ecosia.ecosiaapp.EcosiaSnapshotTests"), + let path = testBundle.path(forResource: "environment", ofType: "json"), + let data = try? Data(contentsOf: URL(fileURLWithPath: path)), + let json = try? JSONSerialization.jsonObject(with: data, options: []), + let dict = json as? [String: String], + let localesString = dict["LOCALES"] else { + // Fallback to default locale if the JSON file is not found or cannot be parsed + return [Locale(identifier: "en")] + } + let localeIdentifiers = localesString.split(separator: ",").map({ String($0) }) + return localeIdentifiers.map { Locale(identifier: $0) } + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/LocalizationOverrideTestingBundle.swift b/firefox-ios/EcosiaTests/SnapshotTests/LocalizationOverrideTestingBundle.swift new file mode 100644 index 0000000000000..4aa21ed421017 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/LocalizationOverrideTestingBundle.swift @@ -0,0 +1,21 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import ObjectiveC.runtime +import Ecosia + +var overriddenLocaleIdentifier: String = "" + +final class LocalizationOverrideTestingBundle: Bundle { + + override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String { + // You can dynamically choose the table based on a stored locale or use a predetermined table + guard let path = Bundle.ecosia.path(forResource: overriddenLocaleIdentifier, ofType: "lproj"), + let bundle = Bundle(path: path) else { + return super.localizedString(forKey: key, value: value, table: tableName) + } + return bundle.localizedString(forKey: key, value: value, table: tableName) + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/NTP/NTPComponentTests.swift b/firefox-ios/EcosiaTests/SnapshotTests/NTP/NTPComponentTests.swift new file mode 100644 index 0000000000000..a3cab722bb508 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/NTP/NTPComponentTests.swift @@ -0,0 +1,121 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapshotTesting +import XCTest +import Common +import Shared +import Ecosia +import MozillaAppServices +@testable import Client + +final class NTPComponentTests: SnapshotBaseTests { + + private let commonWidth = 375 + + func testNTPLogoCell() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + let logo = NTPLogoCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + logo.applyTheme(theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return logo + }) + } + + func testNTPLibraryCell() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + let libraryCell = NTPLibraryCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + libraryCell.applyTheme(theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return libraryCell + }) + } + + func testNTPReferralMultipleInvitesCell() { + impactInfoReferralCellWithInvites(2) + } + + func testNTPReferralSingleInviteCell() { + impactInfoReferralCellWithInvites(1) + } + + func testNTPTotalTreesCell() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + let cell = NTPImpactCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + let mockInfoItemSection: ClimateImpactInfo = .totalTrees(value: 200356458) + cell.configure(items: [mockInfoItemSection], delegate: nil, theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return cell + }) + } + + func testNTPTotalInvestedCell() { + /* + Precision to .97 to accommodate differences in Locale formatter + as not possible to update Locale.current on the fly nor swizzle it + */ + SnapshotTestHelper.assertSnapshot(initializingWith: { + let cell = NTPImpactCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + let mockInfoItemSection: ClimateImpactInfo = .totalInvested(value: 89942822) + cell.configure(items: [mockInfoItemSection], delegate: nil, theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return cell + }, + precision: 0.97) + } + + func testNTPNewsCell() { + do { + // Precision to .98 to accommodate different timestamps + let mockNews = try createMockNewsModel() + SnapshotTestHelper.assertSnapshot(initializingWith: { + let cell = NTPNewsCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + cell.configure(mockNews!, images: Images(.init(configuration: .ephemeral)), row: 0, totalCount: 1) + cell.applyTheme(theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return cell + }, + precision: 0.98) + } catch { + XCTFail("Failed to create mock NewsModel: \(error)") + } + } + + func testNTPCustomizationCell() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + let customizationCell = NTPCustomizationCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + customizationCell.applyTheme(theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return customizationCell + }) + } +} + +extension NTPComponentTests { + + private func impactInfoReferralCellWithInvites(_ invites: Int) { + let invitesTestNameString = invites > 1 ? "multiple_invites" : "single_invite" + SnapshotTestHelper.assertSnapshot(initializingWith: { + let cell = NTPImpactCell(frame: CGRect(x: 0, y: 0, width: self.commonWidth, height: 100)) + let mockInfoItemSection: ClimateImpactInfo = .referral(value: invites) + cell.configure(items: [mockInfoItemSection], delegate: nil, theme: self.themeManager.getCurrentTheme(for: .snapshotTestDefaultUUID)) + return cell + }, testName: "testNTPReferralInvitesCell_\(invitesTestNameString)") + } + + private func createMockNewsModel() throws -> NewsModel? { + let currentTimestamp = Date().timeIntervalSince1970 + let jsonString = """ + { + "id": 123, + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + "language": "en", + "publishDate": \(currentTimestamp), + "imageUrl": "https://example.com/image.jpg", + "targetUrl": "https://example.com/news", + "trackingName": "example_news_tracking" + } + """ + let jsonData = Data(jsonString.utf8) + let decoder = JSONDecoder() + + // Custom date decoding strategy if needed + decoder.dateDecodingStrategy = .secondsSince1970 + return try decoder.decode(NewsModel.self, from: jsonData) + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/NTP/NTPTests.swift b/firefox-ios/EcosiaTests/SnapshotTests/NTP/NTPTests.swift new file mode 100644 index 0000000000000..1c58b8645c2ec --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/NTP/NTPTests.swift @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapshotTesting +import XCTest +import Ecosia +import Common +@testable import Client + +final class NTPTests: SnapshotBaseTests { + + func testNTPShowingImpactIntro() { + User.shared.showImpactIntro() + snapshotNTP(impactIntroShown: true) + } + + func testNTPImpactIntroHidden() { + User.shared.hideImpactIntro() + snapshotNTP(impactIntroShown: false) + } +} + +extension NTPTests { + fileprivate func snapshotNTP(impactIntroShown: Bool) { + SnapshotTestHelper.assertSnapshot(initializingWith: { + let urlBar = URLBarView(profile: self.profile, windowUUID: .snapshotTestDefaultUUID) + let overlayManager = MockOverlayModeManager() + overlayManager.setURLBar(urlBarView: urlBar) + + let homePageViewController = LegacyHomepageViewController(profile: self.profile, + toastContainer: UIView(), + tabManager: MockTabManager(), + overlayManager: overlayManager, + referrals: .init(), + delegate: nil, + auth: EcosiaAuth(browserViewController: BrowserViewController(profile: self.profile, tabManager: MockTabManager()))) + return homePageViewController + }, + // Precision at .95 to accommodate a snapshot looking slightly different due to the different data output + // from the statistics json + precision: 0.95, + testName: impactIntroShown ? "NTP_with_impact_intro" : "NTP_without_impact_intro") + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/Onboarding/OnboardingTests.swift b/firefox-ios/EcosiaTests/SnapshotTests/Onboarding/OnboardingTests.swift new file mode 100644 index 0000000000000..541f89e39a0a1 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/Onboarding/OnboardingTests.swift @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapshotTesting +import XCTest +@testable import Client +@testable import Ecosia + +final class OnboardingTests: SnapshotBaseTests { + + func testWelcomeScreen() { + SnapshotTestHelper.assertSnapshot(initializingWith: { + WelcomeViewController(delegate: MockWelcomeDelegate(), windowUUID: .snapshotTestDefaultUUID) + }, wait: 1.0) + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/SnapshotArtifacts b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotArtifacts new file mode 160000 index 0000000000000..dfa6d6d8db46e --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotArtifacts @@ -0,0 +1 @@ +Subproject commit dfa6d6d8db46e60e94f7bfebb3f499305069614c diff --git a/firefox-ios/EcosiaTests/SnapshotTests/SnapshotBaseTests.swift b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotBaseTests.swift new file mode 100644 index 0000000000000..447dfa6e940a4 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotBaseTests.swift @@ -0,0 +1,38 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapshotTesting +import XCTest +import Common +@testable import Client + +extension WindowUUID { + static let snapshotTestDefaultUUID = WindowUUID(uuidString: "E9E9E9E9-E9E9-E9E9-E9E9-CD68A019860E")! +} + +class SnapshotBaseTests: XCTestCase { + + var profile: MockProfile! + var themeManager: ThemeManager! + private let allThemes: [ThemeConfiguration] = [ + ThemeConfiguration(theme: .light), + ThemeConfiguration(theme: .dark) + ] + + override func setUp() { + super.setUp() + let mockThemeManager = EcosiaMockThemeManager() + mockThemeManager.windowUUID = .snapshotTestDefaultUUID + DependencyHelperMock().bootstrapDependencies(themeManager: mockThemeManager) + profile = MockProfile() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) + themeManager = AppContainer.shared.resolve() + } + + override func tearDown() { + super.tearDown() + AppContainer.shared.reset() + profile = nil + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/SnapshotTestHelper.swift b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotTestHelper.swift new file mode 100644 index 0000000000000..1e6de2dea85e8 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotTestHelper.swift @@ -0,0 +1,264 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SnapshotTesting +import UIKit +import Common +import XCTest +@testable import Client + +struct ThemeConfiguration { + enum Theme: String, CaseIterable { + case light, dark + } + + let theme: Theme +} + +/// A utility class to facilitate snapshot testing across different UI themes and device configurations +/// for both UIViews and UIViewControllers. +final class SnapshotTestHelper { + + /// Performs a snapshot test on dynamically initialized content within a specified window environment, + /// applying theme settings and device configurations beforehand. + /// + /// - Parameters: + /// - initializer: A closure that returns newly initialized content (`UIView` or `UIViewController`). + /// - locales: An array of `Locale` specifying the locales for the snapshot. + /// - wait: The time interval to wait before taking the snapshot. + /// - precision: The precision of the snapshot comparison. + /// - file: The file in which failures should be reported. + /// - testName: The name of the test. + /// - line: The line number in the source code file where the failure occurred. + private static func performSnapshot( + initializingWith initializer: @escaping () -> T, + locales: [Locale], + wait: TimeInterval, + precision: CGFloat, + file: StaticString, + testName: String, + line: UInt + ) { + let themes: [(UIUserInterfaceStyle, ThemeConfiguration.Theme)] = [ + (.light, .light), + (.dark, .dark) + ] + + guard let testBundle = Bundle(identifier: "com.ecosia.ecosiaapp.EcosiaSnapshotTests"), + let envPath = testBundle.path(forResource: "environment", ofType: "json"), + let envData = try? Data(contentsOf: URL(fileURLWithPath: envPath)), + let envJson = try? JSONSerialization.jsonObject(with: envData, options: []), + let envDict = envJson as? [String: Any], + let devicesArray = envDict["DEVICES"] as? [[String: Any]], + let localesArray = envDict["LOCALES"] as? [String], + let simulatorDeviceName = envDict["SIMULATOR_DEVICE_NAME"] as? String else { + fatalError("Could not retrieve devices and locales from environment.json") + } + + let themeManager: ThemeManager = AppContainer.shared.resolve() + + // Map devices from environment.json + let devicesToTest: [DeviceType] = devicesArray.compactMap { deviceDict in + guard let deviceName = deviceDict["name"] as? String, + let orientation = deviceDict["orientation"] as? String else { + print("Invalid device entry: \(deviceDict)") + return nil + } + return DeviceType.from(deviceName: deviceName, orientation: orientation) + } + + // Map locales from environment.json + let locales = localesArray.map { Locale(identifier: $0) } + + for deviceType in devicesToTest { + let config = deviceType.config + let deviceName = deviceType.name + let window = UIWindow(frame: CGRect(origin: .zero, size: config.size!)) + let traits: UITraitCollection = .init(traitsFrom: [config.traits]) + + for locale in locales { + for (themeStyle, themeSuffix) in themes { + setLocale(locale) + changeThemeTo(themeStyle, suffix: themeSuffix, themeManager: themeManager) + updateContentInitializingWith(initializer, inWindow: window) + RunLoop.current.run(until: Date(timeIntervalSinceNow: wait)) + + let isCurrentDeviceMatchingSimulator = deviceName == simulatorDeviceName + + let snapshotting = Snapshotting.image( + precision: Float(precision), + size: isCurrentDeviceMatchingSimulator ? nil : config.size!, + traits: traits + ) + + let snapshotName = "\(String.cleanFunctionName(testName))_\(themeSuffix.rawValue)_\(deviceType.rawValue)_\(locale.identifier)" + + let fileUrl = URL(fileURLWithPath: "\(file)", isDirectory: false) + let fileName = fileUrl.deletingPathExtension().lastPathComponent + let domain = fileUrl.deletingLastPathComponent().lastPathComponent + let snapshotDirectoryUrl = fileUrl + .deletingLastPathComponent() + .deletingLastPathComponent() + .appendingPathComponent("SnapshotArtifacts", isDirectory: true) + .appendingPathComponent(domain, isDirectory: true) + .appendingPathComponent(fileName, isDirectory: true) + + var snapshotDirectory: String! + if #available(iOS 16.0, *) { + snapshotDirectory = snapshotDirectoryUrl.path() + } else { + snapshotDirectory = snapshotDirectoryUrl.path + } + + let failure = verifySnapshot( + of: window, + as: snapshotting, + snapshotDirectory: snapshotDirectory, + file: file, + testName: snapshotName + ) + + guard let message = failure else { return } + XCTFail(message, file: file, line: line) + } + } + } + } + + /// Sets the application's locale to the specified locale for testing. + /// + /// - Parameter locale: The locale to set for the application. + private static func setLocale(_ locale: Locale) { + overriddenLocaleIdentifier = locale.identifier + UserDefaults.standard.set([locale.identifier], forKey: "AppleLanguages") + UserDefaults.standard.set(locale.identifier, forKey: "AppleLocale") + UserDefaults.standard.synchronize() + swizzleMainBundle() // Swap the main bundle to use your custom bundle + } + + /// Swaps the main bundle to use a custom bundle for localization override during testing. + private static func swizzleMainBundle() { + object_setClass(Bundle.ecosia, LocalizationOverrideTestingBundle.self) + } + + /// Updates the window with newly initialized content and makes it visible. + /// This method initializes content using a provided initializer closure, sets up the content within the specified window, + /// and makes the window key and visible, ready for interaction or snapshotting. + /// + /// - Parameters: + /// - initializer: A closure that returns newly initialized content (`UIView` or `UIViewController`). + /// - window: The `UIWindow` in which the content will be displayed. + private static func updateContentInitializingWith(_ initializer: @escaping () -> T, inWindow window: UIWindow) { + let content = initializer() + setupContent(content, in: window) + window.makeKeyAndVisible() + } + + /// Changes the current theme to a specified UI style and updates the LegacyThemeManager accordingly. + /// This method applies a specified theme and updates the global theme settings through a theme manager. + /// + /// - Parameters: + /// - theme: The `UIUserInterfaceStyle` to set, e.g., `.light` or `.dark`. + /// - suffix: The `ThemeConfiguration.Theme` that specifies additional theme details, typically used for naming or logging. + /// - themeManager: The `ThemeManager` responsible for applying theme changes across the app. + private static func changeThemeTo(_ theme: UIUserInterfaceStyle, suffix: ThemeConfiguration.Theme, themeManager: ThemeManager) { + themeManager.setManualTheme(to: suffix == .light ? .light : .dark) + } + + /// Captures snapshots of a `UIViewController` across multiple device configurations. + /// + /// - Parameters: + /// - initializer: A closure that returns a newly initialized `UIViewController`. + /// - locales: An array of `Locale` specifying the locales for the snapshot. Defaults to the loaded from `snapshot_configuration.json` of each test if no other array is passed. + /// - wait: The time interval to delay the snapshot. + /// - precision: The precision of the snapshot comparison. Default to 0.99 to allow slightly different colors between CI and local runs. + /// - file: The file in which failures should be reported. + /// - testName: The name of the test. + /// - line: The line number to report failures. + static func assertSnapshot( + initializingWith initializer: @escaping () -> UIViewController, + locales: [Locale] = LocaleRetriever.getLocales(), + wait: TimeInterval = 0.5, + precision: CGFloat = 0.99, + file: StaticString = #file, + testName: String = #function, + line: UInt = #line + ) { + performSnapshot( + initializingWith: initializer, + locales: locales, + wait: wait, + precision: precision, + file: file, + testName: testName, + line: line + ) + } + + /// Captures snapshots of a UIView across multiple device configurations. + /// + /// - Parameters: + /// - initializer: A closure that returns a newly initialized UIView. + /// - locales: An array of `Locale` specifying the locales for the snapshot. Defaults to the loaded from `snapshot_configuration.json` of each test if no other array is passed. + /// - wait: The time interval to delay the snapshot. + /// - precision: The precision of the snapshot comparison. Default to 0.99 to allow slightly different colors between CI and local runs. + /// - file: The file in which failures should be reported. + /// - testName: The name of the test. + /// - line: The line number to report failures. + static func assertSnapshot( + initializingWith initializer: @escaping () -> UIView, + locales: [Locale] = LocaleRetriever.getLocales(), + wait: TimeInterval = 0.5, + precision: CGFloat = 0.99, + file: StaticString = #file, + testName: String = #function, + line: UInt = #line + ) { + performSnapshot( + initializingWith: initializer, + locales: locales, + wait: wait, + precision: precision, + file: file, + testName: testName, + line: line + ) + } + + /// Setup function for adding content to a UIWindow, used in snapshot testing. + /// + /// - Parameters: + /// - content: Thecontent to be added, either a UIView or UIViewController. + /// - window: The UIWindow to which the content will be added. + private static func setupContent(_ content: T, in window: UIWindow) { + if let view = content as? UIView { + window.addSubview(view) + window.bounds = view.bounds + } else if let viewController = content as? UIViewController { + window.rootViewController = viewController + window.bounds = viewController.view.bounds + viewController.loadViewIfNeeded() + viewController.view.layoutIfNeeded() + } + applyDataReloadAndLayoutIfNeeded(for: window) + } + + /// Recursively searches the view hierarchy starting from the provided view + /// and applies `reloadData()` and `layoutIfNeeded` on any `UICollectionView` or `UITableView`. + /// + /// - Parameter view: The root view from which to start the search. + private static func applyDataReloadAndLayoutIfNeeded(for view: UIView) { + if let collectionView = view as? UICollectionView { + collectionView.reloadData() + collectionView.layoutIfNeeded() + } else if let tableView = view as? UITableView { + tableView.reloadData() + tableView.layoutIfNeeded() + } + + for subview in view.subviews { + applyDataReloadAndLayoutIfNeeded(for: subview) + } + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/SnapshotTests.xctestplan b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotTests.xctestplan new file mode 100644 index 0000000000000..072da3afef409 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/SnapshotTests.xctestplan @@ -0,0 +1,32 @@ +{ + "configurations" : [ + { + "id" : "F463F962-D1EE-45DE-BF5E-83E24419C3FD", + "name" : "Test Scheme Action", + "options" : { + + } + } + ], + "defaultOptions" : { + + }, + "testTargets" : [ + { + "parallelizable" : true, + "skippedTests" : [ + "NTPTests", + "OnboardingTests", + "OnboardingTests\/testWelcomeScreen()", + "OnboardingTests\/testWelcomeStepsScreens()", + "SnapshotBaseTests" + ], + "target" : { + "containerPath" : "container:Client.xcodeproj", + "identifier" : "2C6C90812C614A16007D9B43", + "name" : "EcosiaSnapshotTests" + } + } + ], + "version" : 1 +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/String+Extension.swift b/firefox-ios/EcosiaTests/SnapshotTests/String+Extension.swift new file mode 100644 index 0000000000000..4defe4837e12c --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/String+Extension.swift @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +extension String { + static func cleanFunctionName(_ name: String) -> String { + return name.replaceFirstOccurrence(of: "()", with: "") + } + + func camelCaseToSnakeCase() -> String { + let pattern = "([a-z0-9])([A-Z])" + let regex = try? NSRegularExpression(pattern: pattern, options: []) + let range = NSRange(location: 0, length: self.utf16.count) + let result = regex?.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "$1_$2") + return result?.lowercased() ?? "" + } +} diff --git a/firefox-ios/EcosiaTests/SnapshotTests/environment.json b/firefox-ios/EcosiaTests/SnapshotTests/environment.json new file mode 100644 index 0000000000000..22cae7085cab6 --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/environment.json @@ -0,0 +1,24 @@ +{ + "DEVICES": [ + { + "name": "iPhone SE (3rd generation)", + "orientation": "portrait" + }, + { + "name": "iPhone 16 Pro", + "orientation": "portrait" + }, + { + "name": "iPad Pro 11-inch (M4)", + "orientation": "portrait" + } +], + "LOCALES": [ + "en", + "it", + "de", + "es", + "nl" +], + "SIMULATOR_DEVICE_NAME": "iPhone 16 Pro" + } diff --git a/firefox-ios/EcosiaTests/SnapshotTests/snapshot_configuration.json b/firefox-ios/EcosiaTests/SnapshotTests/snapshot_configuration.json new file mode 100644 index 0000000000000..a4d93902ca8ea --- /dev/null +++ b/firefox-ios/EcosiaTests/SnapshotTests/snapshot_configuration.json @@ -0,0 +1,41 @@ +{ + "locales": [ + "en", + "it", + "de", + "es", + "nl" + ], + "devices": [ + { + "name": "iPhone SE (3rd generation)", + "orientation": "portrait" + }, + { + "name": "iPhone 16 Pro", + "orientation": "portrait", + "os": "18.6", + "isDefaultTestDevice": true + }, + { + "name": "iPhone 16 Pro Max", + "orientation": "landscape" + }, + { + "name": "iPad Pro 11-inch (M4)", + "orientation": "portrait" + } + ], + "testBundles": [ + { + "name": "EcosiaSnapshotTests", + "testClasses": [ + { + "name": "NTPComponentTests", + "devices": ["iPhone 16 Pro"], + "locales": ["all"] + } + ] + } + ] +} \ No newline at end of file diff --git a/firefox-ios/EcosiaTests/UI/Account/EcosiaAccountAvatarPerformanceTests.swift b/firefox-ios/EcosiaTests/UI/Account/EcosiaAccountAvatarPerformanceTests.swift new file mode 100644 index 0000000000000..7e9190ae432ca --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/Account/EcosiaAccountAvatarPerformanceTests.swift @@ -0,0 +1,129 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import SwiftUI +@testable import Ecosia + +@available(iOS 16.0, *) +final class EcosiaAccountAvatarPerformanceTests: XCTestCase { + + // MARK: - Performance Tests + + @MainActor + func testViewModelInitializationPerformance() { + // Test that ViewModel initialization is fast (Apple recommends < 16ms for UI) + measure { + for _ in 0..<100 { + _ = EcosiaAccountAvatarViewModel( + avatarURL: URL(string: "https://example.com/avatar.jpg"), + progress: 0.75 + ) + } + } + } + + @MainActor + func testProgressUpdatePerformance() { + // Test that progress updates are fast enough for smooth animations + let viewModel = EcosiaAccountAvatarViewModel() + + measure { + for i in 0..<1000 { + let progress = Double(i % 100) / 100.0 + viewModel.updateProgress(progress) + } + } + } + + @MainActor + func testSparkleAnimationTriggerPerformance() { + // Test that sparkle animation triggering is responsive + let viewModel = EcosiaAccountAvatarViewModel() + + measure { + for _ in 0..<50 { + viewModel.triggerSparkles(duration: 0.1) + } + } + } + + // MARK: - Responsiveness Tests + + func testMainThreadResponsiveness() { + // Test that our notification handlers don't block the main thread + let startTime = CFAbsoluteTimeGetCurrent() + let iterations = 1000 + + for i in 0..! + + override func setUp() { + super.setUp() + cancellables = Set() + } + + override func tearDown() { + viewModel = nil + cancellables = nil + super.tearDown() + } + + // MARK: - Initialization Tests + + @MainActor + func testInitialization_withDefaults() { + // Given / When + viewModel = EcosiaAccountAvatarViewModel() + + // Then + XCTAssertNil(viewModel.avatarURL) + XCTAssertEqual(viewModel.progress, 0.25) + XCTAssertFalse(viewModel.showSparkles) + } + + @MainActor + func testInitialization_withCustomValues() { + // Given / When + viewModel = EcosiaAccountAvatarViewModel( + progress: 0.75, + seedCount: 10, + levelNumber: 3 + ) + + // Then + XCTAssertEqual(viewModel.progress, 0.75) + XCTAssertEqual(viewModel.seedCount, 10) + XCTAssertEqual(viewModel.currentLevelNumber, 3) + XCTAssertFalse(viewModel.showSparkles) + } + + @MainActor + func testInitialization_progressClamping() { + // Given / When + let viewModelUnder = EcosiaAccountAvatarViewModel(progress: -0.5) + let viewModelOver = EcosiaAccountAvatarViewModel(progress: 1.5) + + // Then + XCTAssertEqual(viewModelUnder.progress, 0.0) + XCTAssertEqual(viewModelOver.progress, 1.0) + } + + // MARK: - Manual Update Tests + + @MainActor + func testUpdateAvatarURL() { + // Given + viewModel = EcosiaAccountAvatarViewModel() + let newURL = URL(string: "https://example.com/new-avatar.jpg") + + // When + viewModel.updateAvatarURL(newURL) + + // Then + XCTAssertEqual(viewModel.avatarURL, newURL) + } + + @MainActor + func testUpdateProgress() { + // Given + viewModel = EcosiaAccountAvatarViewModel() + + // When + viewModel.updateProgress(0.8) + + // Then + XCTAssertEqual(viewModel.progress, 0.8) + } + + @MainActor + func testUpdateProgress_clamping() { + // Given + viewModel = EcosiaAccountAvatarViewModel() + + // When + viewModel.updateProgress(-0.5) + XCTAssertEqual(viewModel.progress, 0.0) + + viewModel.updateProgress(1.5) + XCTAssertEqual(viewModel.progress, 1.0) + } + + @MainActor + func testTriggerSparkles() async { + // Given + viewModel = EcosiaAccountAvatarViewModel() + XCTAssertFalse(viewModel.showSparkles) + + // When + viewModel.triggerSparkles(duration: 0.1) + + // Then + XCTAssertTrue(viewModel.showSparkles) + + // Wait for sparkles to auto-hide + try? await Task.sleep(nanoseconds: 200_000_000) // 0.2 seconds + XCTAssertFalse(viewModel.showSparkles) + } + + // MARK: - Notification Tests + + @MainActor + func testProgressUpdateNotification() { + // Given / When + viewModel = EcosiaAccountAvatarViewModel() + let expectation = XCTestExpectation(description: "Progress updated to 0.6") + + viewModel.$progress + .dropFirst() + .sink { progress in + if progress == 0.6 { + expectation.fulfill() + } + } + .store(in: &cancellables) + + EcosiaAccountNotificationCenter.postProgressUpdated(progress: 0.6) + + // Then + wait(for: [expectation], timeout: 1.0) + } + + @MainActor + func testLevelUpNotification() { + // Given / When + viewModel = EcosiaAccountAvatarViewModel() + let sparklesExpectation = XCTestExpectation(description: "Sparkles triggered") + let levelExpectation = XCTestExpectation(description: "Level updated") + + viewModel.$showSparkles + .dropFirst() + .sink { showSparkles in + if showSparkles { + sparklesExpectation.fulfill() + } + } + .store(in: &cancellables) + + viewModel.$currentLevelNumber + .dropFirst() + .sink { level in + if level == 3 { + levelExpectation.fulfill() + } + } + .store(in: &cancellables) + + EcosiaAccountNotificationCenter.postLevelUp(newLevel: 3, newProgress: 0.9) + + // Then + wait(for: [sparklesExpectation, levelExpectation], timeout: 1.0) + } +} + +// MARK: - Notification Center Tests +final class EcosiaAccountNotificationCenterTests: XCTestCase { + + func testPostProgressUpdated() { + // Given + let expectation = XCTestExpectation(description: "Progress notification received") + let observer = NotificationCenter.default.addObserver( + forName: .EcosiaAccountProgressUpdated, + object: nil, + queue: .main + ) { notification in + guard let userInfo = notification.userInfo, + let progress = userInfo[EcosiaAccountNotificationKeys.progress] as? Double else { + XCTFail("Expected progress in userInfo") + return + } + + XCTAssertEqual(progress, 0.7) + expectation.fulfill() + } + + // When + EcosiaAccountNotificationCenter.postProgressUpdated(progress: 0.7) + + // Then + wait(for: [expectation], timeout: 1.0) + NotificationCenter.default.removeObserver(observer) + } + + func testPostLevelUp() { + // Given + let expectation = XCTestExpectation(description: "Level up notification received") + let observer = NotificationCenter.default.addObserver( + forName: .EcosiaAccountLevelUp, + object: nil, + queue: .main + ) { notification in + guard let userInfo = notification.userInfo, + let newLevel = userInfo[EcosiaAccountNotificationKeys.newLevel] as? Int, + let newProgress = userInfo[EcosiaAccountNotificationKeys.newProgress] as? Double else { + XCTFail("Expected level and progress in userInfo") + return + } + + XCTAssertEqual(newLevel, 5) + XCTAssertEqual(newProgress, 0.85) + expectation.fulfill() + } + + // When + EcosiaAccountNotificationCenter.postLevelUp(newLevel: 5, newProgress: 0.85) + + // Then + wait(for: [expectation], timeout: 1.0) + NotificationCenter.default.removeObserver(observer) + } +} diff --git a/firefox-ios/EcosiaTests/UI/Account/EcosiaWebViewModalTests.swift b/firefox-ios/EcosiaTests/UI/Account/EcosiaWebViewModalTests.swift new file mode 100644 index 0000000000000..93557b7ae3528 --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/Account/EcosiaWebViewModalTests.swift @@ -0,0 +1,395 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import WebKit +@testable import Ecosia + +@available(iOS 16.0, *) +final class EcosiaWebViewModalTests: XCTestCase { + + private var coordinator: MockCoordinator! + private var mockWebView: MockWKWebView! + private var mockParent: MockWebViewRepresentable! + + override func setUp() { + super.setUp() + mockWebView = MockWKWebView() + mockParent = MockWebViewRepresentable() + coordinator = MockCoordinator(parent: mockParent) + } + + override func tearDown() { + coordinator = nil + mockWebView = nil + mockParent = nil + super.tearDown() + } + + // MARK: - WKUIDelegate Tests + + func testCreateWebViewWith_targetBlankLink_loadsInModal() { + // Given + let blankURL = URL(string: "https://example.com/blank-page")! + let currentURL = URL(string: "https://example.com/origin")! + mockWebView.mockURL = currentURL + + let navigationAction = MockNavigationAction( + request: URLRequest(url: blankURL), + targetFrame: nil + ) + + // When + let result = coordinator.webView( + mockWebView, + createWebViewWith: WKWebViewConfiguration(), + for: navigationAction, + windowFeatures: WKWindowFeatures() + ) + + // Then + XCTAssertNil(result, "Should return nil to prevent new window creation") + XCTAssertEqual(mockWebView.loadedRequests.count, 1) + XCTAssertEqual(mockWebView.loadedRequests.first?.url, blankURL) + XCTAssertTrue(coordinator.blankTargetURLs.contains(currentURL.absoluteString)) + } + + func testCreateWebViewWith_noTargetFrame_recordsOriginURL() { + // Given + let originURL = URL(string: "https://example.com/page1")! + mockWebView.mockURL = originURL + + let blankURL = URL(string: "https://example.com/blank")! + let navigationAction = MockNavigationAction( + request: URLRequest(url: blankURL), + targetFrame: nil + ) + + // When + _ = coordinator.webView( + mockWebView, + createWebViewWith: WKWebViewConfiguration(), + for: navigationAction, + windowFeatures: WKWindowFeatures() + ) + + // Then + XCTAssertTrue(coordinator.blankTargetURLs.contains(originURL.absoluteString)) + } + + func testCreateWebViewWith_noURL_returnsNil() { + // Given + let navigationAction = MockNavigationAction( + request: URLRequest(url: URL(string: "about:blank")!), + targetFrame: nil + ) + navigationAction.mockRequestURL = nil + + // When + let result = coordinator.webView( + mockWebView, + createWebViewWith: WKWebViewConfiguration(), + for: navigationAction, + windowFeatures: WKWindowFeatures() + ) + + // Then + XCTAssertNil(result) + XCTAssertEqual(mockWebView.loadedRequests.count, 0) + } + + // MARK: - Back Navigation Prevention Tests + + func testDecidePolicyFor_backToBlankTargetOrigin_preventsNavigation() { + // Given + let originURL = URL(string: "https://example.com/origin")! + coordinator.blankTargetURLs.insert(originURL.absoluteString) + + let navigationAction = MockNavigationAction( + request: URLRequest(url: originURL), + navigationType: .backForward + ) + + let expectation = XCTestExpectation(description: "Decision handler called") + var capturedPolicy: WKNavigationActionPolicy? + + // When + coordinator.webView(mockWebView, decidePolicyFor: navigationAction) { policy in + capturedPolicy = policy + expectation.fulfill() + } + + // Then + wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(capturedPolicy, .cancel) + XCTAssertEqual(mockWebView.goBackCallCount, 1) + } + + func testDecidePolicyFor_backToBlankTargetOrigin_cannotGoBack_reloadsInitialURL() { + // Given + let originURL = URL(string: "https://example.com/origin")! + let initialURL = URL(string: "https://example.com/initial")! + mockParent.mockURL = initialURL + mockWebView.mockCanGoBack = false + coordinator.blankTargetURLs.insert(originURL.absoluteString) + + let navigationAction = MockNavigationAction( + request: URLRequest(url: originURL), + navigationType: .backForward + ) + + let expectation = XCTestExpectation(description: "Decision handler called") + + // When + coordinator.webView(mockWebView, decidePolicyFor: navigationAction) { _ in + expectation.fulfill() + } + + // Then + wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(mockWebView.loadedRequests.count, 1) + XCTAssertEqual(mockWebView.loadedRequests.first?.url, initialURL) + XCTAssertTrue(coordinator.blankTargetURLs.isEmpty, "Should clear tracked URLs") + } + + func testDecidePolicyFor_normalBackNavigation_allowsNavigation() { + // Given + let url = URL(string: "https://example.com/page")! + let navigationAction = MockNavigationAction( + request: URLRequest(url: url), + navigationType: .backForward + ) + + let expectation = XCTestExpectation(description: "Decision handler called") + var capturedPolicy: WKNavigationActionPolicy? + + // When + coordinator.webView(mockWebView, decidePolicyFor: navigationAction) { policy in + capturedPolicy = policy + expectation.fulfill() + } + + // Then + wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(capturedPolicy, .allow) + XCTAssertEqual(mockWebView.goBackCallCount, 0) + } + + func testDecidePolicyFor_nonBackForwardNavigation_allowsNavigation() { + // Given + let originURL = URL(string: "https://example.com/origin")! + coordinator.blankTargetURLs.insert(originURL.absoluteString) + + let navigationAction = MockNavigationAction( + request: URLRequest(url: originURL), + navigationType: .linkActivated + ) + + let expectation = XCTestExpectation(description: "Decision handler called") + var capturedPolicy: WKNavigationActionPolicy? + + // When + coordinator.webView(mockWebView, decidePolicyFor: navigationAction) { policy in + capturedPolicy = policy + expectation.fulfill() + } + + // Then + wait(for: [expectation], timeout: 1.0) + XCTAssertEqual(capturedPolicy, .allow) + } + + // MARK: - Multiple Blank Target URLs Tests + + func testMultipleBlankTargetURLs_tracksAll() { + // Given + let url1 = URL(string: "https://example.com/page1")! + let url2 = URL(string: "https://example.com/page2")! + let url3 = URL(string: "https://example.com/page3")! + + mockWebView.mockURL = url1 + let action1 = MockNavigationAction( + request: URLRequest(url: URL(string: "https://example.com/blank1")!), + targetFrame: nil + ) + _ = coordinator.webView(mockWebView, createWebViewWith: WKWebViewConfiguration(), for: action1, windowFeatures: WKWindowFeatures()) + + mockWebView.mockURL = url2 + let action2 = MockNavigationAction( + request: URLRequest(url: URL(string: "https://example.com/blank2")!), + targetFrame: nil + ) + _ = coordinator.webView(mockWebView, createWebViewWith: WKWebViewConfiguration(), for: action2, windowFeatures: WKWindowFeatures()) + + mockWebView.mockURL = url3 + let action3 = MockNavigationAction( + request: URLRequest(url: URL(string: "https://example.com/blank3")!), + targetFrame: nil + ) + _ = coordinator.webView(mockWebView, createWebViewWith: WKWebViewConfiguration(), for: action3, windowFeatures: WKWindowFeatures()) + + // Then + XCTAssertEqual(coordinator.blankTargetURLs.count, 3) + XCTAssertTrue(coordinator.blankTargetURLs.contains(url1.absoluteString)) + XCTAssertTrue(coordinator.blankTargetURLs.contains(url2.absoluteString)) + XCTAssertTrue(coordinator.blankTargetURLs.contains(url3.absoluteString)) + } + + func testBlankTargetURLs_clearedWhenReloadingInitial() { + // Given + let originURL = URL(string: "https://example.com/origin")! + coordinator.blankTargetURLs.insert(originURL.absoluteString) + coordinator.blankTargetURLs.insert("https://example.com/other") + mockWebView.mockCanGoBack = false + mockParent.mockURL = URL(string: "https://example.com/initial")! + + let navigationAction = MockNavigationAction( + request: URLRequest(url: originURL), + navigationType: .backForward + ) + + let expectation = XCTestExpectation(description: "Decision handler called") + + // When + coordinator.webView(mockWebView, decidePolicyFor: navigationAction) { _ in + expectation.fulfill() + } + + // Then + wait(for: [expectation], timeout: 1.0) + XCTAssertTrue(coordinator.blankTargetURLs.isEmpty) + } +} + +// MARK: - Mock Classes + +@available(iOS 16.0, *) +private class MockCoordinator: NSObject, WKNavigationDelegate, WKUIDelegate { + let parent: MockWebViewRepresentable + var blankTargetURLs: Set = [] + + init(parent: MockWebViewRepresentable) { + self.parent = parent + } + + func webView( + _ webView: WKWebView, + createWebViewWith configuration: WKWebViewConfiguration, + for navigationAction: WKNavigationAction, + windowFeatures: WKWindowFeatures + ) -> WKWebView? { + guard navigationAction.targetFrame == nil, + let url = navigationAction.request.url else { + return nil + } + + if let currentURL = webView.url { + blankTargetURLs.insert(currentURL.absoluteString) + } + + webView.load(URLRequest(url: url)) + return nil + } + + func webView( + _ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void + ) { + guard let url = navigationAction.request.url else { + decisionHandler(.allow) + return + } + + if navigationAction.navigationType == .backForward, + blankTargetURLs.contains(url.absoluteString) { + decisionHandler(.cancel) + + if webView.canGoBack { + webView.goBack() + } else { + webView.load(URLRequest(url: parent.mockURL)) + blankTargetURLs.removeAll() + } + return + } + + decisionHandler(.allow) + } +} + +@available(iOS 16.0, *) +private class MockWebViewRepresentable { + var mockURL = URL(string: "https://example.com")! +} + +@available(iOS 16.0, *) +private class MockWKWebView: WKWebView { + var mockURL: URL? + var mockCanGoBack = true + var loadedRequests: [URLRequest] = [] + var goBackCallCount = 0 + + override var url: URL? { + return mockURL + } + + override var canGoBack: Bool { + return mockCanGoBack + } + + override func load(_ request: URLRequest) -> WKNavigation? { + loadedRequests.append(request) + return nil + } + + override func goBack() -> WKNavigation? { + goBackCallCount += 1 + return nil + } +} + +@available(iOS 16.0, *) +private class MockNavigationAction: WKNavigationAction { + private let mockRequest: URLRequest + private let mockTargetFrame: WKFrameInfo? + private let mockNavigationType: WKNavigationType + private var shouldOverrideURL = false + var mockRequestURL: URL? { + didSet { + shouldOverrideURL = true + } + } + + init(request: URLRequest, targetFrame: WKFrameInfo? = nil, navigationType: WKNavigationType = .other) { + self.mockRequest = request + self.mockTargetFrame = targetFrame + self.mockNavigationType = navigationType + self.mockRequestURL = request.url + } + + override var request: URLRequest { + guard shouldOverrideURL else { + return mockRequest + } + var modifiedRequest = mockRequest + modifiedRequest.url = mockRequestURL + return modifiedRequest + } + + override var targetFrame: WKFrameInfo? { + return mockTargetFrame + } + + override var navigationType: WKNavigationType { + return mockNavigationType + } +} + +@available(iOS 16.0, *) +private class MockFrameInfo: WKFrameInfo { + override var isMainFrame: Bool { + return true + } +} diff --git a/firefox-ios/EcosiaTests/UI/Account/LevelSystem/GrowthPointsLevelSystemTests.swift b/firefox-ios/EcosiaTests/UI/Account/LevelSystem/GrowthPointsLevelSystemTests.swift new file mode 100644 index 0000000000000..f7f2daf7a875d --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/Account/LevelSystem/GrowthPointsLevelSystemTests.swift @@ -0,0 +1,308 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia + +final class GrowthPointsLevelSystemTests: XCTestCase { + + // MARK: - Helper Methods + + private func createMockResponse( + currentLevel: Int = 2, + previousLevel: Int = 1, + growthPointsEarned: Int = 125, + growthPointsRequired: Int = 175 + ) -> AccountVisitResponse { + let timestamp = "2024-12-07T10:50:26Z" + return AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 10, + totalAmount: 10, + previousTotalAmount: 8, + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 200, + totalAmount: 200, + previousTotalAmount: 175, + level: AccountVisitResponse.Level( + number: currentLevel, + totalGrowthPointsRequired: 75, + seedsRewardedForLevelUp: 2, + growthPointsToUnlockNextLevel: growthPointsRequired, + growthPointsEarnedTowardsNextLevel: growthPointsEarned + ), + previousLevel: AccountVisitResponse.Level( + number: previousLevel, + totalGrowthPointsRequired: 0, + seedsRewardedForLevelUp: 1, + growthPointsToUnlockNextLevel: 75, + growthPointsEarnedTowardsNextLevel: 75 + ), + isModified: true, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + } + + // MARK: - Level Detection Tests + + func testCurrentLevel() { + // Given + let response = createMockResponse(currentLevel: 5) + + // When + let level = GrowthPointsLevelSystem.currentLevel(from: response) + + // Then + XCTAssertEqual(level, 5) + } + + func testPreviousLevel() { + // Given + let response = createMockResponse(previousLevel: 3) + + // When + let level = GrowthPointsLevelSystem.previousLevel(from: response) + + // Then + XCTAssertEqual(level, 3) + } + + func testCheckLevelUp_WhenLeveledUp() { + // Given + let response = createMockResponse(currentLevel: 3, previousLevel: 2) + + // When + let didLevelUp = GrowthPointsLevelSystem.didLevelUp(from: response) + + // Then + XCTAssertTrue(didLevelUp) + } + + func testCheckLevelUp_WhenNoLevelUp() { + // Given + let response = createMockResponse(currentLevel: 2, previousLevel: 2) + + // When + let didLevelUp = GrowthPointsLevelSystem.didLevelUp(from: response) + + // Then + XCTAssertFalse(didLevelUp) + } + + // MARK: - Progress Calculation Tests + + func testProgressToNextLevel_MidProgress() { + // Given + // 125 earned out of 175 required = ~0.714 + let response = createMockResponse(growthPointsEarned: 125, growthPointsRequired: 175) + + // When + let progress = GrowthPointsLevelSystem.progressToNextLevel(from: response) + + // Then + XCTAssertEqual(progress, 125.0 / 175.0, accuracy: 0.01) + } + + func testProgressToNextLevel_ZeroProgress() { + // Given + let response = createMockResponse(growthPointsEarned: 0, growthPointsRequired: 175) + + // When + let progress = GrowthPointsLevelSystem.progressToNextLevel(from: response) + + // Then + XCTAssertEqual(progress, 0.0, accuracy: 0.01) + } + + func testProgressToNextLevel_FullProgress() { + // Given + let response = createMockResponse(growthPointsEarned: 175, growthPointsRequired: 175) + + // When + let progress = GrowthPointsLevelSystem.progressToNextLevel(from: response) + + // Then + XCTAssertEqual(progress, 1.0, accuracy: 0.01) + } + + // MARK: - Growth Points Tests + + func testGrowthPointsEarned() { + // Given + let response = createMockResponse(growthPointsEarned: 125) + + // When + let earned = GrowthPointsLevelSystem.growthPointsEarned(from: response) + + // Then + XCTAssertEqual(earned, 125) + } + + func testGrowthPointsRequired() { + // Given + let response = createMockResponse(growthPointsRequired: 175) + + // When + let required = GrowthPointsLevelSystem.growthPointsRequired(from: response) + + // Then + XCTAssertEqual(required, 175) + } + + func testSeedsRewardedForLevelUp() { + // Given + let response = createMockResponse() + + // When + let seedsRewarded = GrowthPointsLevelSystem.seedsRewardedForLevelUp(from: response) + + // Then + XCTAssertEqual(seedsRewarded, 2) + } + + func testTotalGrowthPointsRequired() { + // Given + let response = createMockResponse() + + // When + let totalRequired = GrowthPointsLevelSystem.totalGrowthPointsRequired(from: response) + + // Then + XCTAssertEqual(totalRequired, 75) + } + + // MARK: - Edge Cases + + func testProgressCalculation_WithZeroRequired() { + // Given + let timestamp = "2024-12-07T10:50:26Z" + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 10, + totalAmount: 10, + previousTotalAmount: 10, + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 100, + level: AccountVisitResponse.Level( + number: 1, + totalGrowthPointsRequired: 0, + seedsRewardedForLevelUp: 0, + growthPointsToUnlockNextLevel: 0, + growthPointsEarnedTowardsNextLevel: 0 + ), + previousLevel: AccountVisitResponse.Level( + number: 1, + totalGrowthPointsRequired: 0, + seedsRewardedForLevelUp: 0, + growthPointsToUnlockNextLevel: 0, + growthPointsEarnedTowardsNextLevel: 0 + ), + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + // When + let progress = GrowthPointsLevelSystem.progressToNextLevel(from: response) + + // Then + XCTAssertEqual(progress, 0.0, accuracy: 0.01) + } + + // MARK: - Level Thresholds Tests + + func testLevelThresholds_MatchBackendSpecification() { + // swiftlint:disable large_tuple + // Given + let expectedLevels: [(number: Int, totalGrowthPointsRequired: Int, seedsRewardedForLevelUp: Int)] = [ + (1, 0, 0), + (2, 75, 2), + (3, 250, 7), + (4, 500, 12), + (5, 750, 18), + (6, 1250, 25), + (7, 2000, 33), + (8, 2750, 42), + (9, 3750, 52), + (10, 5000, 63), + (11, 6500, 75), + (12, 8000, 88), + (13, 10000, 101), + (14, 12000, 116), + (15, 14250, 131), + (16, 17000, 147), + (17, 19750, 164), + (18, 23000, 182), + (19, 26250, 200), + (20, 30000, 220) + ] + // swiftlint:enable large_tuple + let timestamp = "2024-12-07T10:50:26Z" + + // When/Then + for expected in expectedLevels { + let response = AccountVisitResponse( + seeds: AccountVisitResponse.Seeds( + balanceAmount: 100, + totalAmount: 100, + previousTotalAmount: 100, + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ), + growthPoints: AccountVisitResponse.GrowthPoints( + balanceAmount: expected.totalGrowthPointsRequired, + totalAmount: expected.totalGrowthPointsRequired, + previousTotalAmount: expected.totalGrowthPointsRequired, + level: AccountVisitResponse.Level( + number: expected.number, + totalGrowthPointsRequired: expected.totalGrowthPointsRequired, + seedsRewardedForLevelUp: expected.seedsRewardedForLevelUp, + growthPointsToUnlockNextLevel: 100, + growthPointsEarnedTowardsNextLevel: 0 + ), + previousLevel: AccountVisitResponse.Level( + number: expected.number, + totalGrowthPointsRequired: expected.totalGrowthPointsRequired, + seedsRewardedForLevelUp: expected.seedsRewardedForLevelUp, + growthPointsToUnlockNextLevel: 100, + growthPointsEarnedTowardsNextLevel: 0 + ), + isModified: false, + lastVisitAt: timestamp, + updatedAt: timestamp + ) + ) + + XCTAssertEqual( + GrowthPointsLevelSystem.currentLevel(from: response), + expected.number, + "Level number mismatch for level \(expected.number)" + ) + XCTAssertEqual( + GrowthPointsLevelSystem.totalGrowthPointsRequired(from: response), + expected.totalGrowthPointsRequired, + "Total growth points mismatch for level \(expected.number)" + ) + XCTAssertEqual( + GrowthPointsLevelSystem.seedsRewardedForLevelUp(from: response), + expected.seedsRewardedForLevelUp, + "Seeds reward mismatch for level \(expected.number)" + ) + } + } +} diff --git a/firefox-ios/EcosiaTests/UI/FeedbackView/FeedbackThemeTests.swift b/firefox-ios/EcosiaTests/UI/FeedbackView/FeedbackThemeTests.swift new file mode 100644 index 0000000000000..347e0d13c1f3a --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/FeedbackView/FeedbackThemeTests.swift @@ -0,0 +1,28 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import SwiftUI +import Common +@testable import Client +@testable import Ecosia + +final class FeedbackThemeTests: XCTestCase { + + // MARK: - Integration Tests + + func testIntegrationWithFeedbackView() { + // Given + let windowUUID: WindowUUID = .XCTestDefaultUUID + let feedbackView = FeedbackView(windowUUID: windowUUID) + + // When/Then + let viewMirror = Mirror(reflecting: feedbackView) + XCTAssertNotNil(viewMirror.descendant("_theme")) + + // Also verify that the body contains our ThemeModifier (the Ecosia one we made) + let bodyMirror = Mirror(reflecting: feedbackView.body) + XCTAssertTrue(bodyMirror.description.contains("ThemeModifier")) + } +} diff --git a/firefox-ios/EcosiaTests/UI/NTPHeader/EcosiaAISearchButtonThemeTests.swift b/firefox-ios/EcosiaTests/UI/NTPHeader/EcosiaAISearchButtonThemeTests.swift new file mode 100644 index 0000000000000..54869ffea9dee --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/NTPHeader/EcosiaAISearchButtonThemeTests.swift @@ -0,0 +1,29 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import SwiftUI +import Common +@testable import Client +@testable import Ecosia + +final class EcosiaAISearchButtonThemeTests: XCTestCase { + + // MARK: - Integration Tests + + @available(iOS 16.0, *) + func testIntegrationWithButton() { + // Given + let windowUUID: WindowUUID = .XCTestDefaultUUID + let button = EcosiaAISearchButton(windowUUID: windowUUID, onTap: {}) + + // When/Then + let buttonMirror = Mirror(reflecting: button) + XCTAssertNotNil(buttonMirror.descendant("_theme")) + + // Also verify that the body contains our ThemeModifier (the Ecosia one we made) + let bodyMirror = Mirror(reflecting: button.body) + XCTAssertTrue(bodyMirror.description.contains("ThemeModifier")) + } +} diff --git a/firefox-ios/EcosiaTests/UI/ProductTour/ProductTourManagerTests.swift b/firefox-ios/EcosiaTests/UI/ProductTour/ProductTourManagerTests.swift new file mode 100644 index 0000000000000..93e432f75587a --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/ProductTour/ProductTourManagerTests.swift @@ -0,0 +1,784 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Ecosia +import Common + +final class ProductTourManagerTests: XCTestCase { + var userDefaults: UserDefaults! + var sut: ProductTourManager! + var suiteName: String! + var authManager: EcosiaBrowserWindowAuthManager! + var testWindowUUID: WindowUUID! + + override func setUp() { + super.setUp() + suiteName = "ProductTourManagerTests-\(UUID().uuidString)" + userDefaults = UserDefaults(suiteName: suiteName)! + authManager = EcosiaBrowserWindowAuthManager.shared + testWindowUUID = WindowUUID.XCTestDefaultUUID + EcosiaAuthWindowRegistry.shared.registerWindow(testWindowUUID) + sut = ProductTourManager(userDefaults: userDefaults, authManager: authManager, isExperimentEnabled: { true }) + } + + override func tearDown() { + if let suiteName = suiteName { + userDefaults.removePersistentDomain(forName: suiteName) + } + authManager.clearAllStates() + EcosiaAuthWindowRegistry.shared.clearAllWindows() + userDefaults = nil + sut = nil + suiteName = nil + authManager = nil + testWindowUUID = nil + } + + // MARK: - Helpers + + /// Simulates a login by dispatching a `userLoggedIn` action through the auth manager, + /// which triggers the `ProductTourManager`'s notification handler. + private func simulateLogin(accountOrigin: AccountOrigin?) { + let action = AuthStateAction( + type: .userLoggedIn, + windowUUID: testWindowUUID, + isLoggedIn: true, + accountOrigin: accountOrigin + ) + authManager.dispatch(action: action, for: testWindowUUID) + } + + // MARK: - Initialization Tests + + func testInitialState_WhenExperimentDisabled_IsCompleted() { + // Given/When: Experiment is disabled (default behavior) + let manager = ProductTourManager(userDefaults: userDefaults, authManager: authManager) + + // Then: All milestones should be marked complete + XCTAssertEqual(manager.completedMilestones, .all) + XCTAssertFalse(manager.isInProductTour) + XCTAssertFalse(manager.shouldShowProductTourHomepage) + } + + func testInitialState_WhenNoSavedMilestones_DefaultsToCompleted() { + // Given: No saved milestones in UserDefaults + userDefaults.removeObject(forKey: "ProductTourMilestones") + + // When: Manager initializes + let manager = ProductTourManager(userDefaults: userDefaults, authManager: authManager) + + // Then: Should default to completed (experiment disabled) + XCTAssertEqual(manager.completedMilestones, .all) + } + + // MARK: - shouldShowProductTourHomepage Tests + + func testShouldShowProductTourHomepage_WhenFreshTour_ReturnsTrue() { + // Given: Fresh tour + sut.resetTour() + + // Then: Should show product tour homepage + XCTAssertTrue(sut.shouldShowProductTourHomepage) + } + + func testShouldShowProductTourHomepage_AfterFirstSearch_ReturnsFalse() { + // Given: Tour where first search is completed + sut.resetTour() + sut.completeFirstSearchIfNeeded() + + // Then: Should not show product tour homepage + XCTAssertFalse(sut.shouldShowProductTourHomepage) + } + + func testShouldShowProductTourHomepage_AfterExternalWebsiteVisit_StillReturnsTrue() { + // Given: Tour where external website was visited but search not yet done + sut.resetTour() + sut.completeExternalWebsiteVisitIfNeeded() + + // Then: Should still show product tour homepage (search not done yet) + XCTAssertTrue(sut.shouldShowProductTourHomepage) + } + + func testShouldShowProductTourHomepage_WhenTourCompleted_ReturnsFalse() { + // Given: Completed tour + sut.resetTour() + sut.completeTour() + + // Then: Should not show product tour homepage + XCTAssertFalse(sut.shouldShowProductTourHomepage) + } + + // MARK: - First Search Tests + + func testCompleteFirstSearchIfNeeded_MarksFirstSearchMilestone() { + // Given: Fresh tour + sut.resetTour() + XCTAssertFalse(sut.completedMilestones.contains(.firstSearchDone)) + + // When: Completing first search + sut.completeFirstSearchIfNeeded() + + // Then: First search milestone should be set + XCTAssertTrue(sut.completedMilestones.contains(.firstSearchDone)) + } + + func testCompleteFirstSearchIfNeeded_WhenAlreadyDone_DoesNothing() { + // Given: First search already completed + sut.resetTour() + sut.completeFirstSearchIfNeeded() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Calling again + sut.completeFirstSearchIfNeeded() + + // Then: No new event should be emitted + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + func testCompleteFirstSearchIfNeeded_WhenTourCompleted_DoesNothing() { + // Given: Tour is completed + sut.completeTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Attempting to complete first search + sut.completeFirstSearchIfNeeded() + + // Then: No event should be emitted + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + // MARK: - External Website Visit Tests + + func testCompleteExternalWebsiteVisitIfNeeded_MarksVisitMilestone() { + // Given: Fresh tour + sut.resetTour() + XCTAssertFalse(sut.completedMilestones.contains(.externalWebsiteVisitDone)) + + // When: Visiting external website + sut.completeExternalWebsiteVisitIfNeeded() + + // Then: External website visit milestone should be set + XCTAssertTrue(sut.completedMilestones.contains(.externalWebsiteVisitDone)) + } + + func testCompleteExternalWebsiteVisitIfNeeded_WhenAlreadyDone_DoesNothing() { + // Given: External website already visited + sut.resetTour() + sut.completeExternalWebsiteVisitIfNeeded() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Calling again + sut.completeExternalWebsiteVisitIfNeeded() + + // Then: No new event should be emitted + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + func testCompleteExternalWebsiteVisitIfNeeded_WhenTourCompleted_DoesNothing() { + // Given: Tour is completed + sut.completeTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Attempting to complete external website visit + sut.completeExternalWebsiteVisitIfNeeded() + + // Then: No event should be emitted + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + // MARK: - Spotlight Completion Tests + + func testCompleteSearchSpotlight_MarksMilestone() { + // Given: Fresh tour + sut.resetTour() + + // When: Completing search spotlight + sut.completeSearchSpotlight() + + // Then: Search spotlight milestone should be marked + XCTAssertTrue(sut.completedMilestones.contains(.searchSpotlightDone)) + } + + func testCompleteExternalWebsiteSpotlight_MarksMilestone() { + // Given: Fresh tour + sut.resetTour() + + // When: Completing external website spotlight + sut.completeExternalWebsiteSpotlight() + + // Then: External website spotlight milestone should be marked + XCTAssertTrue(sut.completedMilestones.contains(.externalWebsiteSpotlightDone)) + } + + // MARK: - shouldShow Spotlight Tests + + func testShouldShowSearchSpotlight_WhenSearchDoneButSpotlightNot_ReturnsTrue() { + // Given: First search completed, spotlight not shown + sut.resetTour() + sut.completeFirstSearchIfNeeded() + + // Then + XCTAssertTrue(sut.shouldShowSearchSpotlight) + } + + func testShouldShowSearchSpotlight_WhenSpotlightDone_ReturnsFalse() { + // Given: Search spotlight already completed + sut.resetTour() + sut.completeFirstSearchIfNeeded() + sut.completeSearchSpotlight() + + // Then + XCTAssertFalse(sut.shouldShowSearchSpotlight) + } + + func testShouldShowSearchSpotlight_WhenSearchNotDone_ReturnsFalse() { + // Given: Fresh tour, no search yet + sut.resetTour() + + // Then + XCTAssertFalse(sut.shouldShowSearchSpotlight) + } + + func testShouldShowExternalWebsiteSpotlight_WhenVisitDoneButSpotlightNot_ReturnsTrue() { + // Given: External website visited, spotlight not shown + sut.resetTour() + sut.completeExternalWebsiteVisitIfNeeded() + + // Then + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + } + + func testShouldShowExternalWebsiteSpotlight_WhenSpotlightDone_ReturnsFalse() { + // Given: External website spotlight already completed + sut.resetTour() + sut.completeExternalWebsiteVisitIfNeeded() + sut.completeExternalWebsiteSpotlight() + + // Then + XCTAssertFalse(sut.shouldShowExternalWebsiteSpotlight) + } + + // MARK: - Tour Completion Tests + + func testTourCompletes_WhenAllMilestonesCompleted_SearchTrackFirst() { + // Given: Fresh tour + sut.resetTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Completing search track first, then external website track + sut.completeFirstSearchIfNeeded() + sut.completeSearchSpotlight() + XCTAssertTrue(sut.isInProductTour, "Tour should still be in progress") + + sut.completeExternalWebsiteVisitIfNeeded() + sut.completeExternalWebsiteSpotlight() + + // Then: Tour should be completed + XCTAssertFalse(sut.isInProductTour) + XCTAssertEqual(observer.receivedEvents.last, .tourCompleted) + } + + func testTourCompletes_WhenAllMilestonesCompleted_ExternalTrackFirst() { + // Given: Fresh tour + sut.resetTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Completing external website track first, then search track + sut.completeExternalWebsiteVisitIfNeeded() + sut.completeExternalWebsiteSpotlight() + XCTAssertTrue(sut.isInProductTour, "Tour should still be in progress") + + sut.completeFirstSearchIfNeeded() + sut.completeSearchSpotlight() + + // Then: Tour should be completed + XCTAssertFalse(sut.isInProductTour) + XCTAssertEqual(observer.receivedEvents.last, .tourCompleted) + } + + func testTourDoesNotComplete_WithOnlyOneTrackDone() { + // Given: Fresh tour + sut.resetTour() + + // When: Only completing search track + sut.completeFirstSearchIfNeeded() + sut.completeSearchSpotlight() + + // Then: Tour should not be completed + XCTAssertTrue(sut.isInProductTour) + } + + func testCompleteTour_ForcesAllMilestones() { + // Given: Fresh tour + sut.resetTour() + XCTAssertTrue(sut.isInProductTour) + + // When: Force-completing tour + sut.completeTour() + + // Then: All milestones marked and tour done + XCTAssertEqual(sut.completedMilestones, .all) + XCTAssertFalse(sut.isInProductTour) + } + + func testCompleteTour_WhenAlreadyCompleted_DoesNotNotify() { + // Given: Already completed tour + sut.completeTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Calling completeTour again + sut.completeTour() + + // Then: No event should be emitted + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + // MARK: - Reset Tests + + func testResetTour_ClearsAllMilestones() { + // Given: Tour with completed milestones + sut.resetTour() + sut.completeFirstSearchIfNeeded() + sut.completeSearchSpotlight() + sut.completeExternalWebsiteVisitIfNeeded() + sut.completeExternalWebsiteSpotlight() + + // When: Resetting tour + sut.resetTour() + + // Then: Milestones should be cleared + XCTAssertEqual(sut.completedMilestones, []) + XCTAssertTrue(sut.isInProductTour) + XCTAssertTrue(sut.shouldShowProductTourHomepage) + } + + // MARK: - isInProductTour Tests + + func testIsInProductTour_WhenFreshTour_ReturnsTrue() { + sut.resetTour() + XCTAssertTrue(sut.isInProductTour) + } + + func testIsInProductTour_WhenPartiallyComplete_ReturnsTrue() { + sut.resetTour() + sut.completeFirstSearchIfNeeded() + sut.completeSearchSpotlight() + XCTAssertTrue(sut.isInProductTour) + } + + func testIsInProductTour_WhenFullyComplete_ReturnsFalse() { + sut.completeTour() + XCTAssertFalse(sut.isInProductTour) + } + + // MARK: - Independence Tests + + func testSearchAndExternalTracks_AreIndependent() { + // Given: Fresh tour + sut.resetTour() + + // When: Completing external website visit + sut.completeExternalWebsiteVisitIfNeeded() + + // Then: Search-related state should be unaffected + XCTAssertTrue(sut.shouldShowProductTourHomepage, "Homepage should still show since search not done") + XCTAssertFalse(sut.shouldShowSearchSpotlight, "Search spotlight should not show yet") + + // When: Now completing first search + sut.completeFirstSearchIfNeeded() + + // Then: Both tracks should be independently queryable + XCTAssertFalse(sut.shouldShowProductTourHomepage) + XCTAssertTrue(sut.shouldShowSearchSpotlight) + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + } + + // MARK: - Persistence Tests + + func testMilestonesAreNotPersisted_WhenExperimentDisabled() { + // Given: Experiment is disabled + let disabledManager = ProductTourManager( + userDefaults: userDefaults, + authManager: authManager, + isExperimentEnabled: { false } + ) + + // When: Milestones change + disabledManager.resetTour() + disabledManager.completeFirstSearchIfNeeded() + + // Then: Milestones should NOT be persisted + let savedValue = userDefaults.integer(forKey: "ProductTourMilestones") + XCTAssertEqual(savedValue, 0, "Milestones should not be saved when experiment is disabled") + } + + func testPersistedMilestones_AreIgnoredWhenExperimentDisabled() { + // Given: Milestones are manually saved in UserDefaults + userDefaults.set(ProductTourMilestones.firstSearchDone.rawValue, forKey: "ProductTourMilestones") + + // When: Creating a new manager instance with experiment disabled + let newManager = ProductTourManager(userDefaults: userDefaults, authManager: authManager) + + // Then: Should default to all milestones complete (experiment disabled) + XCTAssertEqual(newManager.completedMilestones, .all) + } + + // MARK: - Observer Tests + + func testObserver_ReceivesSearchCompletedEvent() { + let observer = MockProductTourObserver() + sut.resetTour() + sut.addObserver(observer) + + sut.completeFirstSearchIfNeeded() + + XCTAssertEqual(observer.receivedEvents, [.searchCompleted]) + } + + func testObserver_ReceivesExternalWebsiteVisitedEvent() { + let observer = MockProductTourObserver() + sut.resetTour() + sut.addObserver(observer) + + sut.completeExternalWebsiteVisitIfNeeded() + + XCTAssertEqual(observer.receivedEvents, [.externalWebsiteVisited]) + } + + func testObserver_ReceivesTourCompletedEvent() { + let observer = MockProductTourObserver() + sut.resetTour() + sut.addObserver(observer) + + sut.completeTour() + + XCTAssertEqual(observer.receivedEvents, [.tourCompleted]) + } + + func testObserver_ReceivesTourStartedOnReset() { + let observer = MockProductTourObserver() + sut.addObserver(observer) + + sut.resetTour() + + XCTAssertEqual(observer.receivedEvents, [.tourStarted]) + } + + func testAddObserver_DoesNotAddDuplicates() { + let observer = MockProductTourObserver() + sut.addObserver(observer) + sut.addObserver(observer) + sut.addObserver(observer) + + sut.resetTour() + XCTAssertEqual(observer.receivedEvents.count, 1) + } + + func testRemoveObserver_StopsNotifyingObserver() { + let observer = MockProductTourObserver() + sut.addObserver(observer) + sut.removeObserver(observer) + + sut.resetTour() + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + func testWeakObservers_AreDeallocatedCorrectly() { + var observer: MockProductTourObserver? = MockProductTourObserver() + sut.addObserver(observer!) + observer = nil + + XCTAssertNoThrow(sut.resetTour()) + } + + // MARK: - Sign-In Flow Tests + + func testSignInFlowDidStart_SetsIsSignInFlowActive() { + sut.resetTour() + + sut.signInFlowDidStart() + + XCTAssertTrue(sut.isSignInFlowActive) + } + + func testSignInFlowDidStart_NotifiesObserversWithSignInFlowStarted() { + sut.resetTour() + let observer = MockProductTourObserver() + sut.addObserver(observer) + + sut.signInFlowDidStart() + + XCTAssertEqual(observer.receivedEvents, [.signInFlowStarted]) + } + + func testSignInFlowDidStart_WhenAlreadyActive_DoesNotNotifyAgain() { + sut.resetTour() + sut.signInFlowDidStart() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + sut.signInFlowDidStart() + + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + func testSignInFlowDidEnd_ClearsIsSignInFlowActive() { + sut.resetTour() + sut.signInFlowDidStart() + + sut.signInFlowDidEnd() + + XCTAssertFalse(sut.isSignInFlowActive) + } + + func testSignInFlowDidEnd_NotifiesObserversWithSignInFlowEnded() { + sut.resetTour() + sut.signInFlowDidStart() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + sut.signInFlowDidEnd() + + XCTAssertEqual(observer.receivedEvents, [.signInFlowEnded]) + } + + func testSignInFlowDidEnd_WhenNotActive_DoesNotNotify() { + sut.resetTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + sut.signInFlowDidEnd() + + XCTAssertTrue(observer.receivedEvents.isEmpty) + } + + // MARK: - Account Origin Tests + + func testExistingAccountLogin_SkipsSearchTrack() { + // Given: Fresh tour + sut.resetTour() + XCTAssertTrue(sut.shouldShowProductTourHomepage) + + // When: Existing account logs in + simulateLogin(accountOrigin: .existingAccount) + + // Then: Search track is skipped, only external website track remains + XCTAssertFalse(sut.shouldShowProductTourHomepage) + XCTAssertFalse(sut.shouldShowSearchSpotlight) + XCTAssertTrue(sut.isInProductTour) + } + + func testExistingAccountLogin_ExternalWebsiteTrackStillWorks() { + // Given: Fresh tour with existing account login + sut.resetTour() + simulateLogin(accountOrigin: .existingAccount) + + // When: External website visit happens + sut.completeExternalWebsiteVisitIfNeeded() + + // Then: External website spotlight should show + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + + // When: Spotlight is dismissed + sut.completeExternalWebsiteSpotlight() + + // Then: Tour completes + XCTAssertFalse(sut.isInProductTour) + } + + func testNewAccountLogin_FullTourRemains() { + // Given: Fresh tour + sut.resetTour() + + // When: New account logs in + simulateLogin(accountOrigin: .newAccount) + + // Then: Full tour still active — search track not skipped + XCTAssertTrue(sut.shouldShowProductTourHomepage) + XCTAssertTrue(sut.isInProductTour) + } + + func testNilAccountOrigin_FullTourRemains() { + // Given: Fresh tour + sut.resetTour() + + // When: Login without account origin (claim not configured) + simulateLogin(accountOrigin: nil) + + // Then: Full tour still active — treated same as new account + XCTAssertTrue(sut.shouldShowProductTourHomepage) + XCTAssertTrue(sut.isInProductTour) + } + + func testExistingAccountLogin_WhenTourAlreadyCompleted_DoesNothing() { + // Given: Tour already completed + sut.completeTour() + XCTAssertFalse(sut.isInProductTour) + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Existing account logs in + simulateLogin(accountOrigin: .existingAccount) + + // Then: No events emitted, tour stays completed + XCTAssertTrue(observer.receivedEvents.isEmpty) + XCTAssertFalse(sut.isInProductTour) + } + + func testExistingAccountLogin_NotifiesSearchTrackCompleted() { + // Given: Fresh tour + sut.resetTour() + + let observer = MockProductTourObserver() + sut.addObserver(observer) + + // When: Existing account logs in + simulateLogin(accountOrigin: .existingAccount) + + // Then: Observers receive searchTrackCompleted so they can skip to external website track + XCTAssertTrue(observer.receivedEvents.contains(.searchTrackCompleted)) + XCTAssertFalse(observer.receivedEvents.contains(.searchCompleted)) + } + + func testCompleteWorkflow_ExistingAccount() { + // Given: Fresh tour + let observer = MockProductTourObserver() + sut.addObserver(observer) + sut.resetTour() + + // Step 1: Existing account logs in → search track auto-completed + simulateLogin(accountOrigin: .existingAccount) + XCTAssertFalse(sut.shouldShowProductTourHomepage) + XCTAssertFalse(sut.shouldShowSearchSpotlight) + + // Step 2: External website visit + sut.completeExternalWebsiteVisitIfNeeded() + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + + // Step 3: External website spotlight done → tour completes + sut.completeExternalWebsiteSpotlight() + XCTAssertFalse(sut.isInProductTour) + + XCTAssertEqual(observer.receivedEvents, [ + .tourStarted, + .searchTrackCompleted, + .externalWebsiteVisited, + .tourCompleted + ]) + } + + // MARK: - Integration Tests + + func testCompleteWorkflow_SearchFirstThenExternalWebsite() { + let observer = MockProductTourObserver() + sut.addObserver(observer) + sut.resetTour() + + // Step 1: First search + XCTAssertTrue(sut.shouldShowProductTourHomepage) + sut.completeFirstSearchIfNeeded() + XCTAssertFalse(sut.shouldShowProductTourHomepage) + XCTAssertTrue(sut.shouldShowSearchSpotlight) + + // Step 2: Search spotlight done + sut.completeSearchSpotlight() + XCTAssertFalse(sut.shouldShowSearchSpotlight) + XCTAssertTrue(sut.isInProductTour) + + // Step 3: External website visit + sut.completeExternalWebsiteVisitIfNeeded() + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + + // Step 4: External website spotlight done → tour completes + sut.completeExternalWebsiteSpotlight() + XCTAssertFalse(sut.shouldShowExternalWebsiteSpotlight) + XCTAssertFalse(sut.isInProductTour) + + XCTAssertEqual(observer.receivedEvents, [ + .tourStarted, + .searchCompleted, + .externalWebsiteVisited, + .tourCompleted + ]) + } + + func testCompleteWorkflow_ExternalWebsiteFirstThenSearch() { + let observer = MockProductTourObserver() + sut.addObserver(observer) + sut.resetTour() + + // Step 1: External website visit before search + sut.completeExternalWebsiteVisitIfNeeded() + XCTAssertTrue(sut.shouldShowProductTourHomepage, "Homepage still needed — search not done") + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + + // Step 2: External website spotlight done + sut.completeExternalWebsiteSpotlight() + XCTAssertTrue(sut.isInProductTour) + + // Step 3: First search + sut.completeFirstSearchIfNeeded() + XCTAssertTrue(sut.shouldShowSearchSpotlight) + + // Step 4: Search spotlight done → tour completes + sut.completeSearchSpotlight() + XCTAssertFalse(sut.isInProductTour) + + XCTAssertEqual(observer.receivedEvents, [ + .tourStarted, + .externalWebsiteVisited, + .searchCompleted, + .tourCompleted + ]) + } + + func testCompleteWorkflow_InterleavedMilestones() { + // Given: Fresh tour + sut.resetTour() + + // When: Both triggers happen before either spotlight is dismissed + sut.completeFirstSearchIfNeeded() + sut.completeExternalWebsiteVisitIfNeeded() + + XCTAssertTrue(sut.shouldShowSearchSpotlight) + XCTAssertTrue(sut.shouldShowExternalWebsiteSpotlight) + XCTAssertTrue(sut.isInProductTour) + + // Complete both spotlights + sut.completeSearchSpotlight() + XCTAssertTrue(sut.isInProductTour) + + sut.completeExternalWebsiteSpotlight() + XCTAssertFalse(sut.isInProductTour) + } +} + +// MARK: - Mock Classes + +final class MockProductTourObserver: ProductTourObserver { + var receivedEvents: [ProductTourEvent] = [] + + func productTour(didReceiveEvent event: ProductTourEvent) { + receivedEvents.append(event) + } +} diff --git a/firefox-ios/EcosiaTests/UI/ProductTour/ProductTourSpotlightCoordinatorTests.swift b/firefox-ios/EcosiaTests/UI/ProductTour/ProductTourSpotlightCoordinatorTests.swift new file mode 100644 index 0000000000000..80032fc1c36bf --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/ProductTour/ProductTourSpotlightCoordinatorTests.swift @@ -0,0 +1,397 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Common +import UIKit +import XCTest + +@testable import Client +@testable import Ecosia + +final class ProductTourSpotlightCoordinatorTests: XCTestCase { + + // MARK: - Properties + + private var sut: ProductTourSpotlightCoordinator! + private var viewController: UIViewController! + private var bottomContentView: UIView! + private var tourManager: ProductTourManager! + private var userDefaults: UserDefaults! + private var suiteName: String! + + // MARK: - Setup / Teardown + + override func setUp() { + super.setUp() + suiteName = "ProductTourSpotlightCoordinatorTests-\(UUID().uuidString)" + userDefaults = UserDefaults(suiteName: suiteName)! + tourManager = ProductTourManager(userDefaults: userDefaults, isExperimentEnabled: { true }) + + viewController = UIViewController() + bottomContentView = UIView() + + // Load the view so layout is available + viewController.loadViewIfNeeded() + viewController.view.addSubview(bottomContentView) + + sut = ProductTourSpotlightCoordinator( + viewController: viewController, + bottomContentView: bottomContentView, + theme: LightTheme(), + tourManager: tourManager + ) + + // Put the tour into an active state for all tests + tourManager.resetTour() + } + + override func tearDown() { + sut = nil + viewController = nil + bottomContentView = nil + tourManager = nil + + if let suiteName { + userDefaults.removePersistentDomain(forName: suiteName) + } + userDefaults = nil + suiteName = nil + + super.tearDown() + } + + // MARK: - Initial State Tests + + func testInitialState_IsNotShowingSpotlight() { + XCTAssertFalse(sut.isShowingSpotlight) + } + + // MARK: - productTour(didReceiveEvent:) Tests + + func testDidReceiveEvent_searchCompleted_showsSpotlight() { + sut.productTour(didReceiveEvent: .searchCompleted) + + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_searchTrackCompleted_doesNotShowSpotlight() { + sut.productTour(didReceiveEvent: .searchTrackCompleted) + + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_signInFlowStarted_doesNotShowSpotlight() { + sut.productTour(didReceiveEvent: .signInFlowStarted) + + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_signInFlowStarted_doesNotDismissExistingSpotlight() { + // Given: A spotlight is already showing + sut.productTour(didReceiveEvent: .searchCompleted) + XCTAssertTrue(sut.isShowingSpotlight) + + // When: Sign-in flow starts + sut.productTour(didReceiveEvent: .signInFlowStarted) + + // Then: Existing spotlight remains visible + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_signInFlowEnded_doesNotShowSpotlight() { + sut.productTour(didReceiveEvent: .signInFlowEnded) + + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_signInFlowEnded_doesNotDismissExistingSpotlight() { + // Given: A spotlight is already showing + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + XCTAssertTrue(sut.isShowingSpotlight) + + // When: Sign-in flow ends + sut.productTour(didReceiveEvent: .signInFlowEnded) + + // Then: Existing spotlight remains visible + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_externalWebsiteVisited_showsSpotlight() { + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_tourCompleted_dismissesSpotlight() { + // Given: A spotlight is already showing + sut.productTour(didReceiveEvent: .searchCompleted) + XCTAssertTrue(sut.isShowingSpotlight) + + // When: Tour is completed + sut.productTour(didReceiveEvent: .tourCompleted) + + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_tourStarted_dismissesSpotlight() { + // Given: A spotlight is already showing + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + XCTAssertTrue(sut.isShowingSpotlight) + + // When: Tour is reset/started + sut.productTour(didReceiveEvent: .tourStarted) + + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testDidReceiveEvent_searchCompleted_replacesExistingSpotlight() { + // Given: External website spotlight is already showing + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + XCTAssertTrue(sut.isShowingSpotlight) + + // When: A new spotlight event arrives + sut.productTour(didReceiveEvent: .searchCompleted) + + // Then: Coordinator should still be showing a spotlight (the new one) + XCTAssertTrue(sut.isShowingSpotlight) + } + + // MARK: - showSpotlightIfNeeded Tests + + func testShowSpotlightIfNeeded_whenSearchSpotlightDue_showsSpotlight() { + // Given: First search is done but spotlight not yet shown + tourManager.completeFirstSearchIfNeeded() + + sut.showSpotlightIfNeeded() + + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testShowSpotlightIfNeeded_whenExternalWebsiteSpotlightDue_showsSpotlight() { + // Given: External website visited but spotlight not yet shown + tourManager.completeExternalWebsiteVisitIfNeeded() + + sut.showSpotlightIfNeeded() + + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testShowSpotlightIfNeeded_whenNothingDue_doesNotShowSpotlight() { + // Given: Fresh tour with no milestones completed + sut.showSpotlightIfNeeded() + + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testShowSpotlightIfNeeded_whenTourAlreadyComplete_doesNotShowSpotlight() { + tourManager.completeTour() + + sut.showSpotlightIfNeeded() + + XCTAssertFalse(sut.isShowingSpotlight) + } + + // MARK: - updateTheme Tests + + func testUpdateTheme_doesNotCrashWhenNoSpotlightShowing() { + XCTAssertNoThrow(sut.updateTheme(DarkTheme())) + } + + func testUpdateTheme_doesNotCrashWhenSpotlightIsShowing() { + sut.productTour(didReceiveEvent: .searchCompleted) + + XCTAssertNoThrow(sut.updateTheme(DarkTheme())) + } + + // MARK: - openURL Callback Tests + + func testOpenURLCallback_isNilByDefault() { + XCTAssertNil(sut.openURL) + } + + func testOpenURLCallback_canBeAssigned() { + var capturedURL: URL? + sut.openURL = { capturedURL = $0 } + + sut.openURL?(URL(string: "https://example.com")!) + + XCTAssertEqual(capturedURL, URL(string: "https://example.com")) + } + + // MARK: - Search Spotlight: Primary Action (step advancement) + + func testSearchSpotlight_primaryAction_onStep1_doesNotDismiss() { + // Given: Search spotlight is on step 1 of 2 + sut.productTour(didReceiveEvent: .searchCompleted) + XCTAssertTrue(sut.isShowingSpotlight) + + // When: Primary button tapped + sut.handlePrimaryAction() + + // Then: Spotlight stays visible (moved to step 2, not finished) + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testSearchSpotlight_primaryAction_onStep1_doesNotCompleteSearchMilestone() { + // Given: Search spotlight on step 1 + sut.productTour(didReceiveEvent: .searchCompleted) + + // When: Primary tapped (moves to step 2, not yet done) + sut.handlePrimaryAction() + + // Then: Search spotlight milestone still incomplete + XCTAssertFalse(tourManager.completedMilestones.contains(.searchSpotlightDone)) + } + + func testSearchSpotlight_primaryAction_onStep2_dismissesSpotlight() { + // Given: Search spotlight advanced to final step + sut.productTour(didReceiveEvent: .searchCompleted) + sut.handlePrimaryAction() // step 1 → step 2 + + // When: Primary tapped on final step + sut.handlePrimaryAction() + + // Then: Spotlight is dismissed + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testSearchSpotlight_primaryAction_onStep2_completesSearchMilestone() { + // Given: Search spotlight on final step + sut.productTour(didReceiveEvent: .searchCompleted) + sut.handlePrimaryAction() // step 1 → step 2 + + // When: Final primary tapped + sut.handlePrimaryAction() + + // Then: Search spotlight milestone is marked complete + XCTAssertTrue(tourManager.completedMilestones.contains(.searchSpotlightDone)) + } + + // MARK: - Search Spotlight: Secondary Action (skip / back) + + func testSearchSpotlight_secondaryAction_onStep1_skips() { + // Given: Search spotlight on step 1 (secondary = skip) + sut.productTour(didReceiveEvent: .searchCompleted) + + // When: Secondary tapped + sut.handleSecondaryAction() + + // Then: Spotlight dismissed and milestone complete + XCTAssertFalse(sut.isShowingSpotlight) + XCTAssertTrue(tourManager.completedMilestones.contains(.searchSpotlightDone)) + } + + func testSearchSpotlight_secondaryAction_onStep2_goesBack() { + // Given: Search spotlight advanced to step 2 (secondary = back) + sut.productTour(didReceiveEvent: .searchCompleted) + sut.handlePrimaryAction() // step 1 → step 2 + + // When: Secondary tapped (go back) + sut.handleSecondaryAction() + + // Then: Spotlight stays visible, milestone not yet complete + XCTAssertTrue(sut.isShowingSpotlight) + XCTAssertFalse(tourManager.completedMilestones.contains(.searchSpotlightDone)) + } + + func testSearchSpotlight_secondaryAction_backThenPrimaryTwice_completesTour() { + // Given: Navigate forward, back, then complete normally + sut.productTour(didReceiveEvent: .searchCompleted) + sut.handlePrimaryAction() // step 1 → step 2 + sut.handleSecondaryAction() // step 2 → step 1 (back) + sut.handlePrimaryAction() // step 1 → step 2 + sut.handlePrimaryAction() // step 2 → complete + + XCTAssertFalse(sut.isShowingSpotlight) + XCTAssertTrue(tourManager.completedMilestones.contains(.searchSpotlightDone)) + } + + // MARK: - External Website Spotlight: Primary Action + + func testExternalWebsiteSpotlight_primaryAction_dismissesSpotlight() { + // Given: External website spotlight (single step) + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + + // When: Primary tapped + sut.handlePrimaryAction() + + // Then: Dismissed + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testExternalWebsiteSpotlight_primaryAction_completesMilestone() { + // Given: External website spotlight + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + + // When: Primary tapped + sut.handlePrimaryAction() + + // Then: Milestone marked complete + XCTAssertTrue(tourManager.completedMilestones.contains(.externalWebsiteSpotlightDone)) + } + + // MARK: - External Website Spotlight: Secondary Action (Read More / openURL) + + func testExternalWebsiteSpotlight_secondaryAction_callsOpenURL() { + // Given: External website spotlight with openURL handler assigned + var capturedURL: URL? + sut.openURL = { capturedURL = $0 } + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + + // When: Secondary tapped (Read More) + sut.handleSecondaryAction() + + // Then: openURL was called with a non-nil URL + XCTAssertNotNil(capturedURL) + } + + func testExternalWebsiteSpotlight_secondaryAction_dismissesSpotlight() { + // Given: External website spotlight + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + + // When: Secondary tapped + sut.handleSecondaryAction() + + // Then: Spotlight dismissed + XCTAssertFalse(sut.isShowingSpotlight) + } + + func testExternalWebsiteSpotlight_secondaryAction_completesMilestone() { + // Given: External website spotlight + sut.productTour(didReceiveEvent: .externalWebsiteVisited) + + // When: Secondary tapped + sut.handleSecondaryAction() + + // Then: Milestone marked complete even when navigating via Read More + XCTAssertTrue(tourManager.completedMilestones.contains(.externalWebsiteSpotlightDone)) + } + + // MARK: - Observer Registration Tests + + func testCoordinator_isRegisteredAsObserver_onInit() { + // We verify indirect registration by checking the coordinator reacts + // to events broadcast from the tour manager it was given + tourManager.completeFirstSearchIfNeeded() // fires .searchCompleted + + XCTAssertTrue(sut.isShowingSpotlight) + } + + func testCoordinator_deregistersOnDeinit() { + // Given: A coordinator that reacts to tour events + var coordinator: ProductTourSpotlightCoordinator? = ProductTourSpotlightCoordinator( + viewController: viewController, + bottomContentView: bottomContentView, + theme: LightTheme(), + tourManager: tourManager + ) + _ = coordinator // silence warning + + // When: The coordinator is deallocated + coordinator = nil + + // Then: Further tour events should not crash + XCTAssertNoThrow(tourManager.completeFirstSearchIfNeeded()) + } +} diff --git a/firefox-ios/EcosiaTests/UI/TabManagement/InvisibleTabManagerTests.swift b/firefox-ios/EcosiaTests/UI/TabManagement/InvisibleTabManagerTests.swift new file mode 100644 index 0000000000000..b2c7aa4b198f2 --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/TabManagement/InvisibleTabManagerTests.swift @@ -0,0 +1,272 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Common +import WebKit +@testable import Client + +/// Test suite for InvisibleTabManager functionality +final class InvisibleTabManagerTests: XCTestCase { + + // MARK: - Properties + + private var manager: InvisibleTabManager! + private var testTab: Tab! + private var windowUUID: WindowUUID! + + // MARK: - Setup & Teardown + + override func setUp() { + super.setUp() + + // Create a window UUID for testing + windowUUID = WindowUUID() + + // Get the shared manager instance and clear any existing state + manager = InvisibleTabManager.shared + manager.clearAllInvisibleTabs() + + // Create a test tab + testTab = createTestTab() + } + + override func tearDown() { + // Clean up state after each test + manager.clearAllInvisibleTabs() + testTab = nil + windowUUID = nil + manager = nil + super.tearDown() + } + + // MARK: - Helper Methods + + private func createTestTab(url: URL? = URL(string: "https://example.com")) -> Tab { + let profile = MockProfile() // Use existing MockProfile from test suite + return Tab(profile: profile, isPrivate: false, windowUUID: windowUUID) + } + + // MARK: - Tab Visibility Management Tests + + func testMarkTabAsInvisible() { + // Given + XCTAssertFalse(manager.isTabInvisible(testTab)) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 0) + + // When + manager.markTabAsInvisible(testTab) + + // Then + XCTAssertTrue(manager.isTabInvisible(testTab)) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1) + XCTAssertTrue(manager.invisibleTabUUIDs.contains(testTab.tabUUID)) + } + + func testMarkTabAsVisible() { + // Given + manager.markTabAsInvisible(testTab) + XCTAssertTrue(manager.isTabInvisible(testTab)) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1) + + // When + manager.markTabAsVisible(testTab) + + // Then + XCTAssertFalse(manager.isTabInvisible(testTab)) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 0) + XCTAssertFalse(manager.invisibleTabUUIDs.contains(testTab.tabUUID)) + } + + func testMarkSameTabMultipleTimes() { + // Given + manager.markTabAsInvisible(testTab) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1) + + // When + manager.markTabAsInvisible(testTab) + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1, "Should only count the tab once") + XCTAssertTrue(manager.isTabInvisible(testTab)) + } + + func testClearAllInvisibleTabs() { + // Given + manager.markTabAsInvisible(testTab) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1) + + // When + manager.clearAllInvisibleTabs() + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 0) + XCTAssertFalse(manager.isTabInvisible(testTab)) + } + + func testMultipleTabsManagement() { + // Given + let tab1 = createTestTab(url: URL(string: "https://example1.com")) + let tab2 = createTestTab(url: URL(string: "https://example2.com")) + let tab3 = createTestTab(url: URL(string: "https://example3.com")) + + // When + manager.markTabAsInvisible(tab1) + manager.markTabAsInvisible(tab2) + manager.markTabAsInvisible(tab3) + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 3) + XCTAssertTrue(manager.isTabInvisible(tab1)) + XCTAssertTrue(manager.isTabInvisible(tab2)) + XCTAssertTrue(manager.isTabInvisible(tab3)) + + // When + manager.markTabAsVisible(tab2) + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 2) + XCTAssertTrue(manager.isTabInvisible(tab1)) + XCTAssertFalse(manager.isTabInvisible(tab2)) + XCTAssertTrue(manager.isTabInvisible(tab3)) + } + + // MARK: - Filter Methods Tests + + func testGetVisibleTabs() { + // Given + let tab1 = createTestTab(url: URL(string: "https://visible1.com")) + let tab2 = createTestTab(url: URL(string: "https://invisible.com")) + let tab3 = createTestTab(url: URL(string: "https://visible2.com")) + let allTabs = [tab1, tab2, tab3] + + manager.markTabAsInvisible(tab2) + + // When + let visibleTabs = manager.getVisibleTabs(from: allTabs) + + // Then + XCTAssertEqual(visibleTabs.count, 2) + XCTAssertTrue(visibleTabs.contains(tab1)) + XCTAssertFalse(visibleTabs.contains(tab2)) + XCTAssertTrue(visibleTabs.contains(tab3)) + } + + func testGetInvisibleTabs() { + // Given + let tab1 = createTestTab(url: URL(string: "https://visible.com")) + let tab2 = createTestTab(url: URL(string: "https://invisible1.com")) + let tab3 = createTestTab(url: URL(string: "https://invisible2.com")) + let allTabs = [tab1, tab2, tab3] + + manager.markTabAsInvisible(tab2) + manager.markTabAsInvisible(tab3) + + // When + let invisibleTabs = manager.getInvisibleTabs(from: allTabs) + + // Then + XCTAssertEqual(invisibleTabs.count, 2) + XCTAssertFalse(invisibleTabs.contains(tab1)) + XCTAssertTrue(invisibleTabs.contains(tab2)) + XCTAssertTrue(invisibleTabs.contains(tab3)) + } + + // MARK: - Cleanup Tests + + func testCleanupRemovedTabs() { + // Given + let tab1 = createTestTab(url: URL(string: "https://tab1.com")) + let tab2 = createTestTab(url: URL(string: "https://tab2.com")) + let tab3 = createTestTab(url: URL(string: "https://tab3.com")) + + manager.markTabAsInvisible(tab1) + manager.markTabAsInvisible(tab2) + manager.markTabAsInvisible(tab3) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 3) + + // When + let existingTabUUIDs: Set = [tab1.tabUUID, tab3.tabUUID] + manager.cleanupRemovedTabs(existingTabUUIDs: existingTabUUIDs) + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 2) + XCTAssertTrue(manager.isTabInvisible(tab1)) + XCTAssertFalse(manager.isTabInvisible(tab2)) + XCTAssertTrue(manager.isTabInvisible(tab3)) + } + + // MARK: - Thread Safety Tests + + func testConcurrentAccess() { + let expectation = self.expectation(description: "Concurrent operations should complete") + let concurrentQueue = DispatchQueue(label: "test.concurrent", attributes: .concurrent) + let group = DispatchGroup() + + // Create multiple tabs for testing + var tabs: [Tab] = [] + for i in 0..<100 { + tabs.append(createTestTab(url: URL(string: "https://test\(i).com"))) + } + + // Perform concurrent operations + for tab in tabs { + group.enter() + concurrentQueue.async { + self.manager.markTabAsInvisible(tab) + group.leave() + } + } + + group.notify(queue: .main) { + XCTAssertEqual(self.manager.invisibleTabUUIDs.count, 100) + expectation.fulfill() + } + + waitForExpectations(timeout: 5.0) + } + + func testLargeScaleOperations() { + // Given + var tabs: [Tab] = [] + for i in 0..<1000 { + tabs.append(createTestTab(url: URL(string: "https://large\(i).com"))) + } + + // When + for tab in tabs { + manager.markTabAsInvisible(tab) + } + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1000) + + // When + manager.clearAllInvisibleTabs() + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 0) + } + + func testIsTabInvisibleWithNonExistentTab() { + // Given + let nonExistentTab = createTestTab(url: URL(string: "https://nonexistent.com")) + + // When/Then + XCTAssertFalse(manager.isTabInvisible(nonExistentTab)) + } + + func testMarkingSameTabTwice() { + // Given + manager.markTabAsInvisible(testTab) + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1) + + // When + manager.markTabAsInvisible(testTab) + + // Then + XCTAssertEqual(manager.invisibleTabUUIDs.count, 1, "Should only count once for same UUID") + XCTAssertTrue(manager.isTabInvisible(testTab)) + } +} diff --git a/firefox-ios/EcosiaTests/UI/TabManagement/TabAutoCloseManagerTests.swift b/firefox-ios/EcosiaTests/UI/TabManagement/TabAutoCloseManagerTests.swift new file mode 100644 index 0000000000000..db520f782467f --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/TabManagement/TabAutoCloseManagerTests.swift @@ -0,0 +1,446 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import WebKit +import Common +@testable import Client + +final class InvisibleTabAutoCloseManagerTests: XCTestCase { + + // MARK: - Properties + + private var manager: InvisibleTabAutoCloseManager! + private var mockTabManager: TabAutoCloseTestMockTabManager! + private var mockNotificationCenter: TabAutoCloseManagerMockNotificationCenter! + + // MARK: - Setup and Teardown + + override func setUp() { + super.setUp() + + /* + Since we're using singletons, we need to clear any leftover state + from previous tests to avoid interference between test runs + */ + InvisibleTabAutoCloseManager.shared.cleanupAllObservers() + InvisibleTabManager.shared.clearAllInvisibleTabs() + + mockTabManager = TabAutoCloseTestMockTabManager() + mockNotificationCenter = TabAutoCloseManagerMockNotificationCenter() + + manager = InvisibleTabAutoCloseManager.shared + manager.setTabManager(mockTabManager) + } + + override func tearDown() { + manager.cleanupAllObservers() + InvisibleTabManager.shared.clearAllInvisibleTabs() + mockTabManager = nil + mockNotificationCenter = nil + super.tearDown() + } + + // MARK: - Helper Methods + + private func createMockTab(uuid: String = UUID().uuidString, isPrivate: Bool = false, isInvisible: Bool = true) -> Tab { + let profile = MockProfile() + let tab = Tab(profile: profile, isPrivate: isPrivate, windowUUID: WindowUUID()) + tab.tabUUID = uuid + + /* + The auto-close manager only tracks invisible tabs, + so we mark them that way by default for testing + */ + if isInvisible { + InvisibleTabManager.shared.markTabAsInvisible(tab) + } + return tab + } + + // MARK: - Basic Setup Tests + + func testSetupAutoCloseForTab() { + // Given + let tab = createMockTab(uuid: "test-tab") + mockTabManager.tabs = [tab] + + // When + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 1, "Should track one tab") + XCTAssertTrue(manager.trackedTabUUIDs.contains(tab.tabUUID), "Should track the specific tab") + } + + func testSetupAutoCloseForMultipleTabs() { + // Given + let tabs = [ + createMockTab(uuid: "tab1"), + createMockTab(uuid: "tab2"), + createMockTab(uuid: "tab3") + ] + mockTabManager.tabs = tabs + + // When + tabs.forEach { manager.setupAutoCloseForTab($0, on: .EcosiaAuthStateChanged, timeout: 10.0) } + + // Then + XCTAssertEqual(manager.trackedTabCount, 3, "Should track all tabs") + tabs.forEach { tab in + XCTAssertTrue(manager.trackedTabUUIDs.contains(tab.tabUUID), "Should track tab \(tab.tabUUID)") + } + } + + // MARK: - Cancel Auto-Close Tests + + func testCancelAutoCloseForTab() { + // Given + let tab = createMockTab(uuid: "cancel-tab") + mockTabManager.tabs = [tab] + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 10.0) + XCTAssertEqual(manager.trackedTabCount, 1) + + // When + manager.cancelAutoCloseForTab(tab.tabUUID) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should not track any tabs after cancel") + XCTAssertFalse(manager.trackedTabUUIDs.contains(tab.tabUUID), "Should not track the cancelled tab") + } + + func testCancelAutoCloseForNonExistentTab() { + // Given + let nonExistentUUID = "non-existent-tab" + + // When + manager.cancelAutoCloseForTab(nonExistentUUID) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should not affect tracking state") + } + + func testCancelAutoCloseForMultipleTabs() { + // Given + let tabs = [ + createMockTab(uuid: "tab1"), + createMockTab(uuid: "tab2"), + createMockTab(uuid: "tab3") + ] + mockTabManager.tabs = tabs + tabs.forEach { manager.setupAutoCloseForTab($0, on: .EcosiaAuthStateChanged, timeout: 10.0) } + XCTAssertEqual(manager.trackedTabCount, 3) + + // When + manager.cancelAutoCloseForTabs(tabs.map { $0.tabUUID }) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should not track any tabs after cancel") + } + + // MARK: - Notification Handling Tests + + func testNotificationTriggersAutoClose() { + // Given + let tab = createMockTab(uuid: "notification-tab") + mockTabManager.tabs = [tab] + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // When + NotificationCenter.default.post(name: .EcosiaAuthStateChanged, object: nil) + + // Allow some processing time + let expectation = XCTestExpectation(description: "Notification processed") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should cleanup tracking after notification") + } + + func testNotificationWithCustomName() { + // Given + let tab = createMockTab(uuid: "custom-notification-tab") + mockTabManager.tabs = [tab] + let customNotification = Notification.Name("CustomAuthNotification") + manager.setupAutoCloseForTab(tab, on: customNotification, timeout: 10.0) + + // When + NotificationCenter.default.post(name: customNotification, object: nil) + + // Allow some processing time + let expectation = XCTestExpectation(description: "Custom notification processed") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should cleanup tracking after custom notification") + } + + func testNotificationDoesNotAffectOtherTabs() { + // Given + let authTab = createMockTab(uuid: "auth-tab") + let regularTab = createMockTab(uuid: "regular-tab") + mockTabManager.tabs = [authTab, regularTab] + + manager.setupAutoCloseForTab(authTab, on: .EcosiaAuthStateChanged, timeout: 10.0) + manager.setupAutoCloseForTab(regularTab, on: Notification.Name("DifferentNotification"), timeout: 10.0) + + // When + NotificationCenter.default.post(name: .EcosiaAuthStateChanged, object: nil) + + // Allow some processing time + let expectation = XCTestExpectation(description: "Notification processed") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 1, "Should only affect tabs listening to the specific notification") + XCTAssertFalse(manager.trackedTabUUIDs.contains(authTab.tabUUID), "Auth tab should be cleaned up") + XCTAssertTrue(manager.trackedTabUUIDs.contains(regularTab.tabUUID), "Regular tab should still be tracked") + } + + // MARK: - Timeout Tests + + func testTimeoutTriggersAutoClose() { + // Given + let tab = createMockTab(uuid: "timeout-tab") + mockTabManager.tabs = [tab] + let shortTimeout: TimeInterval = 0.1 + + // When + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: shortTimeout) + + // Wait for timeout to trigger + let expectation = XCTestExpectation(description: "Timeout processed") + DispatchQueue.main.asyncAfter(deadline: .now() + shortTimeout + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should cleanup tracking after timeout") + } + + func testTimeoutDoesNotTriggerIfNotificationReceived() { + // Given + let tab = createMockTab(uuid: "no-timeout-tab") + mockTabManager.tabs = [tab] + let timeout: TimeInterval = 0.2 + + // When + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: timeout) + + // Send notification before timeout + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + NotificationCenter.default.post(name: .EcosiaAuthStateChanged, object: nil) + } + + // Wait past timeout + let expectation = XCTestExpectation(description: "Processing complete") + DispatchQueue.main.asyncAfter(deadline: .now() + timeout + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should cleanup tracking via notification, not timeout") + } + + // MARK: - Tab Manager Integration Tests + + func testAutoCloseRemovesTabFromManager() { + // Given + let tab = createMockTab(uuid: "remove-tab") + mockTabManager.tabs = [tab] + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // When + NotificationCenter.default.post(name: .EcosiaAuthStateChanged, object: nil) + + // Allow some processing time + let expectation = XCTestExpectation(description: "Tab removal processed") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertFalse(mockTabManager.tabs.contains(tab), "Tab should be removed from tab manager") + } + + func testAutoCloseWithMultipleTabsRemovesCorrectTab() { + // Given + let autoCloseTab = createMockTab(uuid: "auto-close-tab") + let regularTab = createMockTab(uuid: "regular-tab") + mockTabManager.tabs = [autoCloseTab, regularTab] + manager.setupAutoCloseForTab(autoCloseTab, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // When + NotificationCenter.default.post(name: .EcosiaAuthStateChanged, object: nil) + + // Allow some processing time + let expectation = XCTestExpectation(description: "Tab removal processed") + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + } + wait(for: [expectation], timeout: 1.0) + + // Then + XCTAssertFalse(mockTabManager.tabs.contains(autoCloseTab), "Auto-close tab should be removed") + XCTAssertTrue(mockTabManager.tabs.contains(regularTab), "Regular tab should remain") + } + + // MARK: - Cleanup Tests + + func testCleanupAllObservers() { + // Given + let tabs = [ + createMockTab(uuid: "tab1"), + createMockTab(uuid: "tab2"), + createMockTab(uuid: "tab3") + ] + mockTabManager.tabs = tabs + tabs.forEach { manager.setupAutoCloseForTab($0, on: .EcosiaAuthStateChanged, timeout: 10.0) } + XCTAssertEqual(manager.trackedTabCount, 3) + + // When + manager.cleanupAllObservers() + + // Then + XCTAssertEqual(manager.trackedTabCount, 0, "Should cleanup all tracking") + } + + func testCleanupAllObserversIdempotent() { + // Given + let tab = createMockTab(uuid: "idempotent-tab") + mockTabManager.tabs = [tab] + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // When + manager.cleanupAllObservers() + manager.cleanupAllObservers() + manager.cleanupAllObservers() + + // Then - Should not crash + XCTAssertEqual(manager.trackedTabCount, 0, "Should remain at zero") + } + + // MARK: - State Consistency Tests + + func testTrackedTabUUIDsConsistency() { + // Given + let tabs = [ + createMockTab(uuid: "uuid1"), + createMockTab(uuid: "uuid2"), + createMockTab(uuid: "uuid3") + ] + mockTabManager.tabs = tabs + + // When + tabs.forEach { manager.setupAutoCloseForTab($0, on: .EcosiaAuthStateChanged, timeout: 10.0) } + + // Then + XCTAssertEqual(manager.trackedTabCount, manager.trackedTabUUIDs.count, "Count should match UUID array size") + tabs.forEach { tab in + XCTAssertTrue(manager.trackedTabUUIDs.contains(tab.tabUUID), "Should contain each tab UUID") + } + } + + func testTabRemovalUpdatesTracking() { + // Given + let tab1 = createMockTab(uuid: "tab1") + let tab2 = createMockTab(uuid: "tab2") + mockTabManager.tabs = [tab1, tab2] + manager.setupAutoCloseForTab(tab1, on: .EcosiaAuthStateChanged, timeout: 10.0) + manager.setupAutoCloseForTab(tab2, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // When + manager.cancelAutoCloseForTab(tab1.tabUUID) + + // Then + XCTAssertEqual(manager.trackedTabCount, 1, "Should track one less tab") + XCTAssertFalse(manager.trackedTabUUIDs.contains(tab1.tabUUID), "Should not track removed tab") + XCTAssertTrue(manager.trackedTabUUIDs.contains(tab2.tabUUID), "Should still track remaining tab") + } + + // MARK: - Error Handling Tests + + func testDuplicateSetupForSameTab() { + // Given + let tab = createMockTab(uuid: "duplicate-tab") + mockTabManager.tabs = [tab] + + // When + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 10.0) + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 5.0) + manager.setupAutoCloseForTab(tab, on: .EcosiaAuthStateChanged, timeout: 15.0) + + // Then + XCTAssertEqual(manager.trackedTabCount, 1, "Should only track tab once, regardless of multiple setups") + } + + // MARK: - Memory Management Tests + + func testNoRetainCyclesAfterCleanup() { + // Given + var tab: Tab? = createMockTab(uuid: "memory-tab") + mockTabManager.tabs = [tab!] + manager.setupAutoCloseForTab(tab!, on: .EcosiaAuthStateChanged, timeout: 10.0) + + // When + manager.cleanupAllObservers() + tab = nil + + // Then - Should not crash and tab should be deallocated + XCTAssertEqual(manager.trackedTabCount, 0, "Should cleanup all tracking") + } +} + +// MARK: - Custom Mock TabManager + +/* + The shared MockTabManager has an empty removeTab implementation, + but our tests need to verify that tabs actually get removed. + This subclass provides a working implementation for testing. + */ +class TabAutoCloseTestMockTabManager: MockTabManager { + override func removeTab(_ tab: Tab, completion: (() -> Void)?) { + tabs.removeAll { $0.tabUUID == tab.tabUUID } + completion?() + } +} + +// MARK: - Mock Notification Center +// swiftlint:disable large_tuple +class TabAutoCloseManagerMockNotificationCenter: NotificationCenter, @unchecked Sendable { + private var observers: [(name: Notification.Name, observer: Any, selector: Selector)] = [] + + override func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?) { + if let name = aName { + observers.append((name: name, observer: observer, selector: aSelector)) + } + } + + override func removeObserver(_ observer: Any, name aName: NSNotification.Name?, object anObject: Any?) { + observers.removeAll { (_, obs, _) in + return (obs as AnyObject) === (observer as AnyObject) + } + } + + func simulateNotification(name: Notification.Name, object: Any? = nil, userInfo: [AnyHashable: Any]? = nil) { + let notification = Notification(name: name, object: object, userInfo: userInfo) + + for (notificationName, observer, selector) in observers where notificationName == name { + _ = (observer as AnyObject).perform(selector, with: notification) + } + } +} +// swiftlint:enable large_tuple diff --git a/firefox-ios/EcosiaTests/UI/Themable/ThemableMockThemeManager.swift b/firefox-ios/EcosiaTests/UI/Themable/ThemableMockThemeManager.swift new file mode 100644 index 0000000000000..6513701414cef --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/Themable/ThemableMockThemeManager.swift @@ -0,0 +1,36 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import SwiftUI +import Common + +final class ThemeableMockThemeManager: ThemeManager { + var currentTheme: Theme = LightTheme() + var themeChangeHandler: ((Theme) -> Void)? + + func getCurrentTheme(for window: WindowUUID?) -> Theme { + return currentTheme + } + + func setCurrentTheme(_ theme: Theme) { + currentTheme = theme + themeChangeHandler?(theme) + } + + var systemThemeIsOn: Bool = false + var automaticBrightnessIsOn: Bool = false + var automaticBrightnessValue: Float = 0.5 + + func setSystemTheme(isOn: Bool) {} + func setManualTheme(to newTheme: ThemeType) {} + func getUserManualTheme() -> ThemeType { return .light } + func setAutomaticBrightness(isOn: Bool) {} + func setAutomaticBrightnessValue(_ value: Float) {} + func applyThemeUpdatesToWindows() {} + func setPrivateTheme(isOn: Bool, for window: WindowUUID) {} + func getPrivateThemeIsOn(for window: WindowUUID) -> Bool { return false } + func setWindow(_ window: UIWindow, for uuid: WindowUUID) {} + func windowDidClose(uuid: WindowUUID) {} + func windowNonspecificTheme() -> Theme { return currentTheme } +} diff --git a/firefox-ios/EcosiaTests/UI/Themable/ThemeableSwiftUIViewTests.swift b/firefox-ios/EcosiaTests/UI/Themable/ThemeableSwiftUIViewTests.swift new file mode 100644 index 0000000000000..d08a42c353191 --- /dev/null +++ b/firefox-ios/EcosiaTests/UI/Themable/ThemeableSwiftUIViewTests.swift @@ -0,0 +1,113 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import SwiftUI +import Common +@testable import Ecosia + +final class ThemeableSwiftUIViewTests: XCTestCase { + + // MARK: - Unit Tests + + func testThemeUpdatesCorrectly() { + // Given + let mockThemeManager = ThemeableMockThemeManager() + mockThemeManager.currentTheme = LightTheme() + + // When + var testTheme = TestTheme() + testTheme.applyTheme(theme: mockThemeManager.getCurrentTheme(for: .XCTestDefaultUUID)) + + // Then + XCTAssertEqual(testTheme.backgroundColor, Color.white) + XCTAssertEqual(testTheme.textColor, Color.black) + + // When theme changes + mockThemeManager.currentTheme = DarkTheme() + testTheme.applyTheme(theme: mockThemeManager.getCurrentTheme(for: .XCTestDefaultUUID)) + + // Then + XCTAssertEqual(testTheme.backgroundColor, Color.black) + XCTAssertEqual(testTheme.textColor, Color.white) + } + + func testNilWindowUUID() { + // Given + var testTheme = TestTheme() + let windowUUID: WindowUUID? = nil + let themeBinding = Binding( + get: { testTheme }, + set: { testTheme = $0 } + ) + + // When/Then + let view = Text("Test").ecosiaThemed(windowUUID, themeBinding) + let viewMirror = Mirror(reflecting: view) + XCTAssertTrue(viewMirror.description.contains("ThemeModifier")) + } + + // MARK: - Integration Tests + + @available(iOS 16.0, *) + func testThemeModifierWithMockView() { + // Given + let initialTheme = TestTheme() + let mockView = MockView(theme: initialTheme, windowUUID: .XCTestDefaultUUID) + + // When/Then + let mirror = Mirror(reflecting: mockView.body) + XCTAssertTrue(mirror.description.contains("ThemeModifier")) + } +} + +extension ThemeableSwiftUIViewTests { + + struct TestTheme: EcosiaThemeable { + var backgroundColor = Color.white + var textColor = Color.black + var themeApplied = false + + mutating func applyTheme(theme: Theme) { + backgroundColor = theme.type == .dark ? Color.black : Color.white + textColor = theme.type == .dark ? Color.white : Color.black + themeApplied = true + } + } + + @available(iOS 16.0, *) + struct MockView: View { + // Using StateObject instead of State to avoid "Accessing State's value outside of being installed on a View" warning + @StateObject private var themeContainer = ThemeContainer() + let initialTheme: TestTheme + let windowUUID: WindowUUID + + init(theme: TestTheme, windowUUID: WindowUUID) { + self.initialTheme = theme + self.windowUUID = windowUUID + self.themeContainer.theme = theme + } + + var body: some View { + Text("Test") + .foregroundColor(themeContainer.theme.textColor) + .background(themeContainer.theme.backgroundColor) + .ecosiaThemed(windowUUID, $themeContainer.theme) + } + } + + // Helper class to hold our theme in an ObservableObject + class ThemeContainer: ObservableObject { + @Published var theme = TestTheme() + } + + class TestNotificationCenter: NotificationCenter, @unchecked Sendable { + var postedNotifications: [Notification.Name] = [] + + override func post(name: Notification.Name, object: Any?, userInfo: [AnyHashable: Any]? = nil) { + postedNotifications.append(name) + super.post(name: name, object: object, userInfo: userInfo) + } + } +} diff --git a/firefox-ios/EcosiaTests/UnitTest.xctestplan b/firefox-ios/EcosiaTests/UnitTest.xctestplan new file mode 100644 index 0000000000000..bd2d0b6c934be --- /dev/null +++ b/firefox-ios/EcosiaTests/UnitTest.xctestplan @@ -0,0 +1,24 @@ +{ + "configurations" : [ + { + "id" : "9989C302-1D94-43E1-BC80-01AAA064C73B", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + + }, + "testTargets" : [ + { + "target" : { + "containerPath" : "container:Client.xcodeproj", + "identifier" : "2CFE996E2D45329300B25CE0", + "name" : "EcosiaTests" + } + } + ], + "version" : 1 +} diff --git a/firefox-ios/EcosiaTests/VersionTests.swift b/firefox-ios/EcosiaTests/VersionTests.swift new file mode 100644 index 0000000000000..3afcf5794a627 --- /dev/null +++ b/firefox-ios/EcosiaTests/VersionTests.swift @@ -0,0 +1,137 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client +@testable import Ecosia + +final class VersionTests: XCTestCase { + + private static let appVersionUpdateTestKey = "appVersionUpdateTestKey" + + override func setUp() async throws { + UserDefaults.standard.removeObject(forKey: Self.appVersionUpdateTestKey) + } + + // Test initialization + func testInitialization() { + XCTAssertNotNil(Version("1.0.0")) + XCTAssertNil(Version("1.0")) // Not a semantic version + XCTAssertNil(Version("abc")) // Invalid format + } + + // Test description output + func testDescription() { + let version = Version("1.2.3") + XCTAssertEqual(version?.description, "1.2.3") + } + + // Test equality + func testEquality() { + XCTAssertEqual(Version("1.0.0"), Version("1.0.0")) + XCTAssertNotEqual(Version("1.0.0"), Version("1.0.1")) + } + + // Test less-than comparison + func testComparison() { + XCTAssertTrue(Version("1.0.0")! < Version("1.0.1")!) + XCTAssertTrue(Version("1.0.0")! < Version("1.1.0")!) + XCTAssertTrue(Version("1.0.0")! < Version("2.0.0")!) + } + + // Test Hashability + func testHash() { + XCTAssertEqual(Version("1.0.0")?.hashValue, Version("1.0.0")?.hashValue) + XCTAssertNotEqual(Version("1.0.0")?.hashValue, Version("1.0.1")?.hashValue) + } + + // Test Version retrieval and saving using UserDefaults + func testVersionStorage() { + let version1 = Version("1.0.0")! + let version2 = Version("1.0.1")! + + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, + provider: MockAppVersionInfoProvider(mockedAppVersion: version1.description)) + + XCTAssertEqual(Version.saved(forKey: Self.appVersionUpdateTestKey)?.description, version1.description) + + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, provider: MockAppVersionInfoProvider(mockedAppVersion: "1.0.1")) + + XCTAssertEqual(Version.saved(forKey: Self.appVersionUpdateTestKey)?.description, version2.description) + } + + func testDoubleDigitVersions() { + let version1 = Version("10.9.8")! + let version2 = Version("10.10.8")! + let version3 = Version("11.9.8")! + let version4 = Version("11.11.11")! + + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, provider: MockAppVersionInfoProvider(mockedAppVersion: version1.description)) + XCTAssertEqual(Version.saved(forKey: Self.appVersionUpdateTestKey)?.description, version1.description) + + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, provider: MockAppVersionInfoProvider(mockedAppVersion: version2.description)) + XCTAssertEqual(Version.saved(forKey: Self.appVersionUpdateTestKey)?.description, version2.description) + + XCTAssertTrue(version2 < version3) + XCTAssertTrue(version3 < version4) + XCTAssertFalse(version3 == version4) + } +} + +// Integration tests for Version + WhatsNewLocalDataProvider +extension VersionTests { + + func testUpgradeFromVersionWithoutLogic() { + // Simulate that the app was previously on a version that didn't have the Version struct logic. + // We can represent this by not having any version saved in UserDefaults. + + XCTAssertNil(Version.saved(forKey: Self.appVersionUpdateTestKey), "There should be no version saved initially.") + + // Simulate the app being updated to the current version which has the Version struct logic. + let currentVersion = "9.0.0" // Assuming 9.0.0 is the version with the new logic. + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, provider: MockAppVersionInfoProvider(mockedAppVersion: currentVersion)) + + // Check if the version is now saved correctly. + XCTAssertEqual(Version.saved(forKey: Self.appVersionUpdateTestKey)?.description, currentVersion, "The version should be updated to \(currentVersion) after the upgrade.") + } + + func testFakeUpdateToSameVersionAgainstLocalDataProviderItemsData() { + + // Setup + let version = Version("8.3.0")! + let appVersionInfoProvider = MockAppVersionInfoProvider(mockedAppVersion: version.description) + let dataProvider = WhatsNewLocalDataProvider(versionProvider: appVersionInfoProvider) + + // Given: An initial version of 8.3.0 and a "toVersion" of 8.3.0 + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, + provider: appVersionInfoProvider) + + // When: We retrieve the What's New items after this "fake" update + let items = try? dataProvider.getWhatsNewItemsInRange() + + // Then: We should not have items for versions beyond 8.3.0 (like 9.0.0) + XCTAssertTrue(items?.isEmpty == true, "WhatsNewItem list should be empty for fake update to same version") + } + + func testFakeUpdateToMinorVersionAgainstLocalDataProviderItemsData() { + + // Setup + let fromVersion = Version("8.3.0")! + let toVersion = Version("8.3.1")! + let appVersionInfoProvider = MockAppVersionInfoProvider(mockedAppVersion: toVersion.description) + let dataProvider = WhatsNewLocalDataProvider(versionProvider: appVersionInfoProvider) + + // Given: An initial version of 8.3.0 and a "toVersion" of 8.3.1 + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, provider: MockAppVersionInfoProvider(mockedAppVersion: fromVersion.description)) + + // When: We perform a fake update to 8.3.1 + Version.updateFromCurrent(forKey: Self.appVersionUpdateTestKey, provider: appVersionInfoProvider) + + // And: We retrieve the What's New items after this update + let items = try? dataProvider.getWhatsNewItemsInRange() + + // Then: We should not have items for versions beyond 8.3.1 (like 9.0.0) + XCTAssertTrue(items?.isEmpty == true, "WhatsNewItem list should be empty for an update to minor version when there are items for upper versions") + } +} diff --git a/firefox-ios/EcosiaTests/WhatsNewLocalDataProviderTests.swift b/firefox-ios/EcosiaTests/WhatsNewLocalDataProviderTests.swift new file mode 100644 index 0000000000000..27ac7ccf1740c --- /dev/null +++ b/firefox-ios/EcosiaTests/WhatsNewLocalDataProviderTests.swift @@ -0,0 +1,173 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client +@testable import Ecosia + +// This tests are dependant on WhatsNewLocalDataProvider.whatsNewItems hardcoded implementation +final class WhatsNewLocalDataProviderTests: XCTestCase { + + override func setUpWithError() throws { + User.shared.whatsNewItemsVersionsShown = [] + UserDefaults.standard.removeObject(forKey: EcosiaInstallType.installTypeKey) + UserDefaults.standard.removeObject(forKey: EcosiaInstallType.currentInstalledVersionKey) + } + + // MARK: Fresh Install Tests + func testFreshInstallShouldNotShowWhatsNewAndMarkPreviousVersionsAsSeen() { + // Given + EcosiaInstallType.set(type: .fresh) + let items: [Version: [WhatsNewItem]] = [Version("9.0.0")!: [.emptyItem()]] + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "9.0.2"), + whatsNewItems: items) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(shouldShowWhatsNew, "Fresh install should not show what's new") + XCTAssertEqual(User.shared.whatsNewItemsVersionsShown, ["9.0.0"]) + } + + // MARK: Unknown Install Tests + func testUnkownInstallShouldNotShowWhatsNewAndMarkPreviousVersionsAsSeen() { + // Given + EcosiaInstallType.set(type: .unknown) + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "1.0.0")) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(shouldShowWhatsNew, "Unknown install should not show what's new") + XCTAssertEqual(User.shared.whatsNewItemsVersionsShown, [], "No previous versions shoul be marked since 1.0.0 < 9.0.0") + } + + // MARK: Upgrade Install Tests + func testUpgradeToVersionWithItemsShouldShowWhatsNew() { + // Given + EcosiaInstallType.updateCurrentVersion(version: "8.0.0") + EcosiaInstallType.set(type: .upgrade) + let items: [Version: [WhatsNewItem]] = [Version("8.0.0")!: [.emptyItem()], + Version("9.0.0")!: [.emptyItem()], + Version("9.0.1")!: [.emptyItem()], + Version("9.0.2")!: [.emptyItem()] + ] + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "9.0.0"), whatsNewItems: items) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertTrue(dataProvider.getVersionRange() == [Version("9.0.0")!], "The version 9.0.0 should be the only the only version returned, got \(dataProvider.getVersionRange()) instead") + XCTAssertTrue(shouldShowWhatsNew, "Upgrade to a version with items should show whats new") + } + + func testUpgradeToVersionWithoutItemsShouldNotShowWhatsNew() { + // Given + EcosiaInstallType.updateCurrentVersion(version: "8.2.1") + EcosiaInstallType.set(type: .upgrade) + let items: [Version: [WhatsNewItem]] = [Version("8.2.0")!: [.emptyItem()], + Version("8.2.6")!: [.emptyItem()] + ] + let dataProvider = WhatsNewLocalDataProvider(versionProvider: + MockAppVersionInfoProvider(mockedAppVersion: "8.2.5"), + whatsNewItems: items) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(shouldShowWhatsNew, "Upgrade to a version without items should not show whats new") + } + + func testDowngradeShouldNotShowWhatsNew() { + // Given + User.shared.whatsNewItemsVersionsShown = ["9.0.0"] + EcosiaInstallType.set(type: .upgrade) + EcosiaInstallType.updateCurrentVersion(version: "9.3.0") + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "9.0.0")) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(shouldShowWhatsNew, "Downgrade should not show what's new") + } + + func testUpgradeToGreaterVersionThanAnyInBetweenWithItemsShouldShowWhatsNew() { + // Given + EcosiaInstallType.set(type: .upgrade) + EcosiaInstallType.updateCurrentVersion(version: "8.0.0") + let items: [Version: [WhatsNewItem]] = [Version("8.0.0")!: [.emptyItem()], + Version("9.0.0")!: [.emptyItem()], + Version("9.0.1")!: [.emptyItem()], + Version("9.0.2")!: [.emptyItem()] + ] + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "9.0.2"), whatsNewItems: items) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(dataProvider.getVersionRange().contains(Version("8.0.0")!), "The version 8.0.0 should not be among the picked version range, got \(dataProvider.getVersionRange()) instead") + XCTAssertTrue(dataProvider.getVersionRange() == [Version("9.0.0")!, Version("9.0.1")!, Version("9.0.2")!], "The versions 9.0.0 and 9.0.1 should be among the picked version range. Resulting version range \(dataProvider.getVersionRange()).") + XCTAssertTrue(shouldShowWhatsNew, "Upgrade to greater version than the one with items should show what's new") + } + + func testUpgradeWithAlreadyShownItemsShouldNotShow() { + // Given + User.shared.whatsNewItemsVersionsShown = ["9.0.0"] + EcosiaInstallType.set(type: .upgrade) + EcosiaInstallType.updateCurrentVersion(version: "8.0.0") + let items: [Version: [WhatsNewItem]] = [ + Version("8.2.0")!: [.emptyItem()], + Version("9.0.0")!: [.emptyItem()] + ] + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "9.0.2")) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(dataProvider.getVersionRange() == [Version("8.2.0")!], "The version 8.2.0 should not be among the picked version range. Resulting version range: \(dataProvider.getVersionRange())") + XCTAssertFalse(shouldShowWhatsNew, "Upgrade with already shown items should show not show what's new") + } + + func testUpgradeToVersionWithoutItemsLike950ShouldNotShowWhatsNew() { + // Given + EcosiaInstallType.updateCurrentVersion(version: "9.4.0") + EcosiaInstallType.set(type: .upgrade) + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "9.5.0")) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertFalse(shouldShowWhatsNew, "Upgrade to a version without items should not show whats new") + } + + func testUpgradeToVersionWithItemsLike10ShouldShowWhatsNew() { + // Given + EcosiaInstallType.updateCurrentVersion(version: "9.4.0") + EcosiaInstallType.set(type: .upgrade) + let dataProvider = WhatsNewLocalDataProvider(versionProvider: MockAppVersionInfoProvider(mockedAppVersion: "10.0.0")) + + // When + let shouldShowWhatsNew = dataProvider.shouldShowWhatsNewPage + + // Then + XCTAssertTrue(shouldShowWhatsNew, "Upgrade to a version without items should show whats new") + } +} + +extension WhatsNewItem { + + fileprivate static func emptyItem() -> WhatsNewItem { + WhatsNewItem(image: .init(), + title: .init(), + subtitle: .init()) + } +} diff --git a/firefox-ios/Extensions/NotificationService/Info.plist b/firefox-ios/Extensions/NotificationService/Info.plist index c4a8650d0f9cb..a8262f19c13d7 100644 --- a/firefox-ios/Extensions/NotificationService/Info.plist +++ b/firefox-ios/Extensions/NotificationService/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 133.0 + $(MARKETING_VERSION) CFBundleVersion 1 MozDevelopmentTeam diff --git a/firefox-ios/Extensions/ShareTo/Info.plist b/firefox-ios/Extensions/ShareTo/Info.plist index 98587c9b693d8..f77e11700dd67 100644 --- a/firefox-ios/Extensions/ShareTo/Info.plist +++ b/firefox-ios/Extensions/ShareTo/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 133.0 + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleVersion diff --git a/firefox-ios/Extensions/ShareTo/SendToDevice.swift b/firefox-ios/Extensions/ShareTo/SendToDevice.swift index 1244dd2d0b017..c830b5e198b21 100644 --- a/firefox-ios/Extensions/ShareTo/SendToDevice.swift +++ b/firefox-ios/Extensions/ShareTo/SendToDevice.swift @@ -12,7 +12,10 @@ class SendToDevice: DevicePickerViewControllerDelegate, InstructionsViewDelegate var sharedItem: ShareItem? weak var delegate: ShareControllerDelegate? + /* Ecosia: Swap Theme Manager with Ecosia's private let themeManager = DefaultThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + */ + private let themeManager = EcosiaThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) private var profile: Profile { let profile = BrowserProfile(localName: "profile") diff --git a/firefox-ios/Extensions/ShareTo/ShareViewController.swift b/firefox-ios/Extensions/ShareTo/ShareViewController.swift index 20e6d8f3ffb81..6d5eb031e58b4 100644 --- a/firefox-ios/Extensions/ShareTo/ShareViewController.swift +++ b/firefox-ios/Extensions/ShareTo/ShareViewController.swift @@ -7,6 +7,7 @@ import Shared import Storage import Account import Common +import Ecosia import class MozillaAppServices.Viaduct import enum MozillaAppServices.BookmarkRoots @@ -78,7 +79,10 @@ class ShareViewController: UIViewController { private var actionRowHeights = [NSLayoutConstraint]() private var pageInfoRowTitleLabel: UILabel? private var pageInfoRowUrlLabel: UILabel? - private let themeManager = DefaultThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + /* Ecosia: Swap Theme Manager with Ecosia's + private let themeManager: ThemeManager = DefaultThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) + */ + private let themeManager = EcosiaThemeManager(sharedContainerIdentifier: AppInfo.sharedContainerIdentifier) weak var delegate: ShareControllerDelegate? @@ -147,21 +151,30 @@ class ShareViewController: UIViewController { makeActionRow( addTo: stackView, label: .ShareOpenInFirefox, + /* Ecosia: Update image iname imageName: StandardImageIdentifiers.Large.logoFirefox, + */ + imageName: "atlas", action: #selector(actionOpenInFirefoxNow), hasNavigation: false ) makeActionRow( addTo: stackView, label: .ShareLoadInBackground, - imageName: StandardImageIdentifiers.Large.tabTray, + /* Ecosia: Update image iname + imageName: StandardImageIdentifiers.Large.tabTray, + */ + imageName: "load", action: #selector(actionLoadInBackground), hasNavigation: false ) makeActionRow( addTo: stackView, label: .ShareBookmarkThisPage, + /* Ecosia: Update image iname imageName: StandardImageIdentifiers.Large.bookmark, + */ + imageName: "bookmarkAdd", action: #selector(actionBookmarkThisPage), hasNavigation: false ) @@ -172,6 +185,7 @@ class ShareViewController: UIViewController { action: #selector(actionAddToReadingList), hasNavigation: false ) + /* Ecosia: Remove Send To Device option as not offered makeSeparator(addTo: stackView) makeActionRow( addTo: stackView, @@ -180,6 +194,7 @@ class ShareViewController: UIViewController { action: #selector(actionSendToDevice), hasNavigation: true ) + */ } else { pageInfoRowUrlLabel?.removeFromSuperview() makeActionRow( @@ -302,7 +317,13 @@ class ShareViewController: UIViewController { heightConstraint.isActive = true actionRowHeights.append(heightConstraint) + /* Ecosia: Update UIImage gather let icon = UIImageView(image: UIImage(named: imageName)?.withRenderingMode(.alwaysTemplate)) + */ + var icon = UIImageView(image: UIImage(named: imageName)?.withRenderingMode(.alwaysTemplate)) + if let image = UIImage(named: imageName, in: .ecosia, with: nil)?.withRenderingMode(.alwaysTemplate) { + icon = UIImageView(image: image) + } icon.contentMode = .scaleAspectFit icon.tintColor = theme.colors.iconPrimary icon.translatesAutoresizingMaskIntoConstraints = false @@ -395,7 +416,12 @@ class ShareViewController: UIViewController { navigationController?.navigationBar.standardAppearance = appearance navigationController?.navigationBar.scrollEdgeAppearance = appearance navigationController?.navigationBar.isTranslucent = false + // Ecosia: Assign specific Tint Color + navigationController?.navigationBar.tintColor = theme.colors.actionPrimary + /* Ecosia: Update Title View with Ecosia logo navigationItem.titleView = UIImageView(image: UIImage(named: "Icon-Small")) + */ + navigationItem.titleView = UIImageView(image: .init(named: "iconLogo", in: .ecosia, with: nil)) navigationItem.titleView?.contentMode = .scaleAspectFit navigationItem.leftBarButtonItem = UIBarButtonItem( title: .SendToCancelButton, diff --git a/firefox-ios/Providers/TopSitesProvider.swift b/firefox-ios/Providers/TopSitesProvider.swift index 805a70c7bd3ba..d4616c1013705 100644 --- a/firefox-ios/Providers/TopSitesProvider.swift +++ b/firefox-ios/Providers/TopSitesProvider.swift @@ -126,15 +126,23 @@ private extension TopSitesProviderImplementation { return URL(string: site.url, invalidCharacters: false)?.normalizedHost ?? "" } + /* Ecosia: Merge default sites including path to keep blog.ecosia.org + blog.ecosia.org/financial-results */ + let unionOnURLAndPath = { (site: Site) -> String in + return URL(string: site.url)?.normalizedHostAndPath ?? "" + } + // Fetch the default sites let defaultSites = defaultTopSites(prefs) // Create PinnedSite objects. Used by the view layer to tell topsites apart let pinnedSites: [Site] = pinnedSites.map({ PinnedSite(site: $0, faviconResource: nil) }) // Merge default topsites with a user's topsites. - let mergedSites = sites.union(defaultSites, f: unionOnURL) + let mergedSites = pinnedSites.union(sites, f: unionOnURL) + /* Ecosia: Get different `allSites` // Filter out duplicates in merged sites, but do not remove duplicates within pinned sites let duplicateFreeList = pinnedSites.union(mergedSites, f: unionOnURL).filter { $0 as? PinnedSite == nil } let allSites = pinnedSites + duplicateFreeList + */ + let allSites = mergedSites.union(defaultSites, f: unionOnURLAndPath) // Favour topsites from defaultSites as they have better favicons. But keep PinnedSites let newSites = allSites.map { site -> Site in diff --git a/firefox-ios/README.md b/firefox-ios/README.md index c163c18d49ab4..1dd3942576bed 100644 --- a/firefox-ios/README.md +++ b/firefox-ios/README.md @@ -4,7 +4,7 @@ This is the subdirectory that contains the Firefox for iOS application. ## Main branch -Firefox for iOS works with [Xcode 15.4](https://developer.apple.com/download/all/?q=xcode), Swift 5.8 and supports iOS 15.0 and above. +Firefox for iOS works with [Xcode 16.1](https://developer.apple.com/download/all/?q=xcode), Swift 5.6 and supports iOS 15.0 and above. Please make sure you aim your pull requests in the right direction. diff --git a/firefox-ios/Shared/AppConstants.swift b/firefox-ios/Shared/AppConstants.swift index 75759896daf6d..c0c7541b03c6a 100644 --- a/firefox-ios/Shared/AppConstants.swift +++ b/firefox-ios/Shared/AppConstants.swift @@ -6,7 +6,9 @@ import Common import UIKit public enum AppName: String, CustomStringConvertible { - case shortName = "Firefox" + // Ecosia: Update shortName + // case shortName = "Firefox" + case shortName = "Ecosia" public var description: String { return self.rawValue diff --git a/firefox-ios/Shared/AppInfo.swift b/firefox-ios/Shared/AppInfo.swift index bcbfa393c909e..fdbef465d7a49 100644 --- a/firefox-ios/Shared/AppInfo.swift +++ b/firefox-ios/Shared/AppInfo.swift @@ -22,6 +22,7 @@ extension AppInfo { /// Return the keychain access group. public static func keychainAccessGroupWithPrefix(_ prefix: String) -> String { + /* Ecosia: Update group identifier var bundleIdentifier = baseBundleIdentifier if bundleIdentifier == "org.mozilla.ios.FennecEnterprise" { // Bug 1373726 - Base bundle identifier incorrectly generated for Nightly builds @@ -29,18 +30,17 @@ extension AppInfo { bundleIdentifier = "org.mozilla.ios.Fennec.enterprise" } return prefix + "." + bundleIdentifier + */ + ecosiaKeychainAccessGroupWithPrefix(prefix) } // Return the MozWhatsNewTopic key from the Info.plist public static var whatsNewTopic: String? { // By default we don't want to add dot version to what's new section. Set // this to true if you'd like to add dot version for whats new article. - let shouldAddDotVersion = false let appVersionSplit = AppInfo.appVersion.components(separatedBy: ".") let majorAppVersion = appVersionSplit[0] - var dotVersion = "" - if appVersionSplit.count > 1, appVersionSplit[0] != "0" { dotVersion = appVersionSplit[1] } - let topic = "whats-new-ios-\(majorAppVersion)\(shouldAddDotVersion ? dotVersion : "")" + let topic = "whats-new-ios-\(majorAppVersion)" return topic } @@ -54,11 +54,14 @@ extension AppInfo { }() // The App Store page identifier for the Firefox iOS application - public static var appStoreId = "id989804926" + // Ecosia: update App Store ID + // public static var appStoreId = "id989804926" + public static var appStoreId = "id670881887" /// Return the shared container identifier (also known as the app group) to be used with for example background /// http requests. It is the base bundle identifier with a "group." prefix. public static var sharedContainerIdentifier: String { + /* Ecosia: Update group identifier var bundleIdentifier = baseBundleIdentifier if bundleIdentifier == "org.mozilla.ios.FennecEnterprise" { // Bug 1373726 - Base bundle identifier incorrectly generated for Nightly builds @@ -66,5 +69,21 @@ extension AppInfo { bundleIdentifier = "org.mozilla.ios.Fennec.enterprise" } return "group." + bundleIdentifier + */ + ecosiaSharedContainerIdentifier + } +} + +// Ecosia: Add file specific info here to avoid issues with dependencies +extension AppInfo { + /// Return the shared container identifier (also known as the app group) to be used with for example background + /// http requests. It is the base bundle identifier with a "group." prefix. + public static var ecosiaSharedContainerIdentifier: String { + return "\("group.")\(baseBundleIdentifier)" + } + + /// Return the keychain access group. + public static func ecosiaKeychainAccessGroupWithPrefix(_ prefix: String) -> String { + return "\(prefix).\(baseBundleIdentifier)" } } diff --git a/firefox-ios/Shared/DateGroupedTableData.swift b/firefox-ios/Shared/DateGroupedTableData.swift index f18eea7341e7e..bdaf35bf8f95e 100644 --- a/firefox-ios/Shared/DateGroupedTableData.swift +++ b/firefox-ios/Shared/DateGroupedTableData.swift @@ -12,84 +12,72 @@ private func getDate(dayOffset: Int) -> Date { } public struct DateGroupedTableData { - let todayTimestamp = getDate(dayOffset: 0).timeIntervalSince1970 - let yesterdayTimestamp = getDate(dayOffset: -1).timeIntervalSince1970 - let lastWeekTimestamp = getDate(dayOffset: -7).timeIntervalSince1970 - let lastMonthTimestamp = getDate(dayOffset: -30).timeIntervalSince1970 + // Timestamps at which we want the data to be split + var timestamps: [TimeInterval] = [] - var today: [(item: T, timing: TimeInterval)] = [] - var yesterday: [(item: T, timing: TimeInterval)] = [] - var lastWeek: [(item: T, timing: TimeInterval)] = [] - var lastMonth: [(item: T, timing: TimeInterval)] = [] - var older: [(item: T, timing: TimeInterval)] = [] + // Data associated with timestamps -- in each section + var timestampData: [[(item: T, timing: TimeInterval)]] = [] + + var timestampDataNumSections: Int { + return timestampData.count + } public var isEmpty: Bool { - return today.isEmpty && yesterday.isEmpty && lastWeek.isEmpty && lastMonth.isEmpty && older.isEmpty + return timestampData.allSatisfy { $0.isEmpty } } - public init() {} + public init(includeLastHour: Bool = false) { + var timestamps: [TimeInterval] = [] + if includeLastHour { + timestamps.append(Date().lastHour.timeIntervalSince1970) + } + timestamps.append(contentsOf: [ + getDate(dayOffset: 0).timeIntervalSince1970, + getDate(dayOffset: -1).timeIntervalSince1970, + getDate(dayOffset: -7).timeIntervalSince1970, + getDate(dayOffset: -30).timeIntervalSince1970]) + self.init(timestamps: timestamps) + } + + // Timestamps should be ordered chronolgically + public init(timestamps: [TimeInterval]) { + self.timestamps = timestamps + // Arrays maintaining data, split by timestamp. Additional array included for elements older than the last timestamp + timestampData = Array(repeating: [(item: T, timing: TimeInterval)](), count: timestamps.count + 1) + } @discardableResult public mutating func add(_ item: T, timestamp: TimeInterval) -> IndexPath { - if timestamp > todayTimestamp { - today.append((item, timestamp)) - return IndexPath(row: today.count - 1, section: 0) - } else if timestamp > yesterdayTimestamp { - yesterday.append((item, timestamp)) - return IndexPath(row: yesterday.count - 1, section: 1) - } else if timestamp > lastWeekTimestamp { - lastWeek.append((item, timestamp)) - return IndexPath(row: lastWeek.count - 1, section: 2) - } else if timestamp > lastMonthTimestamp { - lastMonth.append((item, timestamp)) - return IndexPath(row: lastMonth.count - 1, section: 3) - } else { - older.append((item, timestamp)) - return IndexPath(row: older.count - 1, section: 4) + for i in 0.. timestamps[i] { + timestampData[i].append((item, timestamp)) + return IndexPath(row: timestampData[i].count - 1, section: i) } + // if we don't match any of the timestamps above, return the older data + timestampData[timestampDataNumSections - 1].append((item, timestamp)) + return IndexPath(row: timestampData[timestampDataNumSections - 1].count - 1, section: timestampDataNumSections - 1) } public mutating func remove(_ item: T) { - if let index = today.firstIndex(where: { item == $0.item }) { - today.remove(at: index) - } else if let index = yesterday.firstIndex(where: { item == $0.item }) { - yesterday.remove(at: index) - } else if let index = lastWeek.firstIndex(where: { item == $0.item }) { - lastWeek.remove(at: index) - } else if let index = lastMonth.firstIndex(where: { item == $0.item }) { - lastMonth.remove(at: index) - } else if let index = older.firstIndex(where: { item == $0.item }) { - older.remove(at: index) + for i in 0.. Int { - switch section { - case 0: return today.count - case 1: return yesterday.count - case 2: return lastWeek.count - case 3: return lastMonth.count - case 4: return older.count - default: return 0 - } + guard section >= 0 && section < timestampDataNumSections else {return 0} + return timestampData[section].count } public func itemsForSection(_ section: Int) -> [T] { - switch section { - case 0: return today.map({ $0.item }) - case 1: return yesterday.map({ $0.item }) - case 2: return lastWeek.map({ $0.item }) - case 3: return lastMonth.map({ $0.item }) - case 4: return older.map({ $0.item }) - default: return [] - } + guard section >= 0 && section < timestampDataNumSections else {return []} + return timestampData[section].map({ $0.item }) } /// Returns all currently fetched items in a single array: `[T.item]`. public func allItems() -> [T] { - let allItems = (today + yesterday + lastWeek + lastMonth + older) - .map { $0.item } - - return allItems + return timestampData.flatMap({ $0 }).map { $0.item } } } diff --git a/firefox-ios/Shared/Extensions/URLExtensions.swift b/firefox-ios/Shared/Extensions/URLExtensions.swift index e2df9b8750620..819503de1e8e2 100644 --- a/firefox-ios/Shared/Extensions/URLExtensions.swift +++ b/firefox-ios/Shared/Extensions/URLExtensions.swift @@ -196,9 +196,15 @@ extension URL { return urls[0] == urls[1] } + /* Ecosia: Create homepage url constant public var isFxHomeUrl: Bool { return absoluteString.hasPrefix("internal://local/about/home") } + */ + public static let homepageUrlString = "internal://local/about/home" + public var isFxHomeUrl: Bool { + return absoluteString.hasPrefix(URL.homepageUrlString) + } } // MARK: - Exported URL Schemes diff --git a/firefox-ios/Shared/Prefs.swift b/firefox-ios/Shared/Prefs.swift index 4005143443230..b0d5d2ec7e6d8 100644 --- a/firefox-ios/Shared/Prefs.swift +++ b/firefox-ios/Shared/Prefs.swift @@ -40,6 +40,9 @@ public struct PrefsKeys { public static let KeyAutofillCreditCardStatus = "KeyAutofillCreditCardStatus" public static let KeyAutofillAddressStatus = "KeyAutofillAddressStatus" + // Only set if we get an actual response, no assumptions, nil otherwise + public static let AppleConfirmedUserIsDefaultBrowser = "AppleConfirmedUserIsDefaultBrowser" + public struct Session { public static let FirstAppUse = "firstAppUse" public static let Last = "lastSession" @@ -191,6 +194,9 @@ public struct PrefsKeys { public static let splashScreenShownKey = "splashScreenShownKey" public static let PasswordGeneratorShown = "PasswordGeneratorShown" + + // Represents whether or not the user has seen the photon main menu once, at least. + public static let PhotonMainMenuShown = "PhotonMainMenuShown" } public protocol Prefs { diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ActivityStream.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ActivityStream.strings new file mode 100644 index 0000000000000..6b7814314976d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ActivityStream.strings @@ -0,0 +1,6 @@ +/* String used in the section title of the Bookmarks section on Home Screen. */ +"ActivityStream.Bookmarks.Title.v128" = "Адыргақәа"; + +/* Show all button text for Bookmarks items on the home page, which opens the Bookmarks panel when tapped. */ +"Bookmarks.Actions.More.v128" = "Зегьы аарԥштәуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/AddressToolbar.strings new file mode 100644 index 0000000000000..1d1fd49bd74d0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/AddressToolbar.strings @@ -0,0 +1,15 @@ +/* Accessibility label for the address field in the address toolbar. */ +"AddressToolbar.Location.A11y.Label.v128" = "Иҭажәгал шәзыҳәара ма шәҭыӡҭыԥ"; + +/* Placeholder for the address field in the address toolbar. */ +"AddressToolbar.Location.Placeholder.v128" = "Иҭажәгал шәзыҳәара ма шәҭыӡҭыԥ"; + +/* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ +"AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Амаӡаратәи ашәарҭадаратәи архиарақәа"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Иаанартуеит аԥшаага алхра"; + +/* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ +"AddressToolbar.SearchEngine.A11y.Label.v128" = "Аԥшаага: %@"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Alert.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Alert.strings new file mode 100644 index 0000000000000..d18d331f04088 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Alert.strings @@ -0,0 +1,12 @@ +/* Button text to dismiss the dialog box that gets presented as a confirmation to remove card and cancel the operation. */ +"CreditCard.SnackBar.CancelRemoveCardButton.v112" = "Аԥыхра"; + +/* Sub label for the dialog box that gets presented as a confirmation to ask user if they would like to remove the saved credit card from local as well as all their synced devices */ +"CreditCard.SnackBar.RemoveCardSublabel.v112" = "Уи акарта асинхронизациа зызу шәхыхәҭақәа зегьы рҟынтәи иаԥыххоит."; + +/* Title label for the dialog box that gets presented as a confirmation to ask user if they would like to remove the saved credit card */ +"CreditCard.SnackBar.RemoveCardTitle.v122" = "Акарта аԥыхтәума?"; + +/* Button text to dismiss the dialog box that gets presented as a confirmation to remove card and perform the operation of removing the credit card. */ +"CreditCard.SnackBar.RemovedCardButton.v112" = "Аныхра"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Alerts.strings new file mode 100644 index 0000000000000..d3c8d454ffced --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Alerts.strings @@ -0,0 +1,24 @@ +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the body text for the alert. */ +"Alerts.FeltDeletion.Body.v122" = "Иарктәуп хатә ҭаӡҩырақәа зегьы, насгьы иԥыхтәуп аҭоурых, акукиқәа, егьырҭ асаит адыррақәа зегьы."; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the cancel action for the alert, cancelling ending your session. */ +"Alerts.FeltDeletion.Button.Cancel.v122" = "Аԥыхра"; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the affirmative action for the alert, confirming that you do want to do that. */ +"Alerts.FeltDeletion.Button.Confirm.v122" = "Асесиа адыррақәа аԥыхтәуп"; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the title for the alert. */ +"Alerts.FeltDeletion.Title.v122" = "Ахатә сеанс алгара?"; + +/* The title for the negative action of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed, and will reject the action of restoring tabs. */ +"Alerts.RestoreTabs.Button.No.v109" = "Мамоу"; + +/* The title for the affirmative action of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed, and will restore existing tabs. */ +"Alerts.RestoreTabs.Button.Yes.v109" = "Агәылаҵақәа реиҭашьақәыргылара"; + +/* The body of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. */ +"Alerts.RestoreTabs.Message.v109" = "Ҳара уи ҳахьхәуеит. Уахьынҵәаз уалагарц азы уҭаӡҩырақәа еиҭашьақәыргылатәуп."; + +/* The title of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. The placeholder will be the Firefox name. */ +"Alerts.RestoreTabs.Title.v109.v2" = "%@ икаҳаит. Аҭаӡҩырақәа реиҭашьақәыргылара?"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/BiometricAuthentication.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/BiometricAuthentication.strings new file mode 100644 index 0000000000000..302ba15889771 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/BiometricAuthentication.strings @@ -0,0 +1,6 @@ +/* Biometric authentication is when the system prompts users for Face ID or fingerprint before accessing protected information. This string asks the user to enter their device passcode to access the protected screen. */ +"Biometry.Screen.UniversalAuthenticationReason.v115" = "Умаӡажәақәа рахь унеирц азы уҭал."; + +/* Biometric authentication is when the system prompts users for Face ID or fingerprint before accessing protected information. This string asks the user to enter their device passcode to access the protected screen for logins and encrypted cards. */ +"Biometry.Screen.UniversalAuthenticationReason.v122" = "Иҵәаху аԥсыӡажәақәеи ашәаратә методқәеи рахь анеиразы аутентификациа ҟаҵа."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/BookmarkPanel.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/BookmarkPanel.strings new file mode 100644 index 0000000000000..96bbd1bab2a11 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/BookmarkPanel.strings @@ -0,0 +1,3 @@ +/* Action button for deleting bookmarks in the bookmarks panel. */ +"Delete" = "Аныхра"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/BookmarkPanelDeleteConfirm.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/BookmarkPanelDeleteConfirm.strings new file mode 100644 index 0000000000000..3a03fcff5a48f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/BookmarkPanelDeleteConfirm.strings @@ -0,0 +1,12 @@ +/* Button label to cancel deletion when the user tried to delete a non-empty folder. */ +"Bookmarks.DeleteFolderWarning.CancelButton.Label" = "Аԥыхра"; + +/* Button label for the button that deletes a folder and all of its children. */ +"Bookmarks.DeleteFolderWarning.DeleteButton.Label" = "Аныхра"; + +/* Main body of the confirmation alert when the user tries to delete a folder that still contains bookmarks and/or folders. */ +"Bookmarks.DeleteFolderWarning.Description" = "Уара ииашаҵәҟьаны иуҭахума уи иаҵанакуа зегьы аныхра?"; + +/* Title of the confirmation alert when the user tries to delete a folder that still contains bookmarks and/or folders. */ +"Bookmarks.DeleteFolderWarning.Title" = "Ари апапка ҭацәӡам."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Bookmarks.strings new file mode 100644 index 0000000000000..9bff7a971aa56 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Bookmarks.strings @@ -0,0 +1,30 @@ +/* When navigating through the bookmarks menu and bookmark folders, a back button with an `All` (bookmarks) label is present to take the user to the top level bookmarks menu. */ +"Bookmarks.Menu.AllBookmarks.v131" = "Зегьы"; + +/* Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen */ +"Bookmarks.Menu.BookmarksTopLevelTitle.v131" = "Адыргақәа"; + +/* The title for the Delete Bookmark button, in the Edit Bookmark popup screen which is summoned from the main menu's Save submenu, which will delete the currently bookmarked site from the user's bookmarks. */ +"Bookmarks.Menu.DeleteBookmark.v132" = "Ианыхтәуп анҵамҭа"; + +/* Label of toast displayed after a bookmark is deleted in the Bookmarks menu. %@ is the name of the bookmark. */ +"Bookmarks.Menu.DeletedBookmark.v131" = "Ианыхуп \"%@\""; + +/* When a folder is longpressed in the bookmarks menu, a `Delete Folder` button is present. */ +"Bookmarks.Menu.DeleteFolder.v131" = "Аҭаӡ аныхтәуп"; + +/* When a bookmark is longpressed in the bookmarks menu, an `Edit Bookmark` button is present. */ +"Bookmarks.Menu.EditBookmark.v131" = "Аҭыӡдырга аредакциа азутәуп"; + +/* When editing a bookmark, you can select the folder that the bookmark will be saved in. The label for this section of the view is `Save in`. */ +"Bookmarks.Menu.EditBookmarkSaveIn.v131" = "Еиқәырхатәуп"; + +/* Label on the top of the `Edit Bookmarks` screen. */ +"Bookmarks.Menu.EditBookmarkTitle.v131" = "Аҭыӡдырга аредакциа азутәуп"; + +/* When a folder is longpressed in the bookmarks menu, an `Edit Folder` button is present. */ +"Bookmarks.Menu.EditFolder.v131" = "Аҭаӡ ԥсахтәуп"; + +/* The title for the Edit context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.Edit.v131" = "Аԥсахра"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/BottomSheet.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/BottomSheet.strings new file mode 100644 index 0000000000000..3e6976f7ba030 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/BottomSheet.strings @@ -0,0 +1,3 @@ +/* When a user is in the process of entering an address, a screen pops up prompting the user if they want to use a saved address. This string is used as the title label of the screen. */ +"Addresses.BottomSheet.UseSavedAddressBottomSheet.v124" = "Иеиқәырхоу адрес ахархәара?"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ContextualHints.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ContextualHints.strings new file mode 100644 index 0000000000000..e928809757919 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ContextualHints.strings @@ -0,0 +1,3 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears to educate users about what the fire button in the toolbar does, when in private mode. */ +"ContextualHints.FeltDeletion.Body.v122" = "Ахатә сеанс ҿыц алагаразы абра шәақәыӷәӷәа. Уҭоурых, акукиқәа, зегьы ԥыхтәуп."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/CredentialProvider.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/CredentialProvider.strings new file mode 100644 index 0000000000000..8dcef2b031ebd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/CredentialProvider.strings @@ -0,0 +1,12 @@ +/* Label shown when there are no logins to list. The placeholder will be replaced with the app name. */ +"LoginsList.NoLoginsFound.Description.v122" = "Аԥсыӡажәақәа %@ еиқәышәҵо ма еиқәышәҵо абра иаарԥшхоит. Иҵәахуа аԥсыӡажәақәа зегьы шифртәуп."; + +/* Label shown when there are no logins saved in the passwords list */ +"LoginsList.NoLoginsFound.Title.v122" = "Амаӡажәақәа еиқәырхаӡам"; + +/* Label displayed when a user searches for an item, and no matches can be found against the search query */ +"LoginsList.NoMatchingResult.Title.v122" = "Иԥшаам ажәамаӡақәа"; + +/* Placeholder text for search field in the credential provider list */ +"LoginsList.Search.Placeholder.v122" = "Амаӡажәақәа рыԥшаара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Credentials.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Credentials.strings new file mode 100644 index 0000000000000..01d68809cdbd4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Credentials.strings @@ -0,0 +1,6 @@ +/* Message shown when you enter Logins & Passwords without having a device passcode set. */ +"Logins.DevicePasscodeRequired.Message.v122" = "Аԥсыӡажәақәа рыхьчареи автоматла рыҭагалареи рзы, Face ID, Touch ID, ма шәхыхәҭа аԥсыӡажәа аашәырты."; + +/* Message shown when you enter Payment Methods without having a device passcode set. */ +"Logins.PaymentMethods.DevicePasscodeRequired.Message.v124.v2" = "Укредиттә карта аинформациа еиқәырханы автоматла иҭәырц азы, Face ID, Touch ID, ма ухы иаурхәо аԥсыӡкод аашәырты."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/CustomizeFirefoxHome.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/CustomizeFirefoxHome.strings new file mode 100644 index 0000000000000..71a601f1e81f3 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/CustomizeFirefoxHome.strings @@ -0,0 +1,9 @@ +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Bookmarks section on the Firefox homepage on or off */ +"Settings.Home.Option.Bookmarks.v128" = "Адыргақәа"; + +/* In the settings menu, in the Firefox homepage customization section, this is the subtitle for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off. The placeholder is the pocket app name. */ +"Settings.Home.Option.ThoughtProvokingStories.subtitle.v116" = "Астатиақәа %@ аҟынтә"; + +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off */ +"Settings.Home.Option.ThoughtProvokingStories.v116" = "Узырхәыцша ажәабжьқәа"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/DisplayCard.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/DisplayCard.strings new file mode 100644 index 0000000000000..df467c358132f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/DisplayCard.strings @@ -0,0 +1,3 @@ +/* Label for the expiry date of the credit card. */ +"CreditCard.DisplayCard.ExpiresLabel.v115" = "Аамҭа нҵәоит"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Edit Card.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Edit Card.strings new file mode 100644 index 0000000000000..33ea8cf32248a --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Edit Card.strings @@ -0,0 +1,3 @@ +/* Title label for the view where user can edit their credit card info */ +"CreditCard.EditCard.EditCreditCardTitle.v122" = "Ахсаала аредакциа азутәуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/EditAddress.strings new file mode 100644 index 0000000000000..536b2298fc293 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/EditAddress.strings @@ -0,0 +1,144 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Аԥыхра"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "Адрес асинхронизациа зызу шәхыхәҭақәа зегьы рҟынтәи ианыххоит."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Аныхра"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Ианыхтәуп адрес"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Адрес ацҵатәуп"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Аокруг"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Ақалақь"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Ақалақь"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Атәыла ма арегион"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Аокруг"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Аҟәша"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Аокруг"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "До/Си"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Ае-маил Апоштатә"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Аемираттә"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Адгьылбжьаха"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Ахьӡ"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Амикрораион"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Арегион"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Аиҿкаара"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Аара"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Аиндекс"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Аԥошьҭа акод"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Аԥошьҭа ақалақь"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Апрефектура"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Апровинциақәа"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Аштат"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Ақалақьцәа"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Аҭелтә"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Ақалақь дгьыл"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Ақыҭа ма ақалақь"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Аԥошьҭа акод"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Аԥыхра"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Адрес ԥсахтәуп"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Амҩаду, аҩны, аквартира"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Ахьчара"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Адрес абара"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Аркра"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Аԥсахра"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Ианыхтәуп адрес"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Адрес аныхуп"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Адрес еиқәырхоуп"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Адрес аиқәырхара алымшеит"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Адрес аԥыхра алымшеит"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Даҽазнык шәҽазышәшәа"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Адрес адыррақәа рҿыцуп"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v132.v2" = "Адрес еиқәырхоуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/EditCard.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/EditCard.strings new file mode 100644 index 0000000000000..d99ffea74e420 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/EditCard.strings @@ -0,0 +1,51 @@ +/* Title label for the view where user can add their credit card info */ +"CreditCard.EditCard.AddCreditCardTitle.v122" = "Ахсаала ацҵатәуп"; + +/* Button label for cancelling editing of the credit card details shown in the form */ +"CreditCard.EditCard.CancelNavBarButtonLabel.v113" = "Аԥыхра"; + +/* Title label for user to input their credit card Expiration date in the format MM / YY printed on their credit card in the text box below. */ +"CreditCard.EditCard.CardExpirationDateTitle.v112" = "Аус аура аамҭа ММ / ШШ"; + +/* Title label for user to input their credit card number printed on their credit card in the text box below. */ +"CreditCard.EditCard.CardNumberTitle.v112" = "Акарта аномер"; + +/* Button label for closing the view where user can view their credit card info */ +"CreditCard.EditCard.CloseNavBarButtonLabel.v113" = "Аркра"; + +/* Label for concealing contents of the credit card number */ +"CreditCard.EditCard.ConcealLabel.v114" = "Аҵәахра"; + +/* Label for copying contents of the form */ +"CreditCard.EditCard.CopyLabel.v113" = "Ақәҭыхра"; + +/* Button label for editing the credit card details shown in the form */ +"CreditCard.EditCard.EditNavBarButtonLabel.v113" = "Аԥсахра"; + +/* Label for credit card expiration date. The %@ will be replaced by the actual date and thus doesn't need translation. */ +"CreditCard.EditCard.ExpiredDateTitle.v112" = "Аамҭа анҵәара арыцхә %@"; + +/* Title label for user to input their name printed on their credit card in the text box below. */ +"CreditCard.EditCard.NameOnCardTitle.v112" = "Аныҟәгаҩ ихьӡ"; + +/* Button title which, when tapped, will allow the user to save valid credit card details. */ +"CreditCard.EditCard.NavButtonSaveTitle.v112" = "Ахьчара"; + +/* Title label for button that allows user to remove their saved credit card. */ +"CreditCard.EditCard.RemoveCardButtonTitle.v112" = "Акарта аныхтәуп"; + +/* Label for revealing the contents of the credit card number */ +"CreditCard.EditCard.RevealLabel.v114" = "Арбара"; + +/* Title label for user to pick a credit card from the list below to be updated. */ +"CreditCard.EditCard.SavedCardListTitle.v112" = "ИХЫРХЫЗ АХАРТАҚӘА"; + +/* Button label for saving the credit card details user entered in the form */ +"CreditCard.EditCard.SaveNavBarButtonLabel.v113" = "Ахьчара"; + +/* Title label for user to use the toggle settings to allow saving and autofilling of credit cards for webpages. */ +"CreditCard.EditCard.ToggleToAllowAutofillTitle.v122" = "Еиқәырхатәуп, насгьы ихарҭәаатәуп ашәаратә методқәа"; + +/* Title label for the view where user can view their credit card info */ +"CreditCard.EditCard.ViewCreditCardTitle.v116" = "Ахсаала абара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/EngagementNotification.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/EngagementNotification.strings new file mode 100644 index 0000000000000..d87557278fb23 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/EngagementNotification.strings @@ -0,0 +1,18 @@ +/* Body of notification send to user after inactivity to encourage them to use the search feature. */ +"Engagement.Notification.Body.v112" = "Ааигәа акы ԥшаа. Мамзаргьы аинтерес зҵоу акы аԥшаара."; + +/* Title of notification send to user after inactivity to encourage them to use the search feature. */ +"Engagement.Notification.Title.v112" = "Раԥхьатәи аԥшаара алагара"; + +/* Body of notification send to user after inactivity to encourage them to use the private browsing feature. Placeholder is app name. */ +"Engagement.Notification.Treatment.A.Body.v114" = "Хаҭалатәи аԥшаара %@ аҟны удыррақәа еиқәнархаӡом, насгьы иҵәаху амҩаԥгаҩцәа ааннакылоит."; + +/* Title of notification send to user after inactivity to encourage them to use the private browsing feature. */ +"Engagement.Notification.Treatment.A.Title.v114" = "Ашьҭа нмыжькәа ауеб аԥшаара"; + +/* Body of notification send to user after inactivity to encourage them to use the private browsing feature. Placeholder is the app name. */ +"Engagement.Notification.Treatment.B.Body.v114" = "Аԥшаара еиқәырхоу акукиқәеи аҭоурыхи рыда %@ аҟны."; + +/* Title of notification send to user after inactivity to encourage them to use the private browsing feature. */ +"Engagement.Notification.Treatment.B.Title.v114" = "Иԥышәшәа хатә ԥшаара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/EnhancedTrackingProtection.strings new file mode 100644 index 0000000000000..5dcd68a6e8063 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/EnhancedTrackingProtection.strings @@ -0,0 +1,117 @@ +/* The title for the certificate common name inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.CommonName.v131" = "Азеиԥш хьӡы"; + +/* The title for the certificate issuer country inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.IssuerCountry.v131" = "Ақалақь"; + +/* The title for the certificate issuer name section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.IssuerName.v131" = "Ажәабжьҳәаҩ"; + +/* The title for the certificate issuer organization inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.IssuerOrganization.v131" = "Аиҿкаара"; + +/* The title for the certificate subject alt names section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.SubjectAltNames.v131" = "Асубиеқт Альтернативтә хьӡқәа"; + +/* The title for the certificate subject alt names DNS name inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.SubjectAltNamesDNSName.v131" = "DNS ахьӡ"; + +/* The title for the certificate subject name section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.SubjectName.v131" = "Амаҭәар ахьӡ"; + +/* The title for the certificates screen inside the certificates screen. */ +"Menu.EnhancedTrackingProtection.Certificates.Title.v131" = "Асертификат"; + +/* The title for the certificate validity section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.Validity.v131" = "Алабҿабара"; + +/* The title for the certificate validity not after date inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.ValidityNotAfter.v131" = "Аус ауеит"; + +/* The title for the certificate validity not before date inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.ValidityNotBefore.v131" = "Аус ауеит"; + +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertCancelButton.v128" = "Аԥыхра"; + +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "Аилкаара"; + +/* The text for the clear cookies and site data alert inside the enhanced tracking protection screen. The placeholder will be replaced with the user's currently visited website */ +"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "%@ азы акукиқәеи асаит адыррақәеи раԥыхра иахҟьаны асаитқәа рҟынтәи уҭыҵыр алшоит, насгьы уаахәаратә кәырҷыжь ҭацәхар алшоит."; + +/* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "Акукиқәеи асаит адыррақәеи ԥыхтәуп"; + +/* The title for the clear cookies and site data button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.ButtonTitle.v128" = "Акукиқәеи асаит адыррақәеи ԥыхтәуп"; + +/* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ +"Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "Акукиқәеи асаит адыррақәеи ԥыхуп"; + +/* The accessibility label for the close button in the Enhanced Tracking protection menu. */ +"Menu.EnhancedTrackingProtection.CloseButton.AccessibilityLabel.v132" = "Амениу амаӡареи ашәарҭадареи арктәуп"; + +/* Text to let users know that the current website is secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "Ишәарҭам аимадара"; + +/* Text to let users know that the current website is not secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Ишәарҭам аимадара"; + +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ашьҭаԥшыгақәа ҳԥыхьамшәеит"; + +/* Text to let users know how many analytics trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Analytics.v132" = "Аҵакы ашьҭаҵара: %@"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Асаитқәа рыбжьара ашьҭаҵаратә кукиқәа: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Анацәашьҭақәа реизгаҩцәа: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Асоциалтә медиа ашьҭаҵаҩцәа: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ +"Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Ашьҭаҵаҩцәа аанкылоуп: %@"; + +/* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ +"Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "Ишьақәырӷәӷәоуп %@ ала"; + +/* The title for the button that allows users to view certificates inside the enhanced tracking protection details screen. */ +"Menu.EnhancedTrackingProtection.Details.ViewCertificatesTitle.v131" = "Асертификат абара"; + +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "%@ аус ауам. Ҳара иҳабжьаҳгоит ахьчара еиҭа аркра."; + +/* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ +"Menu.EnhancedTrackingProtection.Off.Title.v128" = "Уара ахьчара аҿыхуп"; + +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ +"Menu.EnhancedTrackingProtection.On.Header.v128" = "Уара ухьчоуп. Акы ҳгәаҳҭар, адырра шәаҳҭоит."; + +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Шәеимадара шәарҭам."; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Ари асаит аҿы шәгәышәҽаныз"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "%@ агәҽанҵараҿы"; + +/* The title for the privacy settings button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "Амаӡара аопциақәа"; + +/* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.Switch.Title.v128" = "Еиӷьтәу ашьҭаԥшра ахьчара"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Ахьчара Ҿыцуп. Ҳара иҳабжьаҳгоит уи еиҭа аркра."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Ахьчара Ҿыцуп. Ҳара иҳабжьаҳгоит уи даҽазнык иаркырц."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Ари асаит аҿы акы аус амуазар, уи аҿыхра шәҽазышәшәа."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ErrorState.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ErrorState.strings new file mode 100644 index 0000000000000..76b4084d6ce5f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ErrorState.strings @@ -0,0 +1,9 @@ +/* Sub label error string that gets shown when user enters incorrect input for their expiration date on their credit card in the text box. */ +"CreditCard.ErrorState.CardExpirationDateSublabel.v112" = "Иҭажәгал ииашоу аамҭа анҵәара арыцхә."; + +/* Sub label error string that gets shown when user enters incorrect input for their number printed on their credit card in the text box. */ +"CreditCard.ErrorState.CardNumberSublabel.v112" = "Иҭажәгал ииашоу акарта аномер"; + +/* Sub label error string that gets shown when user enters incorrect input for their name printed on their credit card in the text box. */ +"CreditCard.ErrorState.NameOnCardSublabel.v112" = "Ахьӡ ацҵатәуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxHomepage.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxHomepage.strings new file mode 100644 index 0000000000000..281928f24aac2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxHomepage.strings @@ -0,0 +1,12 @@ +/* When the user ends their private session, they are returned to the private mode homepage, and a toastbar popups confirming that their data has been erased. This is the label for that toast. */ +"FirefoxHomepage.FeltDeletion.Link.v122" = "Ахатә ԥшааратә дыррақәа аныхуп"; + +/* The body of the message for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. Placeholder refers to app name. */ +"FirefoxHomepage.FeltPrivacyUI.Body.v122" = "%@ иԥнахуеит укукиқәа, уҭоурых, усаит адыррақәа ухатә ҭаӡҩырақәа зегьы ануаркуа."; + +/* The link for the card that educates users about how private mode works. The link redirects to an external site for more information. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Link.v122" = "Изылшода сусура?"; + +/* The title for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Title.v122" = "Ари ахархәагаҿы ашьҭа аанумыжьлан"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxLogins.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxLogins.strings new file mode 100644 index 0000000000000..404e38fe42637 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxLogins.strings @@ -0,0 +1,18 @@ +/* Prompt for saving the username in the Save Logins prompt. */ +"LoginsHelper.PromptSaveLogin.Title.v122" = "Ахархәаҩ ихьӡ еиқәырхатәума?"; + +/* Prompt for saving a password in the Save Logins prompt. */ +"LoginsHelper.PromptSavePassword.Title.v122" = "Апароль еиқәырхатәума?"; + +/* Prompt for updating the password in the Update Password prompt. */ +"LoginsHelper.PromptUpdateLogin.Title.OneArg.v122" = "Амаӡажәа арҿыцтәума?"; + +/* Prompt for updating a password in the Update Password prompt. */ +"LoginsHelper.PromptUpdateLogin.Title.TwoArg.v122" = "Амаӡажәа арҿыцтәума?"; + +/* Placeholder text for search box in logins list view. */ +"LoginsList.LoginsListSearchPlaceholder.v122" = "Амаӡажәақәа рыԥшаара"; + +/* Title for the list of logins saved by the app */ +"LoginsList.Title.v122" = "ИХЫРХЫЗ АМАЖӘҚӘАҚӘА"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxSync.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxSync.strings new file mode 100644 index 0000000000000..f1ad4cf0ab148 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/FirefoxSync.strings @@ -0,0 +1,9 @@ +/* Toggle for address autofill syncing setting */ +"FirefoxSync.AddressAutofillEngine.v124" = "Адресқәа"; + +/* Toggle for credit cards syncing setting */ +"FirefoxSync.CreditCardsEngine.v122" = "Ашәара аметодқәа"; + +/* Toggle passwords syncing setting, in the Settings > Sync Data menu of the app. */ +"Sync.LoginsEngine.Title.v122" = "Амаӡажәақәа"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Footer.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Footer.strings new file mode 100644 index 0000000000000..c75a9d05b8dd8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Footer.strings @@ -0,0 +1,6 @@ +/* This is the learn more text of the Pocket footer on Firefox Homepage. */ +"FirefoxHomepage.Pocket.Footer.LearnMore.v115" = "Еиҳаны аинформациа"; + +/* This is the title of the Pocket footer on Firefox Homepage. The first placeholder is for the Pocket app name and the second placeholder for the app name */ +"FirefoxHomepage.Pocket.Footer.Title.v116" = "Амч %1$@ ала. %2$@ аҭаацәара ахәҭак."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/FxANotification.strings new file mode 100644 index 0000000000000..b6d4a0033f02b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Иарку %1$@ анҵамҭақәа: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Ааигәа иаркыз абларҭақәа рбара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/JumpBackIn.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/JumpBackIn.strings new file mode 100644 index 0000000000000..633846c1111f7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/JumpBackIn.strings @@ -0,0 +1,6 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one talks about additions to the Firefox homepage regarding a more personalized experience. */ +"ContextualHints.FirefoxHomepage.JumpBackIn.PersonalizedHome" = "Иаԥылатәуп еиқәыршәоу ахатә даҟьа. Аҵыхәтәантәи аҭаӡҩырақәа, анҵамҭақәа, аԥшаара алҵшәақәа абра иаарԥшхоит."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user is logged in and has a tab synced from desktop, this popup indicates which tab that is within the Jump Back In section. */ +"ContextualHints.FirefoxHomepage.JumpBackIn.SyncedTab.v106" = "Уара уҭаӡҩырақәа асинхронизациа ҟаҵоуп! Даҽа ҳәагак аҿы иахьаанужьыз иацҵа."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/KeyboardAccessory.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/KeyboardAccessory.strings new file mode 100644 index 0000000000000..8eb3ec0b4ede7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/KeyboardAccessory.strings @@ -0,0 +1,6 @@ +/* Accessibility label for next button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.NextButton.Accessibility.Label.v124" = "Анаҩстәи аформа аҭыԥ"; + +/* Accessibility label for previous button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.PreviousButton.Accessibility.Label.v124" = "Иаԥхьанеиуа аформа аҭакыра"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/LoginsHelper.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/LoginsHelper.strings new file mode 100644 index 0000000000000..41ab5c8c56fc8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/LoginsHelper.strings @@ -0,0 +1,9 @@ +/* Button to not save the user's password in the logins helper */ +"LoginsHelper.DontSave.Button.v122" = "Уажәазы ​​акәым"; + +/* Button to not update the user's password in the logins helper */ +"LoginsHelper.DontUpdate.Button.v122" = "Уажәазы ​​акәым"; + +/* Button to save the user's password */ +"LoginsHelper.SaveLogin.Button.v122" = "Ахьчара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/MainMenu.strings new file mode 100644 index 0000000000000..c322fe651b9cf --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/MainMenu.strings @@ -0,0 +1,282 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "Хаҭалатәи аԥшаара инаркны аусқәа рыхьчара аҟынӡа, иуҭаху еиҳа ирласны иԥшаа."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "Ҿыц: ирмариоу амениу"; + +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Шьҭахьҟа аара"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Аркра"; + +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "Аҭалара амаӡажәақәеи, абларҭақәеи, егьырҭгьы асинхронизациа азы"; + +/* On the main menu, at the top, when the user is signed out. The description for the sign in action */ +"MainMenu.Account.SignedOut.Description.v131" = "Апарольқәа, абларҭақәа уҳәа убас иҵегьы рсинхронизациа"; + +/* On the main menu, at the top, when the user is signed out. The title for the sign in action */ +"MainMenu.Account.SignedOut.Title.v131" = "Аҭалара"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state. */ +"MainMenu.Account.SyncError.Description.v131" = "Асинхронизациа аанкылоуп"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state. */ +"MainMenu.Account.SyncError.Title.v131" = "Асинхронизациазы даҽазнык аҭалара"; + +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Адыргақәа"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Аҭагаларақәа"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Ажурнал"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Амаӡажәақәа"; + +/* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Адыргақәа"; + +/* On the main menu, the title for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.Downloads.Title.v131" = "Аҭагаларақәа"; + +/* On the main menu, the title for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.History.Title.v131" = "Ахҭыс"; + +/* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.Passwords.Title.v131" = "Амаӡажәақәа"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "Ухатә даҟьа рхиала"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Ацхыраара аԥшаара"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Архиарақәа"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "%@ аҟны ҿыц"; + +/* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ +"MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Ухатә даҟьа рхиала"; + +/* On the main menu, the title for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.GetHelp.Title.v131" = "Ацхыраара аԥшаара"; + +/* On the main menu, the title for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.Settings.Title.v131" = "Архиарақәа"; + +/* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.WhatsNew.Title.v131" = "%@ аҟны ҿыц"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Аҩны"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "Ахатә екран ахь иацҵатәуп"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "Ақәҷаб"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "Аҿацәқәа рахь иацҵатәуп"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "Адырга ацҵатәуп"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Title.v132" = "Ари адаҟьа азгәаҭа"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "Аҭыӡдырга аредакциа азутәуп"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "Аԥхьара ахьӡынҵа аҟынтәи ианыхтәуп"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "Аҿацәқәа рҟынтә иаԥыхтәуп"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Subtitle.v132" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Title.v132" = "PDF еиԥш еиқәырхатәуп"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Subtitle.v132" = "Аԥхьара ахьӡынҵа"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Title.v132" = "Еиқәырхатәуп аԥхьара ахьӡынҵаҿы"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool. */ +"MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131" = "Аҩны"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to add a website to the home screen. */ +"MainMenu.Submenus.Save.AddToHomeScreen.Title.v131" = "Ахатә екран ахь иацҵатәуп"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AddToShortcuts.Subtitle.v131" = "Ақәҷаб"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AddToShortcuts.Title.v131" = "Аҿацәқәа рахь иацҵатәуп"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.BookmarkThisPage.Subtitle.v131" = "Адырга ацҵатәуп"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.BookmarkThisPage.Title.v131" = "Ари адаҟьа азгәаҭа"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.EditBookmark.Title.v131" = "Аҭыӡдырга аредакциа азутәуп"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.RemoveFromReadingList.Title.v131" = "Аԥхьара ахьӡынҵа аҟынтәи ианыхтәуп"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.RemoveFromShortcuts.Title.v131" = "Аҿацәқәа рҟынтә иаԥыхтәуп"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.SaveAsPDF.Subtitle.v131" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.SaveAsPDF.Title.v131" = "PDF еиԥш еиқәырхатәуп"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.SaveToReadingList.Subtitle.v131" = "Аԥхьара ахьӡынҵа"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.SaveToReadingList.Title.v131" = "Еиқәырхатәуп аԥхьара ахьӡынҵаҿы"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Off.Title.v132" = "Уахынлатәи арежим аҿыхтәуп"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.On.Title.v132" = "Уахынлатәи арежим аус арутәуп"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Subtitle.v132" = "Уахынлатәи арежим"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Subtitle.v132" = "Атиулен"; + +/* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "Атиулен"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "Аԥхьара аҿаԥшыра аҿыхтәуп"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "Аԥхьара аҿаԥшыра аус арутәуп"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "Аԥхьара аԥшра"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Ацҳара"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Иԥхасҭоу асаит азы адырраҭара"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Ашара"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Ашара"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Амҽхакрбага"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Амҽхакрбага (%@)"; + +/* On the main menu, the title for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Уахынлатәи арежим аҿыхтәуп"; + +/* On the main menu, the title for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.NightMode.On.Title.v131" = "Уахынлатәи арежим аус арутәуп"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.NightMode.Subtitle.v131" = "Уахынлатәи арежим"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.Print.Subtitle.v131" = "Атиулен"; + +/* On the main menu, the title for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.Print.Title.v131" = "Атиулен"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.Off.Title.v131" = "Аԥхьара аҿаԥшыра аҿыхтәуп"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.On.Title.v131" = "Аԥхьара аҿаԥшыра аус арутәуп"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.ReaderView.Subtitle.v131" = "Аԥхьара аԥшра"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131" = "Ацҳара"; + +/* On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131" = "Иԥхасҭоу асаит азы адырраҭара"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool. */ +"MainMenu.Submenus.Tools.Share.Subtitle.v131" = "Ашара"; + +/* On the main menu, the title for the action that will take the user to the Share module in the application. */ +"MainMenu.Submenus.Tools.Share.Title.v131" = "Ашара"; + +/* On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool. */ +"MainMenu.Submenus.Tools.Zoom.Subtitle.v131" = "Амҽхакрбага"; + +/* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.Zoom.Title.v131" = "Амҽхакрбага (%@)"; + +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Амениу хада"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Ахатә абӷьыц ҿыц"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "Абларҭа ҿыц"; + +/* On the main menu, the title for the action that will create a new private tab. */ +"MainMenu.TabsSection.NewPrivateTab.Title.v131" = "Ахатә абӷьыц ҿыц"; + +/* On the main menu, the title for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.NewTab.Title.v131" = "Абларҭа ҿыц"; + +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "Иԥшаатәуп адаҟьаҿы"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Ахьчара"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Акомпьютер аверсиахь ацара"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "Мобилтә саит ахь ацара"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Аинструментқәа"; + +/* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.FindInPage.Title.v131" = "Адаҟьаҿы иԥшаатәуп…"; + +/* On the main menu, the title for the action that will take the user to the Save submenu in the menu. */ +"MainMenu.ToolsSection.SaveSubmenu.Title.v131" = "Ахьчара"; + +/* On the main menu, the title for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.SwitchToDesktopSite.Title.v131" = "Акомпьютер аверсиахь ацара"; + +/* On the main menu, the title for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.SwitchToMobileSite.Title.v131" = "Мобилтә саит ахь ацара"; + +/* On the main menu, the title for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.ToolsSubmenu.Title.v131" = "Аинструментқәа"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Microsurvey.strings new file mode 100644 index 0000000000000..91ded1facd779 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Microsurvey.strings @@ -0,0 +1,72 @@ +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the button that appears on the prompt that allows the user to tap on and navigates them to the microsurvey to respond to. */ +"Microsurvey.Prompt.Button.v127" = "Ацҵара"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.AccessibilityLabel.v127" = "Аҭҵаара азыҳәара арктәуп"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "Аркра"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the logo image that appears on the prompt to inform the prompt is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Prompt.LogoImage.AccessibilityLabel.v129" = "Алоготип %@"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the text that appears on the prompt to inform the user that this is a prompt to take a survey. Placeholder is for the app name. */ +"Microsurvey.Prompt.TitleLabel.v127" = "Ҳацхраа %@ еиӷьаҳтәырц. Минуҭк ауп иаҭаххо."; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of button on the survey that a user can tap on to submit their responses. */ +"Microsurvey.Survey.Button.v127" = "Ашьҭра"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.AccessibilityLabel.v127" = "Иааигәаны аҭҵаара"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.Button.AccessibilityLabel.v127" = "Аркра"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the text shown on the confirmation page when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.ConfirmationLabel.v127" = "Иҭабуп шәгәаанагаразы!"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the title for the header on the microsurvey when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.HeaderLabel.v127" = "Аҭҵаара хыркәшоуп"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v127" = "Ари аҭҵаара амҩаԥгара"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Ҳаҳәоит, аҭҵаара мҩаԥыжәга"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "Алоготип %@"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Dissatisfied.v132" = "Агәынамӡара"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Neutral.v132" = "Инеитралтәу"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.NotApplicable.v132" = "Сара уи схы иасырхәом"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Satisfied.v132" = "Ицәгьам"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VeryDissatisfied.v132" = "Даара сгәы намӡо"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VerySatisfied.v132" = "Даара сгәы иахәеит"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ %2$@ аҟынтә"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ +"Microsurvey.Survey.PrivacyPolicyLink.v127" = "Амаӡаратә рдырраҭара"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "Иалху"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Иалкаау аныхуп"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Азҵаара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/NativeErrorPage.strings new file mode 100644 index 0000000000000..38878fd2a9de9 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Арҿыцра"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "SSL агха ҟалеит, насгьы асервер ахь ашәарҭадаратә еиқәыршәара ашьақәыргылара залымшеит."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Згәы зҽану. Акы ииашаны иҟам."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Даҽа ҳәагак ала аҽамадара шәҽазышәшәа. Умодем ма уроутер гәаҭа. Wi-Fi аҿыхны даҽазнык аҿакра."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Уинтернет аҿы апроблема ыҟоушәа ауп ишыҟоу."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Notification.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Notification.strings new file mode 100644 index 0000000000000..e32448ce396c0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Notification.strings @@ -0,0 +1,3 @@ +/* Fallback Title of notification if no notification title was configured. The notification is an advise to the user. The argument is the app name. */ +"Notification.Fallback.Title.v113" = "Абжьгара %@ аҟынтә"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Onboarding.strings new file mode 100644 index 0000000000000..2b9095c4ef747 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Onboarding.strings @@ -0,0 +1,153 @@ +/* The title of the button on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ +"DefaultBrowserPopup.ButtonTitle.v114" = "Аиқәыршәарақәа рахь ацара"; + +/* The footer label on the Default Browser Popup, which is below all the instructions asking the users if their Firefox browser is the default browser. If it is then close this message and tap skip. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.DescriptionFooter.v124" = "*%@ стандартла ишьақәыргылоума?* Ари адырраҭара аркны Ахыҵра ақәыӷәӷәара."; + +/* The first label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.FirstLabel.v114" = "1. Еиҭаҵ *Аиқәыршәарақәа*"; + +/* The second label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.SecondLabel.v114" = "2. *Абраузер апликациа стандарт* ақәыӷәӷәара"; + +/* The third label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. Placeholder is the app name. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.ThirdLabel.v114" = "3. Иалшәх *%@*"; + +/* The title on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ +"DefaultBrowserPopup.Title.v114" = "Иԥсахтәуп ишыҟоу еиԥш иҟоу абраузер"; + +/* String used to describe the option to continue to the next onboarding card in Firefox Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Continue.Action.v123" = "Акәша-мыкәша %@"; + +/* String used to describe the description label of the customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Intro.Description.v123" = "Утемеи амыругақәа рпанельи рхиала уԥшааратә стиль иақәшәартә еиԥш."; + +/* String used to describe the option to skip the customization cards in Firefox Onboarding screens and start browsing. */ +"Onboarding.Customization.Intro.Skip.Action.v123" = "Ахәаԥшра уалага"; + +/* String used to describe the title of the customization onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Title.v123" = "%@ анапхгара унапы ианнаҵоит"; + +/* String used to describe the option to save the user setting and continue to the next onboarding in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Continue.Action.v123" = "Еиқәырханы иацҵатәуп"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to dark theme from the available choices. */ +"Onboarding.Customization.Theme.Dark.Action.v123" = "Аиқәаҵәа"; + +/* String used to describe the description label of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Description.v123" = "Интернет иреиӷьу аган ала шәахәаԥш."; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to light theme from the available choices. */ +"Onboarding.Customization.Theme.Light.Action.v123" = "Алашара"; + +/* String used to describe the option to skip the theme customization in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Skip.Action.v123" = "Абжьажьра"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to system theme from the available choices. */ +"Onboarding.Customization.Theme.System.Action.v123" = "Асистема автоматика"; + +/* String used to describe the title of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Title.v123" = "Атема алхтәуп"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the bottom of the screen. */ +"Onboarding.Customization.Toolbar.Bottom.Action.v123" = "Ҵаҟантә"; + +/* String used to describe the option to save set preferences and leave onboarding to start browsing in the app. */ +"Onboarding.Customization.Toolbar.Continue.Action.v123" = "Еиқәырханы ахәаԥшра шәалага"; + +/* String used to describe the description label of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Description.v123" = "Уԥшаара унапы иану."; + +/* String used to describe the option to skip the toolbar customization in Firefox Onboarding screens and start browisg in the app. */ +"Onboarding.Customization.Toolbar.Skip.Action.v123" = "Абжьажьра"; + +/* String used to describe the title of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Title.v123" = "Иалшәх амыругақәа рпанель аҭыԥ"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the top of the screen. */ +"Onboarding.Customization.Toolbar.Top.Action.v123" = "Аханы"; + +/* String used to describes what Firefox is on the first onboarding page in our Onboarding screens. Indie means small independant. */ +"Onboarding.IntroDescriptionPart1.v114" = "Ихьыԥшым. Ахашәалахәы змам. Аус бзиақәа рзы."; + +/* String used to describes what Firefox is on the first onboarding page in our Onboarding screens. */ +"Onboarding.IntroDescriptionPart2.v114" = "Зегьы рзы интернет аиӷьтәра зызку."; + +/* Describes an action on some of the Onboarding screen, including the wallpaper onboarding screen. This string will be on a button so user can skip that onboarding page. */ +"Onboarding.LaterAction.v115" = "Абжьажьра"; + +/* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ +"Onboarding.Notification.Continue.Action.v114" = "Ацҵара"; + +/* String used to describe the description of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Description.v120" = "Шәхыхәҭақәа рыбжьара анҵамҭақәа шәарҭада еиҭагатәуп, насгьы %@ аҟны егьырҭ амаӡаратә ҟазшьақәа ԥшаатәуп."; + +/* String used to describe the option to skip the notification permission in Firefox Onboarding screens. */ +"Onboarding.Notification.Skip.Action.v115" = "Абжьажьра"; + +/* String used to describe the title of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Title.v120" = "Ардыррақәа шәыцхраауеит ашәарҭадараҿы %@"; + +/* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ +"Onboarding.Notification.TurnOnNotifications.Action.v114" = "Ардыррақәа аҿактәуп"; + +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v120" = "Уанҭалалак, насгьы асинхронизациа аныҟауҵалак, еиҳа ушәарҭадахоит. %@ ашәҟәы ҭанаҵоит уԥсыӡажәақәа, уҭыжьымҭақәа уҳәа убас иҵегьы."; + +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v123" = "%@ асинхронизациа аан уԥсыӡажәақәа, уҭыжьымҭақәа уҳәа убас иҵегьы еиқәнаршәоит."; + +/* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ +"Onboarding.Sync.SignIn.Action.v114" = "Аҭалара"; + +/* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ +"Onboarding.Sync.Skip.Action.v114" = "Абжьажьра"; + +/* String used to describes the title of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. */ +"Onboarding.Sync.Title.v120" = "Аиқәыршәагақәа рыбжьара уаннеиуа ашифр еиқәырхатәуп"; + +/* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ +"Onboarding.Wallpaper.Accessibility.Classic.v114" = "Аклассикатә ҭӡамц"; + +/* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ +"Onboarding.Wallpaper.Accessibility.LimitedEdition.v114" = "Аҭӡамц аҭыжьымҭа ԥку"; + +/* Description for the wallpaper onboarding page in our Onboarding screens. This describes to the user that they can set a wallpaper. */ +"Onboarding.Wallpaper.Action.v114" = "Аҭӡамц ашьақәыргылара"; + +/* Description for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that they can choose different wallpapers. */ +"Onboarding.Wallpaper.Description.v114" = "Иалышәх шәгәаԥхарала аҭӡамц."; + +/* Title for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that they can choose different wallpapers. */ +"Onboarding.Wallpaper.SelectorTitle.v114" = "Аԥштәы ақәҭәара шәҽазышәшәа"; + +/* Title for the wallpaper onboarding page in our Onboarding screens. This describes to the user that they can choose different wallpapers. Placeholder is for app name. */ +"Onboarding.Wallpaper.Title.v114" = "Иалхтәуп аҭӡамц %@"; + +/* Describes the action on the first onboarding page in our Onboarding screen. This string will be on a button so user can continue the onboarding. */ +"Onboarding.Welcome.Action.v114" = "Алагара"; + +/* Describes the action on the first onboarding page in our Onboarding screen. This indicates that the user will set their default browser to Firefox. */ +"Onboarding.Welcome.ActionTreatementA.v114" = "Абраузер ишыҟоу еиԥш ишьақәыргылатәуп"; + +/* Accessibility label for close button that dismisses the welcome onboarding screen. Placeholder is for the app name. */ +"Onboarding.Welcome.Close.AccessibilityLabel.v121" = "Иаркны иҭыҵтәуп %@ аҵара"; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Description.TreatementA.v120" = "Ахашәалахәы ззыԥшым аиҿкаара адгылара азызуа ҳбраузер акомпаниақәа онлаин шәышьҭаҵара аанкылара иацхраауеит."; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Welcome.Description.v120" = "Ахашәалахәы ззыԥшым аиҿкаара адгылара азызуа ҳбраузер акомпаниақәа онлаин шәышьҭаҵара аанкылара иацхраауеит."; + +/* String used to describe the title of link button is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Link.Action.v114" = "Еиҳаны еилышәкаа ҳмаӡаратә рдырраҭараҿы"; + +/* Describes the action on the first onboarding page in our Onboarding screen. This string will be on a button so user can skip this onboarding card. */ +"Onboarding.Welcome.Skip.v114" = "Абжьажьра"; + +/* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Title.TreatementA.v120" = "Ҳара бзиа иаҳбоит шәыхьчара"; + +/* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Title.v114" = "Бзиала шәаабеит ихьыԥшым аинтернет ахь"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/PasswordAutofill.strings new file mode 100644 index 0000000000000..0ffb7a8c84b68 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/PasswordAutofill.strings @@ -0,0 +1,15 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(ахархәаҩ ихьӡ ыҟам)"; + +/* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ +"PasswordAutofill.ManagePasswordsButton.v124" = "Амаӡажәа анапхгара"; + +/* This phrase is used as a subtitle in the header of password list screen, indicating to the user that they will be logging into a specific website (represented by %@) using a saved password. It's providing clarity on which website the saved credentials apply to. */ +"PasswordAutofill.SignInWithSavedPassword.v124" = "Уара иҭауҵоит %@"; + +/* This label is used in the password list screen header as a question, prompting the user if they want to use a saved password for logging in. */ +"PasswordAutofill.UseSavedPasswordFromHeader.v124" = "Иеиқәырхоу аԥсыӡажәа ахархәара?"; + +/* Displayed inside the keyboard hint when a user is entering their login credentials and has at least one saved password. Indicates that there are stored passwords available for use in filling out the login form. */ +"PasswordAutofill.UseSavedPasswordFromKeyboard.v124" = "Иеиқәырхоу амаӡажәа ахархәара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/PasswordGenerator.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/PasswordGenerator.strings new file mode 100644 index 0000000000000..860907f887da6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/PasswordGenerator.strings @@ -0,0 +1,27 @@ +/* Accessibility label describing a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.A11yLabel.v132" = "Амаӡажәа агенератор"; + +/* Accessibility label describing the close button for the popup related to a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.CloseButtonA11ylabel.v132" = "Аркра"; + +/* When a user is in the process of creating an account, they have the option to generate a password. The user is capable of copying this password after long pressing the value of the password displayed to them. This string is the label of the copy button that appears after long pressing the password. */ +"PasswordGenerator.CopyPasswordButtonLabel.v132" = "Ақәҭыхра"; + +/* Text displayed when a user interacts with the password field in a signup form, as part of a popup. This popup allows the user to generate a password that they have the option to use when signing up for an account. */ +"PasswordGenerator.Description.v132" = "Ухыԥхьаӡара хьчатәуп иӷәӷәоу, еиԥҟьаны иҟаҵоу аԥсыӡажәала."; + +/* When a user is in the process of creating an account, they have the option to generate a password. The popup displaying the generated password to the user is available by clicking a keyboard accessory button with this label. */ +"PasswordGenerator.KeyboardAccessoryButtonLabel.v132" = "Иӷәӷәоу амаӡажәа ахархәара"; + +/* Prefix to alert accessibility users that a generated password (represented by %@) will be read to them next. */ +"PasswordGenerator.PasswordReadoutPrefaceA11y.v132" = "Иаԥҵоу аԥсыӡажәа: %@"; + +/* Accessibility label describing a refresh password button belonging to a popup that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.RefreshPasswordButtonA11yLabel.v132" = "Иҿыцу иӷәӷәоу амаӡажәа аԥҵара"; + +/* Title text displayed as part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user -- clicking a button fills in the password of the signup form with this generated password. */ +"PasswordGenerator.Title.v132" = "Иӷәӷәоу аԥсыӡажәа ахархәара?"; + +/* Label of a button that is part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user and clicking this button fills in the password field of the signup form with this generated password. */ +"PasswordGenerator.UsePasswordButtonLabel.v132" = "Амаӡажәа ахархәара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/QRCode.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/QRCode.strings new file mode 100644 index 0000000000000..d6ac791af9826 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/QRCode.strings @@ -0,0 +1,3 @@ +/* Accessibility label of the QR code button in the toolbar */ +"QRCode.Toolbar.Button.A11y.Title.v128" = "Асканер QR акод"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/RememberCard.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/RememberCard.strings new file mode 100644 index 0000000000000..49709fb781d5e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/RememberCard.strings @@ -0,0 +1,15 @@ +/* This value is used as the header for the remember card page. The placeholder is for the app name. */ +"CreditCard.RememberCard.Header.v122" = "%@ укарта аномер еиқәнаршәоит. Ушәарҭадаратә код еиқәырхахаӡом."; + +/* This value is used as the title for the Yes button in the remember credit card page */ +"CreditCard.RememberCard.MainButtonTitle.v122" = "Ахьчара"; + +/* This value is used as the title for the remember credit card page */ +"CreditCard.RememberCard.MainTitle.v122" = "Ари акарта шьақәгылатәума?"; + +/* This value is used as the title for the Not Now button in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v115" = "Уажәазы ​​акәым"; + +/* This value is used as the toast message for the saving success alert in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v116" = "Ахсаала ҿыц еиқәырхоуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ResearchSurface.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ResearchSurface.strings new file mode 100644 index 0000000000000..0fa211b4ce00f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ResearchSurface.strings @@ -0,0 +1,9 @@ +/* On the Research Survey popup, the text that explains what the screen is about. Placeholder is for the app name. */ +"Body.Text.v112" = "Ҳаҳәоит, ҳҭҵаара кьаҿ ала %@ еиӷьтәырц шәҳацхраа."; + +/* On the Research Survey popup, the text for the button that, when tapped, will dismiss the popup and take the user to a survey. */ +"PrimaryButton.Label.v112" = "Аҭҵаара амҩаԥгара"; + +/* On the Research Survey popup, the text for the button that, when tapped, will dismiss this screen, and the user will not be taken to the survey. */ +"SecondaryButton.Label.v112" = "Мап иҭабуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ScanQRCode.strings new file mode 100644 index 0000000000000..a9327f280287c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Алшара аҭара"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Мап ацәкра"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Азин аартра %@?"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..94ce9a34b2af8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Аркра"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Иаанартуеит аԥшаара архиарақәа"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Аԥшаара архиарақәа"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Уажәазы ​​аԥшаара:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Аԥшаара архиарақәа"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Уажәазы ​​аԥшаара:"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/SearchHeaderTitle.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/SearchHeaderTitle.strings new file mode 100644 index 0000000000000..b51b9fb0f53a6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/SearchHeaderTitle.strings @@ -0,0 +1,6 @@ +/* When making a new search from the awesome bar, search results appear as the user write new letters in their search. Different sections with results from the selected search engine will appear. This string will be used as a header to separate the selected engine search results from current search query. */ +"Search.EngineSection.Title.v108" = "Аԥшаара %@ аҟны"; + +/* When making a new search from the awesome bar, search results appear as the user write new letters in their search. This string will be used as a header for Google search results listed as suggestions. */ +"Search.Google.Title.v108" = "Google Аԥшаара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/SelectCreditCard.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/SelectCreditCard.strings new file mode 100644 index 0000000000000..4e04c2509366f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/SelectCreditCard.strings @@ -0,0 +1,3 @@ +/* This value is used as the title for the select a credit card from list of available cards. */ +"CreditCard.SelectCreditCard.MainTitle.v122" = "Иеиқәырхоу ахсаала ахархәара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Settings.strings new file mode 100644 index 0000000000000..c4e93ccc67030 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Settings.strings @@ -0,0 +1,174 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Адресқәа рнапхгара"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Адрес %@ азы"; + +/* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ +"Addresses.Settings.SavedAddressesSectionTitle.v124" = "Иҵәаху АДРЕСҚӘА"; + +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Адресқәа %@ аҟны иҵәахтәуп"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Уи аинформациа шьақәыргылатәуп, ԥхьаҟа уи еиҳа имарианы иуԥшаарц азы."; + +/* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ +"Addresses.Settings.Switch.Description.v124" = "Иаҵанакуеит аҭел аномерқәеи ае-маил адресқәеи"; + +/* Title label for user to use the toggle settings to allow saving and autofilling of addresses for webpages. */ +"Addresses.Settings.Switch.Title.v124" = "Еиқәырхатәуп, адресқәа ҭашәырҭәаатәуп"; + +/* Displayed inside the keyboard hint when a user is entering their address and has at least one saved address. Indicates that there are stored addresses available for use in filling out a form. */ +"Addresses.Settings.UseSavedAddressFromKeyboard.v124" = "Иеиқәырхоу адрес ахархәара"; + +/* Accessibility label for the add button in autofill settings screen. Pressing this button presents a modal that allows users to add a card by entering the credit card information. */ +"CreditCard.Settings.AddCard.AccessibilityLabel.v121" = "Ахсаала ацҵатәуп"; + +/* Description label for when there are no credit cards shown in credit card list in autofill settings screen. */ +"CreditCard.Settings.EmptyListDescription.v112" = "Укарта аинформациа шьақәыргылатәуп, даҽазны еиҳа ирласны ашәара улшарц азы."; + +/* Title label for when there are no credit cards shown in credit card list in autofill settings screen. %@ is the product name and should not be altered. */ +"CreditCard.Settings.EmptyListTitle.v122" = "Акарточкақәа %@ ахь еиқәырхатәуп"; + +/* Accessibility label for a credit card list item in autofill settings screen. The first parameter is the credit card issuer (e.g. Visa). The second parameter is the name of the credit card holder. The third parameter is the last 4 digits of the credit card. The fourth parameter is the card's expiration date. */ +"CreditCard.Settings.ListItemA11y.v118" = "%1$@, аԥшәма %2$@, аномер анҵәамҭа %3$@, аамҭа анҵәамҭа %4$@"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string indicates to users that they can deny Firefox from remembering the card that is being used. */ +"CreditCard.Settings.NotNow.v122" = "Уажәазы ​​акәым"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string is a title string of the overall message that asks the user if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.RememberThisCard.v122" = "Ари акарта шьақәгылатәума?"; + +/* When a user is in the process or has finished making a purchase with a remembered card, and if the credit card information doesn't match the contents of the stored information of that card, we show this string. We ask this user if they would like Firefox update the staled information of that credit card. */ +"CreditCard.Settings.UpdateThisCard.v122" = "Ахсаала рҿыцтәума?"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string asks users to confirm if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.Yes.v122" = "Арҿыцра"; + +/* When a user is in the process of making a purchase and has at least one saved credit card, a view above the keyboard shows actions a user can take. When tapping this label, the keyboard will dismiss from view. */ +"CreditCards.Settings.Done.v114" = "Амазеи"; + +/* When a user is in the process or has finished making a purchase, and has at least one card saved, we show this tappable string. This indicates to users that they can navigate to their list of stored credit cards in the app's credit card list screen. */ +"CreditCards.Settings.ManageCards.v112" = "Акартқәа рнапхгара"; + +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label used as a title. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseASavedCard.v122" = "Иеиқәырхоу ахсаала ахархәара"; + +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label inside the keyboard hint. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseSavedCardFromKeyboard.v112" = "Иеиқәырхоу ахсаала ахархәара"; + +/* Settings section title for the old Firefox account */ +"FxA.FirefoxAccount.v119" = "Аԥхьаӡара"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v124" = "Адресқәа автохарҭәаара"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v126" = "Адресқәа"; + +/* Label used as an item in Settings screen. When touched, it will take user to credit card settings page to that will allows to add or modify saved credit cards to allow for autofill in a webpage. */ +"Settings.AutofillCreditCard.Title.v122" = "Ашәара аметодқәа"; + +/* Title displayed in header of the FxA settings panel. */ +"Settings.FxA.Title.v119" = "Аԥхьаӡара"; + +/* This is the description for the setting that toggles Sync related notifications in the settings menu under the Notifications section. */ +"Settings.Notifications.SyncNotificationsStatus.v112" = "Ари архиара аус арутәуп, даҽа ҳәагак аҿы уанҭалалак, абӷьыцқәеи ардыррақәеи раиуразы."; + +/* This is the title for the setting that toggles Sync related notifications in the settings menu under the Notifications section. */ +"Settings.Notifications.SyncNotificationsTitle.v112" = "Асинхронизациа"; + +/* This is the footer title informing the user needs to turn on notifications in iOS Settings. Both placeholders will be replaced with the app name. */ +"Settings.Notifications.SystemNotificationsDisabledMessage.v112" = "Уара %1$@ ардыррақәа зегьы уаҿыхит. Урҭ аус арутәуп Аиқәыршәарақәа алхны > \"Ардыррақәа\" > \"%2$@\""; + +/* This is the description for the setting that toggles Tips and Features feature in the settings menu under the Notifications section. The placeholder will be replaced with the app name. */ +"Settings.Notifications.TipsAndFeaturesNotificationsStatus.v112" = "Еилышәкаа ихәарҭоу аҟазшьақәеи %@ еиҳаны ахәарҭа шшәоуа."; + +/* This is the title for the setting that toggles Tips and Features feature in the settings menu under the Notifications section. */ +"Settings.Notifications.TipsAndFeaturesNotificationsTitle.v112" = "Ахшыҩҵакқәеи аҷыдарақәеи"; + +/* In the settings menu, in the Privacy section, this is the title for Notifications customization section. */ +"Settings.Notifications.Title.v112" = "Ардыргақәа"; + +/* This is the title informing the user needs to turn on notifications in iOS Settings. The placeholder will be replaced with the app name. */ +"Settings.Notifications.TurnOnNotificationsMessage.v112" = "Ардыррақәа %@ аҟны аус рурц азы шәхыхәҭа архиарақәа рахь шәца"; + +/* This is the title informing the user needs to turn on notifications in iOS Settings. */ +"Settings.Notifications.TurnOnNotificationsTitle.v112" = "Ардыррақәа аҿактәуп"; + +/* Accessibility label for default search engine setting. */ +"Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Аԥшаага машьына ишыҟоу еиԥш"; + +/* Accessibility label for Learn more about Firefox Suggest. */ +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Еиҳаны еилкаа Ecosia Suggest иазкны"; + +/* Title for alternate search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Альтернативтә ԥшаагақәа"; + +/* Footer for the `default search engine` settings section in the Search Settings page, which explains in more details what the `Show Search Suggestions` setting includes. */ +"Settings.Search.DefaultSearchEngine.Footer.v122" = "Аԥшаара алҵшәақәа, алаԥшхгара аҭоурых, анҵамҭақәа уҳәа убас иҵегьы"; + +/* Title for the `default search engine` settings section in the Search page in the Settings menu. */ +"Settings.Search.DefaultSearchEngine.Title.v121" = "Аԥшаага машьына ишыҟоу еиԥш"; + +/* Title for the `Suggestions from Search Engines` settings section in the Search page in the Settings menu. */ +"Settings.Search.EnginesSuggestions.Title.v124" = "Аԥшаагақәа рҟынтәи алабжьарақәа"; + +/* Navigation title for search page in the Settings menu. */ +"Settings.Search.PageTitle.v121" = "Аԥшаара"; + +/* Description for `Show in Private Sessions` toggle, located in `Suggestions from Search Engines` section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Description.v125" = "Ахатә сесиақәа рҿы аԥшаага машьынақәа рҟынтәи абжьгарақәа аарԥштәуп"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in the Private Session section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v122" = "Ахатә ԥшаараҿы аҳамҭақәа аарԥштәуп"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in `Suggestions from Search Engines` and `Address Bar - Firefox Suggest` sections in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v124" = "Ахатә ԥенџьырқәа рҿы иаарԥштәуп"; + +/* Title for the `Private Browsing` settings section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Title.v122" = "Ахатә ԥшаара"; + +/* Title for quick-search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.QuickEnginesTitle.v121" = "Ирццаку аԥшааратә системақәа"; + +/* Label for the `show search suggestions` setting, in the Search Settings page. */ +"Settings.Search.ShowSuggestions.v121" = "Аԥшааратә ажәалагалақәа аарԥштәуп"; + +/* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адресқәа рпанель - Ecosia Адгалара"; + +/* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Еиҳаны еилкаа Ecosia Suggest иазкны"; + +/* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Ажәалагалақәа аарԥштәуп Ecosia аҟынтәи Ажәалагала хатә сеансқәа рҿы"; + +/* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ +"Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Аԥшаара аҭоурых ала аԥшаара"; + +/* In the Search page of the Settings menu, the title for the setting to enable search bookmarks. */ +"Settings.Search.Suggest.SearchSearchBookmarks.Title.v124" = "Адыргақәа рҿы аԥшаара"; + +/* In the Search page of the Settings menu, the title for the setting to enable synced tabs. */ +"Settings.Search.Suggest.SearchSyncedTabs.Title.v124" = "Аԥсҭҳәа абларҭақәа рыԥшаара"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124" = "Уԥшаара инақәыршәаны ауеб аҟынтәи аҳәарақәа уоурц"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124.v2" = "Уԥшаара иадҳәалоу аҳәарақәа %@ аҟынтә иуоур"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124" = "Аҳәарақәа %@ аҟынтә"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from the web in Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124.v2" = "Интернет аҟынтәи ажәалагалақәа"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from sponsors. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Description.v124" = "Аспонсорцәа рҟынтәи аамҭа-аамҭалатәи ажәалагалақәа рыла %@ адгылара"; + +/* In the Search page of the Settings menu, the title for the setting to enable Suggestions from sponsors. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Title.v124" = "Аспонсорцәа рҟынтәи ажәалагалақәа"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Share.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Share.strings new file mode 100644 index 0000000000000..a53ac3304e849 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Share.strings @@ -0,0 +1,6 @@ +/* Error message shown in the remote tabs panel */ +"SendTo.NoDevicesFound.Message.v119" = "Асинхронизациазы иҟоу даҽа ҳәагак шәымам ари ахыԥхьаӡара иацҵоу."; + +/* This message appears when a user tries to use 'Send Link to Device' action while not logged in */ +"SendTo.NotSignedIn.Title.v119" = "Уара ухы иаурхәаӡом."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Shopping.strings new file mode 100644 index 0000000000000..4940dde2fabfe --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Shopping.strings @@ -0,0 +1,255 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates that a user can tap on the shopping button to start using the Shopping feature. */ +"ContextualHints.Shopping.NotOptedIn.v120" = "Аахәара ҟашәҵаанӡа, еилышәкаа аалыҵ азы ахҳәаақәа рыгәра шәгозар."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing the Shopping feature. It indicates that a user can go directly to the Shopping feature by tapping the text of the action. */ +"ContextualHints.Shopping.NotOptedInAction.v120" = "Ахҳәаа агәаҭага ԥышәшәа"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears after the user has opted in for the Shopping feature. It indicates that a user can directly open the review checker by tapping the text of the action. */ +"ContextualHints.Shopping.OptedInAction.v120" = "Иаарттәуп ахҳәаа агәаҭара"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one appears after the user has opted in and informs him if he wants use the review checker by tapping the Shopping button. */ +"ContextualHints.Shopping.OptedInBody.v120" = "Агәра угартә иҟоума арҭ ахҳәаақәа? Уажәы игәашәҭ ирҽеиу арейтинг шәбарц азы."; + +/* Footer label from the Fakespot Ad card displayed for the related product we advertise. This is displayed below the ad card, suggested as an alternative to the product reviewed. The first parameter will be replaced by the Fakespot app name */ +"Shopping.AdCard.Footer.v121" = "Аӡыргара %@ аҟынтә"; + +/* Title label for the Fakespot Ad card. This is displayed above a product image, suggested as an alternative to the product reviewed. */ +"Shopping.AdCard.Title.v121" = "Уаҳа хшыҩзышьҭра зҭатәузеи"; + +/* Description adjusted of the rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Description.v121" = "Зыгәра уго ахҳәаа шьаҭас иҟаҵаны"; + +/* Accessibility label, associated to adjusted rating stars. %@ is a decimal value from 0 to 5 that will only use a tenth (example: 3.5). */ +"Shopping.AdjustedRating.StarsAccessibilityLabel.v120" = "%@ 5 еҵәа рҟынтә"; + +/* Title of the adjusted rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Title.v120" = "Ирҽеиу ареитинг"; + +/* Button text of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Button.Text.v120" = "Еилкаауп"; + +/* Title of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Title.v120" = "Анализ актуалра амоуп"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Competitiveness.Title.v120" = "Аконкуренциа"; + +/* Title of the button that shows less reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.LessButton.Title.v120" = "Еиҵаны аарԥшра"; + +/* Title of the button that shows more reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.MoreButton.Title.v120" = "Еиҳаны аарԥшра"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet, specifically focusing on the quality, design, and condition of the product's packaging. This may include details about the box, protective materials, presentation, and overall packaging experience. */ +"Shopping.HighlightsCard.Packaging.Title.v120" = "Аҭра"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Price.Title.v120" = "Ахә"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Quality.Title.v120" = "Ахаҭабзиара"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Shipping.Title.v120" = "Азнагара"; + +/* Title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Title.v120" = "Аҵыхәтәантәи ахҳәаақәа рҟынтәи ахшыҩзышьҭра дуқәа"; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Description.v120" = "Рыцҳарас иҟалаз, ҳара иҳалшом аалыҵ хкқәа рзы ахҳәаа ахаҭабзиара агәаҭара. Иаҳҳәап, аҳамҭатә картақәеи авидеоқәеи, амузыкеи ахәмаррақәеи."; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "Ҳара иҳалшом арҭ ахҳәаақәа рыгәра агара"; + +/* Description text for an information card used in the review checker section. This message is displayed when the reviews for a product are not yet available but are expected to be provided within the next 24 hours. It serves to inform users of the short wait for reviews and encourages them to return soon for the updated information. */ +"Shopping.InfoCard.InfoComingSoon.Description.v121" = "Ҳара ари ахархәага иазкны аҭак ҳауроуп 24 сааҭ рыҩныҵҟа. Ҳаҳәоит, нас шәеиҭахәаԥш."; + +/* Title for an information card that is displayed in the review checker section when certain details about a product or feature are not currently available but are expected to be provided soon. The message should imply that the user can look forward to receiving more information shortly. */ +"Shopping.InfoCard.InfoComingSoon.Title.v121" = "Аинформациа лассы иҟалоит"; + +/* Primary action title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.PrimaryAction.v120" = "Уажәы игәашәҭ"; + +/* Title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.Title.v120" = "Аинформациа ҿыц агәаҭара"; + +/* Description for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Description.v120" = "Ҳара ари азҵаара аӡбаразы аус ҳуеит. Ҳаҳәоит, лассы шәеиҭахәаԥш."; + +/* Title for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Title.v120" = "Уажәазы ​​аинформациа ыҟаӡам"; + +/* Description for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Description.v120" = "Ари ахархәага еиҳаны ахҳәаа анаиулак, уи ахаҭабзиара агәаҭара ҳалшоит."; + +/* Title for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Title.v120" = "Ахҳәаақәа макьана иазхом"; + +/* Description for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This description is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Description.v121" = "Ари ахәҭа шьҭахьҟа иҟоу жәбазар, иҳашәҳәа, ҳара ахҳәаа гәаҳҭоит."; + +/* Primary action label for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This primary action label is used for info card button where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.PrimaryAction.v121" = "Ашьҭаҵарҭаҿы аалыҵ шыҟоу азы адырраҭара"; + +/* Title for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This title is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Title.v121" = "Аалыҵ ыҟаӡам"; + +/* Description for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Description.v120" = "Уи 60 секунд раҟара аамҭа аҭаххар ауеит."; + +/* Title for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Title.v120" = "Ахҳәаақәа рхаҭабзиара агәаҭара"; + +/* Title for info card when the product is in analysis mode. The placeholder represents the percentage of the analysis progress, ranging between 1 and 100. */ +"Shopping.InfoCard.ProgressAnalysis.Title.v123" = "Ахҳәаа ахаҭабзиара агәаҭара (%@)"; + +/* This description appears beneath the confirmation title on the information card to inform the user that their report regarding the product stock status has been received and is being processed. It serves to set the expectation that the review information will be updated within 24 hours and invites the user to revisit the product page for updates. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Description.v121" = "Ҳара ари ахархәага иазкны аҭак ҳауроуп 24 сааҭ рыҩныҵҟа. Ҳаҳәоит, нас шәеиҭахәаԥш."; + +/* This title is displayed on the information card as a confirmation message after a user reports that a previously out-of-stock product is now available. It's meant to acknowledge the user's contribution and encourage community engagement by letting them know their report has been successfully submitted. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Title.v121" = "Иҭабуп адырраҭаразы!"; + +/* Text for the analyzer button displayed when an analysis can be updated for a product. */ +"Shopping.NoAnalysisCard.AnalyzerButton.Title.v120" = "Ахҳәаақәа рхаҭабзиара гәашәҭ"; + +/* Text for the body label, to check the reliability of a product. */ +"Shopping.NoAnalysisCard.BodyLabel.Title.v120" = "Ари ахархәага иазку ахҳәаақәа рыгәра угартә иҟоу-иҟаму аилкааразы, ахҳәаақәа рхаҭабзиара гәашәҭ. Уи 60 секунд раҟара аамҭа аҭаххоит."; + +/* Title for card displayed when a shopping product has not been analysed yet. */ +"Shopping.NoAnalysisCard.HeadlineLabel.Title.v120" = "Арҭ ахҳәаақәа ирызкны макьаназы адыррақәа ыҟам"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replaced by the Fakespot app name. After the colon, what appears are two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v120" = "“Ааи, иԥышәшәа” ҳәа иалхны, шәара шәақәшаҳаҭуп %@ аҟынтә:"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). After the colon, there will be two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v123" = "“Иаҳҳәап, иԥышәшәа” ҳәа иалхны, шәақәшаҳаҭуп:"; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. This string is almost identical with 'Shopping.OptInCard.FirstParagraph.Description', but without Best Buy and Walmart websites, which are not available in many locales. */ +"Shopping.OptInCard.FirstParagraph.AmazonOnly.Description.v122" = "Аахәара ҟашәҵаанӡа %1$@ аҟны аалыҵ ахҳәаа агәрагара гәашәҭ. Ахҳәаа агәаҭара, %2$@ аҟынтәи аексперименталтә ҟазшьа, ииашаҵәҟьаны абраузер аҿы иаԥҵоуп."; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "Аахәара ҟашәҵаанӡа, шәахәаԥш шаҟа игәрагоу аалыҵ ахҳәаа %1$@ аҟны. Ахҳәаа агәаҭара, %2$@ аҟынтәи аексперименталтә ҟазшьа, ииашаҵәҟьаны абраузер аҿы иаԥҵоуп. Уи %3$@-еи %4$@-еи рҿгьы аус ауеит."; + +/* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.HeaderLabel.Title.v120" = "Еилышәкаа игәрагоу аалыҵ ахҳәаа азы ҳгаид"; + +/* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "Еиҳаны аилкаара"; + +/* Text for the main button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.MainButton.Title.v120" = "Ааи, иԥышәа"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v120" = "Амаӡаратә политика"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Firefox app name. */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v123" = "Амаӡаратә рдырраҭара %@"; + +/* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.SecondaryButton.Title.v120" = "Уажәазы ​​акәым"; + +/* Label for the second paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be replaced by the Fakespot app name. The second parameter will be replaced the company name of Mozilla. */ +"Shopping.OptInCard.SecondParagraph.Description.v120" = "%1$@ амч ахархәарала %2$@ аҟынтә, ҳара шәыцхраауеит еилых ҟамҵакәа, гәрагара змам ахҳәаақәа шәҽырцәышәыхьчарц. Ҳара ҳартифициалтә интеллект амодель еснагь аиӷьтәра иаҿуп, аахәаҩцәа раан шәыхьчарц азы."; + +/* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "Ахархәара аԥҟарақәа"; + +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "Ахархәара аԥҟарақәа %@"; + +/* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ +"Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "Ахәшьара %@"; + +/* Title of the reliability card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityCardTitle.v120" = "Шаҟа рыгәра угартә иҟоузеи арҭ ахҳәаақәа?"; + +/* Description of the reliability ratings for rating 'A' and 'B' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.AB.Description.v120" = "Зыгәра уго ахҳәаақәа"; + +/* Description of the reliability rating 'C' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.C.Description.v120" = "Зыгәра угартә иҟоуи зыгәра угашами ахҳәаақәа реилаԥса"; + +/* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Имцу ахҳәаақәа"; + +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "*Ирҽеиу арейтинг* шьаҭас иамоуп зыгәра угартә иҟоу ҳәа иҳаԥхьаӡо ахҳәаа мацара."; + +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Аиҵаҵара акарточка «Ахҳәаа ахаҭабзиара шҳаӡбо»"; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Ирҭбаа акарточка «Ахҳәаа ахаҭабзиара шҳаӡбо»"; + +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "Ҳара ҳхы иаҳархәоит %1$@ by %2$@ AI атехнологиа аалыҵ ахҳәаа аиашара агәаҭаразы. Уи шәара ишәыцхраауеит ахҳәаа ахаҭабзиара мацара ахәшьара аҭараҿы, аалыҵ ахаҭабзиара акәымкәа."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "*Ихадоу адыррақәа* аҵыхәтәантәи 80 мшы рзы %1@ аҟны ахҳәаақәа рҟынтәи иаагоуп, урҭ рыгәра угартә иҟоуп ҳәа ҳгәы иаанагоит."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "*Ихадоу адыррақәа* аҵыхәтәантәи 80 мшы рзы %@ аҟны ахҳәаақәа рҟынтәи иаагоуп, урҭ рыгәра угартә иҟоуп ҳәа ҳгәы иаанагоит."; + +/* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Label.Title.v120" = "Ахҳәаақәа рхаҭабзиара шҳаӡбо"; + +/* The title of the learn more button from How we determine review quality card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the Fakespot app name. */ +"Shopping.ReviewQualityCard.LearnMoreButton.Title.v120" = "Еиҳаны еилышәкаа %@ ахҳәаақәа рхаҭабзиара шаӡбо"; + +/* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "Ҳара ҳагәра ганы ҳаҟоуп, араҟа ииашоуи игәрагоуи ахҳәаа еилаԥсоуп ҳәа."; + +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "Узықәгәыӷыртә иҟоу ахҳәаақәа. Ҳара ҳагәра ганы ҳаҟоуп, ари ахҳәаа еиҳарак ииашоу, еилых ҟамҵа ахҳәаа ҟазҵаз ииашоу аалыҵҳәаҩцәа шракәу."; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "Аалыҵ зегьы рыхҳәаа A инаркны F рҟынӡа *ҩырала* ахәшьара раҳҭоит."; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "Амцтә хҳәаақәа. Ҳара ҳагәра ганы ҳаҟоуп, арҭ ахҳәаақәа мцыркуп ҳәа, мамзаргьы еилых ҟазҵо ахҳәааҩцәа рыла иҩуп ҳәа."; + +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "Акарта архиарақәа реизгара"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "Ирҭбаатәуп архиарақәа ркарточка"; + +/* Accessibility hint for the recommended products label and switch, grouped together. When the group is selected in VoiceOver mode, the hint is read to help the user understand what action can be performed. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityHint.v123" = "Аиқәыршәара аԥсахразы ҩынтә ақәыӷәӷәара."; + +/* Accessibility label for the recommended products label and switch, grouped together. The first placeholder is for the recommended products label, and the second placeholder is for the state of the switch: On/Off. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityLabel.v123" = "%1$@, агәыҵха аиҭакра, %2$@."; + +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "Ахҳәаа агәаҭага амыруга иаԥиҵаз %1$@ ала %2$@"; + +/* Title of the settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.Label.Title.v120" = "Архиарақәа"; + +/* Label of the switch from settings card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the app name. */ +"Shopping.SettingsCard.RecommendedProducts.Label.v120" = "Иаарԥштәуп аалыҵқәа ирыднагало %@"; + +/* Toggled Off accessibility switch value from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOff.AccessibilityLabel.v123" = "Ирцәоу"; + +/* Toggled On accessibility value, from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOn.AccessibilityLabel.v123" = "Аҿк."; + +/* Label of the button from settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.TurnOffButton.Title.v120" = "Ахәаԥшра агәаҭара аҿыхтәуп"; + +/* Beta label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Beta.Title.v120" = "АБЕТА"; + +/* Accessibility label for close button that dismisses the Shopping Experience (Fakespot) sheet. */ +"Shopping.Sheet.Close.AccessibilityLabel.v121" = "Иарктәуп ахҳәаа агәаҭара"; + +/* Label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Title.v120" = "Ахҳәаа агәаҭара"; + +/* Text for body of error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Description.v120" = "Уҳа аимадара гәаҭаны, адаҟьа ҿыц аҭагалара уҽазышәа."; + +/* Title for error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Title.v120" = "Аҳа аимадара ыҟам"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/SnackBar.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/SnackBar.strings new file mode 100644 index 0000000000000..d11e56c0c3499 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/SnackBar.strings @@ -0,0 +1,9 @@ +/* Label text that gets presented as a confirmation at the bottom of screen when the credit card is successfully removed. */ +"CreditCard.SnackBar.RemovedCardLabel.v112" = "Акарта аныхуп"; + +/* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets saved successfully */ +"CreditCard.SnackBar.SavedCardLabel.v112" = "Ахсаала ҿыц еиқәырхоуп"; + +/* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets updated successfully */ +"CreditCard.SnackBar.UpdatedCardLabel.v122" = "Ахсаала адыррақәа рҿыцуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/SuggestedSites.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/SuggestedSites.strings new file mode 100644 index 0000000000000..0138f55713a1e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/SuggestedSites.strings @@ -0,0 +1,3 @@ +/* Title for X (formerly Twitter) tile in the suggested sites section of the homepage. */ +"SuggestedSites.X.Title.v131" = "Х"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/TabLocation.strings new file mode 100644 index 0000000000000..b71720c366414 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/TabLocation.strings @@ -0,0 +1,36 @@ +/* Accessibility hint for the reload button */ +"Address.Bar.Reload.A11y.Hint.v124" = "Ҩынтә еиқәыӷәӷәаны иаанкылатәуп егьырҭ аопциақәа рбаразы"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.NotSecure.A11y.Label.v119" = "Ишәарҭам аимадара. Ирӷәӷәоу ашьҭаԥшра ахьчара аҿыхуп."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.Secure.A11y.Label.v119" = "Ишәарҭам аимадара. Ирӷәӷәоу ашьҭаԥшра ахьчара аҿыхуп."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.NotSecure.A11y.Label.v119" = "Ишәарҭам аимадара"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.Secure.A11y.Label.v119" = "Ишәарҭам аимадара"; + +/* Accessibility label for the lock / tracking protection button on the URL bar */ +"TabLocation.LockButton.AccessibilityLabel.v122" = "Ашьҭаҵара ахьчара"; + +/* Large content title for the lock button. This title is displayed when accessible font sizes are enabled */ +"TabLocation.LockButton.LargeContentTitle.v122" = "Ашьҭаҵара ахьчара"; + +/* Accessibility label for the share button in url bar */ +"TabLocation.Share.A11y.Label.v119" = "Ари адаҟьа еибыҭатәуп"; + +/* Large content title for the share button. This title is displayed when using accessible font sizes is enabled */ +"TabLocation.ShareButton.AccessibilityLabel.v122" = "Ашара"; + +/* Accessibility label for the shopping button in url bar */ +"TabLocation.Shopping.A11y.Label.v120" = "Ахҳәаа агәаҭара"; + +/* Large content title for the tabs button. The argument is the number of open tabs or an infinity symbol. This title is displayed when using accessible font sizes is enabled. */ +"TabsButton.Accessibility.LargeContentTitle.v122" = "Аҵаҩыра аарԥшра: %@"; + +/* Large content title for the button shown in editing mode to remove this site from the top sites panel. */ +"TopSites.RemoveButton.LargeContentTitle.v122" = "Ианыхтәуп адаҟьа"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/TabToolbar.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/TabToolbar.strings new file mode 100644 index 0000000000000..5b125958a0a6f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/TabToolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the tab toolbar fire button in private mode, used to provide users a way to end and delete their private session data. */ +"TabToolbar.Accessibility.DataClearance.v122" = "Адырқәа рыцқьара"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/TabsTray.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/TabsTray.strings new file mode 100644 index 0000000000000..043dd18f5cfc2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/TabsTray.strings @@ -0,0 +1,18 @@ +/* When the user closes tabs in the tab tray, a popup will appear. This is the title for the button to undo the deletion of those tabs */ +"CloseTabsToast.Button.v113" = "Аԥыхра"; + +/* When the user closes an individual tab in the tab tray, a popup will appear informing them the tab was closed. This is the text for the popup. */ +"CloseTabsToast.SingleTabTitle.v113" = "Абларҭа аркуп"; + +/* When the user closes tabs in the tab tray, a popup will appear informing them how many tabs were closed. This is the text for the popup. The placeholder is the number of tabs */ +"CloseTabsToast.Title.v113" = "Аҭаӡҩырақәа аркуп: %d"; + +/* This is the swipe action title for closing an inactive tab by swiping, located in the Inactive Tabs section of the Tabs Tray */ +"InactiveTabs.TabTray.CloseSwipeActionTitle.v115" = "Аркра"; + +/* Users can disable syncing tabs from other devices. In the Sync Tabs panel of the Tab Tray, we inform the user tab syncing can be switched back on to view those tabs. */ +"TabsTray.Sync.SyncTabsDisabled.v116" = "Аҭаӡҩыра асинхронизациа аус арутәуп, даҽа ҳәагақәак рҟынтәи аҭаӡҩырақәа рсиа абара азы."; + +/* Button label to sync tabs in your account */ +"TabsTray.SyncTabs.SyncTabsButton.Title.v119" = "Агәылаҵақәа рсинхронизациа"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Toolbar.strings new file mode 100644 index 0000000000000..ed174141962df --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Абларҭа ҿыц"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Ари абларҭа арктәуп"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ToolbarLocation.strings new file mode 100644 index 0000000000000..f7d1793ececf4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ToolbarLocation.strings @@ -0,0 +1,9 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ +"ContextualHints.Toolbar.Bottom.Description.v107" = "Еиҳа ишәзыманшәалазар, амыругақәа рпанель аҩада ииагатәуп."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "Уи аҭаӡҩыра аҭоурых аҟны адаҟьақәа рыбжьара аиҭасразы ашәақь агәыҵәҟьақәа шәақәыӷәӷәаны иаанкылатәуп."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ +"ContextualHints.Toolbar.Top.Description.v107" = "Уи еиҳа иузыманшәалазар, амыругақәа рпанель ҵаҟа ииагатәуп."; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/UpdateCard.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/UpdateCard.strings new file mode 100644 index 0000000000000..2061723747817 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/UpdateCard.strings @@ -0,0 +1,15 @@ +/* This value is used as the toast message for the saving success alert in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v116" = "Ахсаала адыррақәа рҿыцуп"; + +/* This value is used as the title for the update card page */ +"CreditCard.UpdateCard.MainTitle.v122" = "Ахсаала рҿыцтәума?"; + +/* This value is used as the title for the Manage Cards button from the update credit card page */ +"CreditCard.UpdateCard.ManageCardsButtonTitle.v115" = "Акартқәа рнапхгара"; + +/* This value is used as the title for the Not Now button in the update credit card page */ +"CreditCard.UpdateCard.NotNowButtonTitle.v115" = "Уажәазы ​​акәым"; + +/* This value is used as the title for the button in the update credit card page. It indicates the action to update the details f9 the card. */ +"CreditCard.UpdateCard.YesButtonTitle.v122" = "Арҿыцра"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/Upgrade.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/Upgrade.strings new file mode 100644 index 0000000000000..730b4d94afa34 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/Upgrade.strings @@ -0,0 +1,18 @@ +/* Describes an action on the sync upgrade page in our Upgrade screens. This string will be on a button so user can sign up or login directly in the upgrade. */ +"Upgrade.SyncSign.Action.v114" = "Аҭалара"; + +/* Description string used to sign in to sync in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.SyncSign.Description.v114" = "Уааныжьны уца, даҽа ҳәагақәак рҟынтәи аҭаӡҩырақәа уажәы ухатә даҟьаҿы."; + +/* Title string used to sign in to sync in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.SyncSign.Title.v114" = "Аекранқәа реиҭныԥсахлара еиҳа имариоуп"; + +/* Describes the action on the first upgrade page in the Upgrade screen. This string will be on a button so user can continue the Upgrade. */ +"Upgrade.Welcome.Action.v114" = "Абраузер ишыҟоу еиԥш ишьақәыргылатәуп"; + +/* Description string used to welcome back users in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.Welcome.Description.v114" = "Аԥштәы ҿыцқәа. Аманшәалара ҿыц. Ахашәалахәы ашьҭашәарыцара акәымкәа, ауаа рзы аҽадцалара."; + +/* Title string used to welcome back users in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.Welcome.Title.v114" = "Бзиала шәаабеит еиҳа ахатә интернет ахь"; + diff --git a/firefox-ios/Shared/Supporting Files/ab.lproj/ZoomPageBar.strings b/firefox-ios/Shared/Supporting Files/ab.lproj/ZoomPageBar.strings new file mode 100644 index 0000000000000..45901f474aea4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ab.lproj/ZoomPageBar.strings @@ -0,0 +1,12 @@ +/* Accessibility label for closing the zoom panel in Zoom Page Bar */ +"Menu.ZoomPage.Close.AccessibilityLabel.v113" = "Иарктәуп азум апанель"; + +/* Accessibility label for current zoom level in Zoom Page Bar. The placeholder represents the zoom level */ +"Menu.ZoomPage.CurrentZoomLevel.AccessibilityLabel.v113" = "Уажәтәи ашкала: %@"; + +/* Accessibility label for decreasing the zoom level in Zoom Page Bar */ +"Menu.ZoomPage.DecreaseZoom.AccessibilityLabel.v113" = "Амҽхак армаҷра"; + +/* Accessibility label for increasing the zoom level in Zoom Page Bar */ +"Menu.ZoomPage.IncreaseZoom.AccessibilityLabel.v113" = "Аизум арҭбаара"; + diff --git a/firefox-ios/Shared/Supporting Files/be.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/be.lproj/Settings.strings index b7a58a7dc2656..fd0855cdbaeb6 100644 --- a/firefox-ios/Shared/Supporting Files/be.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/be.lproj/Settings.strings @@ -89,7 +89,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Прадвызначаны пашукавік"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Даведайцеся больш пра Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Даведайцеся больш пра Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Альтэрнатыўныя пошукавыя сістэмы"; @@ -116,10 +116,10 @@ "Settings.Search.ShowSuggestions.v121" = "Паказваць прапановы пошуку"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адрасны радок - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адрасны радок - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Даведайцеся больш пра Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Даведайцеся больш пра Ecosia Suggest"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Пошук у гісторыі аглядання"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/br.lproj/AddressToolbar.strings index 5ce6413f22caa..2d735861bf708 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Arventennoù evit ho puhez prevez ha diogelroez"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Digeriñ a ra dibab al lusker klask"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Lusker enklask: %@"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings index 8ef10842e3c66..84d0c14ec5315 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings @@ -49,6 +49,9 @@ /* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ "Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "Dilamet eo bet an toupinoù hag ar roadennoù lec’hienn"; +/* The accessibility label for the close button in the Enhanced Tracking protection menu. */ +"Menu.EnhancedTrackingProtection.CloseButton.AccessibilityLabel.v132" = "Serriñ al lañser prevezded ha diogelroez"; + /* Text to let users know that the current website is secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "Kennask diarvar"; @@ -58,6 +61,9 @@ /* Text to let users know that no trackers were found on the current website. */ "Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "N'eo bet kavet heulier ebet"; +/* Text to let users know how many analytics trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Analytics.v132" = "Endalc'had heuliañ: %@"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Toupinoù heuliañ etre al lec’hiennoù: %@"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/br.lproj/FxANotification.strings new file mode 100644 index 0000000000000..6a6f24fb11f4f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/br.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ ivinell serret: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Diskouez an ivinelloù bet serret nevez zo"; + diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/KeyboardAccessory.strings b/firefox-ios/Shared/Supporting Files/br.lproj/KeyboardAccessory.strings new file mode 100644 index 0000000000000..e5500f9f0affd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/br.lproj/KeyboardAccessory.strings @@ -0,0 +1,6 @@ +/* Accessibility label for next button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.NextButton.Accessibility.Label.v124" = "Maezienn furmskrid a zeu"; + +/* Accessibility label for previous button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.PreviousButton.Accessibility.Label.v124" = "Maezienn furmskrid kent"; + diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/br.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..ffd710a760103 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/br.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "An eurvezh tremenet"; + diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/LoginsHelper.strings b/firefox-ios/Shared/Supporting Files/br.lproj/LoginsHelper.strings index 8c9b9e7cec3b4..d6a6c9acd6e78 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/LoginsHelper.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/LoginsHelper.strings @@ -1,3 +1,6 @@ +/* Button to not save the user's password in the logins helper */ +"LoginsHelper.DontSave.Button.v122" = "Diwezhatoc’h"; + /* Button to not update the user's password in the logins helper */ "LoginsHelper.DontUpdate.Button.v122" = "Diwezhatoc’h"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/br.lproj/MainMenu.strings index ee67e0dff4b8f..4f8c55580ee51 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/MainMenu.strings @@ -1,9 +1,21 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "Kavit ar pezh a glaskit buanoc’h, eus ar merdeiñ prevez d'an enrollañ"; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "Nevez: lañser simplaet"; + /* The accessibility label for the back button in the Main menu header navigation view. */ "MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Distreiñ"; /* The accessibility label for the close button in the Main menu. */ "MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Serriñ"; +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "Kennaskit da c’houbredañ gerioù-tremen, ivinelloù ha muioc’h c’hoazh"; + +/* On the main menu, at the top, when the user is signed out. The description for the sign in action */ +"MainMenu.Account.SignedOut.Description.v131" = "Goubredit gerioù-tremen, ivinelloù ha muioc’h c’hoazh"; + /* On the main menu, at the top, when the user is signed out. The title for the sign in action */ "MainMenu.Account.SignedOut.Title.v131" = "Kennaskañ"; @@ -46,6 +58,9 @@ /* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ "MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Arventennoù"; +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "Nevez e %@"; + /* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ "MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Personelaat an degemer"; @@ -55,6 +70,9 @@ /* On the main menu, the title for the action that will take the user to the Settings menu. */ "MainMenu.SettingsSection.Settings.Title.v131" = "Arventennoù"; +/* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.WhatsNew.Title.v131" = "Nevez e %@"; + /* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ "MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Degemer"; @@ -76,6 +94,9 @@ /* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ "MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "Kemmañ ar sined"; +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "Dilemel diwar ar roll lenn"; + /* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ "MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "Lemel diouzh ar berradennoù"; @@ -85,6 +106,12 @@ /* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ "MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Title.v132" = "Enrollañ evel PDF"; +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Subtitle.v132" = "Roll Lenn"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Title.v132" = "Enrollañ er roll lenn"; + /* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool. */ "MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131" = "Degemer"; @@ -97,6 +124,96 @@ /* On the main menu, in the Save submenu, the title for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ "MainMenu.Submenus.Save.AddToShortcuts.Title.v131" = "Ouzhpennañ d’ar berradennoù"; +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.BookmarkThisPage.Subtitle.v131" = "Ouzhpennañ ur sined"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.BookmarkThisPage.Title.v131" = "Lakaat ur sined war ar bajenn-mañ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.EditBookmark.Title.v131" = "Embann ar sined"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.RemoveFromReadingList.Title.v131" = "Dilemel diwar ar roll lenn"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.RemoveFromShortcuts.Title.v131" = "Lemel diouzh ar berradennoù"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.SaveAsPDF.Subtitle.v131" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.SaveAsPDF.Title.v131" = "Enrollañ evel PDF"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.SaveToReadingList.Subtitle.v131" = "Roll Lenn"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.SaveToReadingList.Title.v131" = "Enrollañ er roll lenn"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Off.Title.v132" = "Diweredekaat ar mod noz"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.On.Title.v132" = "Gweredekaat ar mod noz"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Subtitle.v132" = "Mod noz"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Subtitle.v132" = "Moullañ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "Moullañ"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "Diweredekaat ar mod lenn"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "Gweredekaat ar mod lenn"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "Mod Lenn"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Danevelliñ"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Danevelliñ ul lec'hienn torret"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Rannañ"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Rannañ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Zoum"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Zoum (%@)"; + +/* On the main menu, the title for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Diweredekaat ar mod noz"; + +/* On the main menu, the title for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.NightMode.On.Title.v131" = "Gweredekaat ar mod noz"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.NightMode.Subtitle.v131" = "Mod noz"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.Print.Subtitle.v131" = "Moullañ"; + +/* On the main menu, the title for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.Print.Title.v131" = "Moullañ"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.Off.Title.v131" = "Diweredekaat ar mod lenn"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.On.Title.v131" = "Gweredekaat ar mod lenn"; + /* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ "MainMenu.Submenus.Tools.ReaderView.Subtitle.v131" = "Mod lenn"; @@ -118,6 +235,9 @@ /* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ "MainMenu.Submenus.Tools.Zoom.Title.v131" = "Zoum (%@)"; +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Lañser pennañ"; + /* On the main menu, the accessibility label for the action that will create a new private tab. */ "MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Ivinell brevez nevez"; @@ -136,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Enrollañ"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Enrollañ an is-menu"; + /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Ostilhoù"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/br.lproj/Settings.strings index 110b99c0a178a..6e8fb34c665f4 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Keflusker enklask dre ziouer"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Gouzout hiroc’h diwar-benn ar pezh a vez aliet gant Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Gouzout hiroc’h diwar-benn ar pezh a vez aliet gant Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Luskerioù enklask all"; @@ -125,10 +125,10 @@ "Settings.Search.ShowSuggestions.v121" = "Diskouez kinnigoù klask"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barrenn chomlec’hioù - Alioù Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barrenn chomlec’hioù - Alioù Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Gouzout hiroc’h diwar-benn ar pezh a vez aliet gant Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Gouzout hiroc’h diwar-benn ar pezh a vez aliet gant Ecosia"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Klask er roll istor"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/br.lproj/Shopping.strings index 565bda247d048..2832d235aadc8 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/Shopping.strings @@ -76,6 +76,12 @@ /* Title for card displayed when a shopping product has not been analysed yet. */ "Shopping.NoAnalysisCard.HeadlineLabel.Title.v120" = "Titour ebet diwar-benn an alioù-se c’hoazh"; +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "Sellit pegen fizius eo alioù ar produ war%1$@ a-raok prenañ. Ar gwirier alioù a zo ur c’heweriuster arnodel gant %2$@ enkorfet er merdeer. Mont a-ra en-dro e %3$@ ha %4$@ ivez."; + +/* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.HeaderLabel.Title.v120" = "Esaeit hon heñcher evit an alioù produ"; + /* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "Gouzout hiroc’h"; @@ -85,9 +91,15 @@ /* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ "Shopping.OptInCard.PrivacyPolicy.Button.Title.v120" = "Reolenn a-fet buhez prevez"; +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Firefox app name. */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v123" = "Evezhiadennoù a-fet buhez prevez %@"; + /* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.SecondaryButton.Title.v120" = "Ket bremañ"; +/* Label for the second paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be replaced by the Fakespot app name. The second parameter will be replaced the company name of Mozilla. */ +"Shopping.OptInCard.SecondParagraph.Description.v120" = "A-drugarez da c'halloud %1$@ gant %2$@ e sikouromp ac’hanoc’h da chom hep lenn alioù n’int ket neptu. Hor patrom NA a glask bepred gwareziñ ac’hanoc’h pa rit prenadennoù."; + /* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ "Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "Termenoù implij"; @@ -109,12 +121,57 @@ /* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Alioù disfizius"; +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "An *notenn reizhet* a zo diazezet war an alioù fizius."; + +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Bihanaat ar gartenn \"Penaos e wiriekaomp perzhded an alioù\""; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Brasaat ar gartenn \"Penaos e wiriekaomp perzhded an alioù\""; + +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "Ober a reomp gant teknologiezh an naouegezh alvezel savet gant %1$@ evit %2$@ evit gwiriañ eo fizius an alioù war ar produioù. Kement-se a sikouro ac’hanoc’h da gompren gwelloc’h perzhded an alioù, met ket hini ar produioù."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "Ar *poentoù pouezus* a zeu eus alioù %1@ er 80 devezh diwezhañ a soñj deomp int fizius. "; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "Ar *poentoù pouezus* a zeu eus alioù %@ er 80 devezh diwezhañ a soñj deomp int fizius. "; + /* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQualityCard.Label.Title.v120" = "Penaos e priziomp perzhded an alioù"; /* The title of the learn more button from How we determine review quality card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the Fakespot app name. */ "Shopping.ReviewQualityCard.LearnMoreButton.Title.v120" = "Gouzout hiroc’h diwar-benn an doare ma vez priziet perzhded an alioù gant %@"; +/* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "Soñjal a ra deomp zo ur meskaj etre alioù fizius ha disfizius"; + +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "Alioù fizius. Soñjal a ra deomp int skrivet gant tud gwir o dues laosket un ali onest ha fizius."; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "Reiñ a reomp d'an holl alioù un *notenn* etre A ha F."; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "Alioù disfizius. Soñjal a ra deomp eo faos an alioù, pe skrivet gant tud n'int ket neptu."; + +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "Bihanaat ar gartenn Arventennoù"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "Brasaat ar gartenn arventennoù"; + +/* Accessibility hint for the recommended products label and switch, grouped together. When the group is selected in VoiceOver mode, the hint is read to help the user understand what action can be performed. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityHint.v123" = "Stokit div wech evit cheñch an arventenn"; + +/* Accessibility label for the recommended products label and switch, grouped together. The first placeholder is for the recommended products label, and the second placeholder is for the state of the switch: On/Off. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityLabel.v123" = "%1$@, trec’haoler, %2$@"; + +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "Ar gwirier alioù a zo lusket gant %1$@ gant %2$@"; + /* Title of the settings card displayed in the shopping review quality bottom sheet. */ "Shopping.SettingsCard.Label.Title.v120" = "Arventennoù"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/SuggestedSites.strings b/firefox-ios/Shared/Supporting Files/br.lproj/SuggestedSites.strings new file mode 100644 index 0000000000000..471f9fc3c84b0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/br.lproj/SuggestedSites.strings @@ -0,0 +1,3 @@ +/* Title for X (formerly Twitter) tile in the suggested sites section of the homepage. */ +"SuggestedSites.X.Title.v131" = "X"; + diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/br.lproj/TabLocation.strings index b1cdb8f62f5f4..ec49d581b4fc9 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/TabLocation.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/TabLocation.strings @@ -1,3 +1,9 @@ +/* Accessibility hint for the reload button */ +"Address.Bar.Reload.A11y.Hint.v124" = "Stokit div wech ha dalc'hit evit muioc’h a zibarzhioù"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.NotSecure.A11y.Label.v119" = "N'eo ket sur ar c’hennask. Diweredekaet eo ar gwarez heuliañ araoket."; + /* Accessibility label for the security icon in url bar */ "TabLocation.ETP.Off.Secure.A11y.Label.v119" = "Kennask suraet. Diweredekaet eo ar gwarez heuliañ araoket."; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/br.lproj/ToolbarLocation.strings index 01beddc11c41a..7e6ddd9822c08 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/ToolbarLocation.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/ToolbarLocation.strings @@ -1,6 +1,9 @@ /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ "ContextualHints.Toolbar.Bottom.Description.v107" = "Dilec'hiit ar varenn ostilhoù en nec’h mard eo ar pezh a glot ganeoc’h."; +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "Stokit ha dalc’hit ar biroù evit lammat etre ar pajennoù e roll istor an ivinell."; + /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ "ContextualHints.Toolbar.Top.Description.v107" = "Dilec'hiit ar varenn ostilhoù e traoñ ar bajenn mard eo ar pezh a glot ganeoc’h."; diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/AddressToolbar.strings index da08e8342dff8..613214e45a097 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Podešavanja sigurnosti i privatnosti"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Otvara izbor pretraživača"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Pretraživač: %@"; diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/FxANotification.strings new file mode 100644 index 0000000000000..938edca05cb75 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ zatvorenih tabova: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Pregledajte nedavno zatvorene tabove"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..942261c134e86 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Zadnji sat"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/MainMenu.strings index b224a35b42ecc..62dd26001ee7f 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Sačuvaj"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sačuvaj podmeni"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Prebacite se na desktop stranicu"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Alati"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podmeni alata"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Pronađi na stranici…"; diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..fc4b101329df8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Zatvori"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Otvara postavke pretrage"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Postavke pretraživanja"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Ovaj put traži u:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Postavke pretraživanja"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Ovaj put traži u:"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings index e3de4d33abd75..da955bf5ee6e3 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Glavni pretraživač"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saznajte više o Firefox prijedlozima"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saznajte više o Ecosia prijedlozima"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternativni pretraživači"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Prikaži prijedloge pretraživanja"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresna traka - Firefox prijedlozi"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresna traka - Ecosia prijedlozi"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saznajte više o Firefox prijedlozima"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saznajte više o Ecosia prijedlozima"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikaži prijedloge iz Firefox Suggest u privatnim sesijama"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikaži prijedloge iz Ecosia Suggest u privatnim sesijama"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Pretraži historiju surfanja"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/AddressToolbar.strings index 65982c3784138..dbffc3b6c1893 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Paràmetres de privadesa i seguretat"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Obre la selecció del motor de cerca"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Motor de cerca: %@"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/Bookmarks.strings index b7619bb3a253d..17976e16086fe 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/Bookmarks.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/Bookmarks.strings @@ -4,6 +4,9 @@ /* Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen */ "Bookmarks.Menu.BookmarksTopLevelTitle.v131" = "Adreces d'interès"; +/* The title for the Delete Bookmark button, in the Edit Bookmark popup screen which is summoned from the main menu's Save submenu, which will delete the currently bookmarked site from the user's bookmarks. */ +"Bookmarks.Menu.DeleteBookmark.v132" = "Suprimeix l’adreça d’interès"; + /* Label of toast displayed after a bookmark is deleted in the Bookmarks menu. %@ is the name of the bookmark. */ "Bookmarks.Menu.DeletedBookmark.v131" = "S'ha suprimit «%@»"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings index ed1b0621f088c..d11ee87d4cd2b 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings @@ -139,3 +139,6 @@ /* Toast message confirming that an address has been successfully updated. */ "Addresses.Toast.AddressUpdatedConfirmation.v129" = "S’ha actualitzat la informació de l’adreça"; +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v132.v2" = "S’ha desat l’adreça"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings index c72d882e4455e..e79657ad5e348 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings @@ -49,6 +49,9 @@ /* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ "Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "S'han eliminat les galetes i dades dels llocs"; +/* The accessibility label for the close button in the Enhanced Tracking protection menu. */ +"Menu.EnhancedTrackingProtection.CloseButton.AccessibilityLabel.v132" = "Tanca el menú de privadesa i seguretat"; + /* Text to let users know that the current website is secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "Connexió segura"; @@ -58,6 +61,9 @@ /* Text to let users know that no trackers were found on the current website. */ "Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "No s'ha trobat cap element de seguiment"; +/* Text to let users know how many analytics trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Analytics.v132" = "Contingut que fa seguiment: %@"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Galetes de seguiment entre llocs: %@"; @@ -73,6 +79,9 @@ /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ "Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "Verificat per %@"; +/* The title for the button that allows users to view certificates inside the enhanced tracking protection details screen. */ +"Menu.EnhancedTrackingProtection.Details.ViewCertificatesTitle.v131" = "Mostra el certificat"; + /* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ "Menu.EnhancedTrackingProtection.Off.Header.v128" = "El %@ està fora de servei. Us recomanem reactivar les proteccions."; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/FxANotification.strings new file mode 100644 index 0000000000000..ac28bd23159ba --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Pestanyes del %1$@ tancades: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Mostra les pestanyes tancades recentment"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f8c27a1d47976 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "La darrera hora"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/MainMenu.strings index 8654ebe822b76..8a2988034873c 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/MainMenu.strings @@ -1,3 +1,18 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "Trobeu-ho tot més ràpidament: des de la navegació privada fins a les accions per a desar."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "Nou: menú simplificat"; + +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Enrere"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Tanca"; + +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "Inicieu la sessió per a sincronitzar contrasenyes, pestanyes i molt més"; + /* On the main menu, at the top, when the user is signed out. The description for the sign in action */ "MainMenu.Account.SignedOut.Description.v131" = "Sincronitzeu contrasenyes, pestanyes i molt més"; @@ -10,6 +25,18 @@ /* On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state. */ "MainMenu.Account.SyncError.Title.v131" = "Torneu a iniciar la sessió per sincronitzar"; +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Adreces d'interès"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Baixades"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Historial"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Contrasenyes"; + /* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ "MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Adreces d'interès"; @@ -22,6 +49,18 @@ /* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ "MainMenu.PanelLinkSection.Passwords.Title.v131" = "Contrasenyes"; +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "Personalitza la pàgina d’inici"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Obteniu ajuda"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Paràmetres"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "Novetats del %@"; + /* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ "MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Personalitza la pàgina d’inici"; @@ -34,6 +73,45 @@ /* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ "MainMenu.SettingsSection.WhatsNew.Title.v131" = "Novetats del %@"; +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Inici"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "Afegeix a la pantalla d'inici"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "Drecera"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "Afegeix a les dreceres"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "Afegeix a les adreces d'interès"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Title.v132" = "Afegeix la pàgina a les adreces d'interès"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "Edita l'adreça d'interès"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "Elimina de la llista de lectura"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "Elimina de les dreceres"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Subtitle.v132" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Title.v132" = "Desa com a PDF"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Subtitle.v132" = "Llista de lectura"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Title.v132" = "Desa a la llista de lectura"; + /* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool. */ "MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131" = "Inici"; @@ -73,6 +151,48 @@ /* On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list. */ "MainMenu.Submenus.Save.SaveToReadingList.Title.v131" = "Desa a la llista de lectura"; +/* On the main menu, the accessibility label for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Off.Title.v132" = "Desactiva el mode nocturn"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.On.Title.v132" = "Activa el mode nocturn"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Subtitle.v132" = "Mode nocturn"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Subtitle.v132" = "Imprimeix"; + +/* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "Imprimeix"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "Desactiva la vista de lectura"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "Activa la vista de lectura"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "Vista de lectura"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Informa"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Informeu sobre un lloc que no funciona"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Comparteix"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Comparteix"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Zoom"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Zoom (%@)"; + /* On the main menu, the title for the action that will turn Night Mode off in the application. */ "MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Desactiva el mode nocturn"; @@ -115,12 +235,42 @@ /* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ "MainMenu.Submenus.Tools.Zoom.Title.v131" = "Zoom (%@)"; +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Menú principal"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Pestanya privada nova"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "Pestanya nova"; + /* On the main menu, the title for the action that will create a new private tab. */ "MainMenu.TabsSection.NewPrivateTab.Title.v131" = "Pestanya privada nova"; /* On the main menu, the title for the action that will create a new, non-private, tab. */ "MainMenu.TabsSection.NewTab.Title.v131" = "Pestanya nova"; +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "Cerca a la pàgina"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Desa"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Submenú Desa"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Canvia al lloc d’escriptori"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "Canvia al lloc mòbil"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Eines"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Submenú d'eines"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Cerca a la pàgina…"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings index 30b5417ad6337..f9cded93d520b 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings @@ -37,6 +37,24 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ "Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "Logotip del %@"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Dissatisfied.v132" = "Insatisfet/a"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Neutral.v132" = "Neutre/a"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.NotApplicable.v132" = "No l'utilitzo"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Satisfied.v132" = "Satisfet/a"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VeryDissatisfied.v132" = "Molt insatisfet/a"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VerySatisfied.v132" = "Molt satisfet/a"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ "Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ de %2$@"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/PasswordGenerator.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/PasswordGenerator.strings new file mode 100644 index 0000000000000..35c946bf7962c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/PasswordGenerator.strings @@ -0,0 +1,27 @@ +/* Accessibility label describing a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.A11yLabel.v132" = "Generador de contrasenyes"; + +/* Accessibility label describing the close button for the popup related to a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.CloseButtonA11ylabel.v132" = "Tanca"; + +/* When a user is in the process of creating an account, they have the option to generate a password. The user is capable of copying this password after long pressing the value of the password displayed to them. This string is the label of the copy button that appears after long pressing the password. */ +"PasswordGenerator.CopyPasswordButtonLabel.v132" = "Còpia"; + +/* Text displayed when a user interacts with the password field in a signup form, as part of a popup. This popup allows the user to generate a password that they have the option to use when signing up for an account. */ +"PasswordGenerator.Description.v132" = "Protegiu el compte utilitzant una contrasenya segura generada aleatòriament."; + +/* When a user is in the process of creating an account, they have the option to generate a password. The popup displaying the generated password to the user is available by clicking a keyboard accessory button with this label. */ +"PasswordGenerator.KeyboardAccessoryButtonLabel.v132" = "Useu una contrasenya segura"; + +/* Prefix to alert accessibility users that a generated password (represented by %@) will be read to them next. */ +"PasswordGenerator.PasswordReadoutPrefaceA11y.v132" = "Contrasenya generada: %@"; + +/* Accessibility label describing a refresh password button belonging to a popup that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.RefreshPasswordButtonA11yLabel.v132" = "Genera una contrasenya segura nova"; + +/* Title text displayed as part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user -- clicking a button fills in the password of the signup form with this generated password. */ +"PasswordGenerator.Title.v132" = "Voleu utilitzar una contrasenya segura?"; + +/* Label of a button that is part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user and clicking this button fills in the password field of the signup form with this generated password. */ +"PasswordGenerator.UsePasswordButtonLabel.v132" = "Utilitza la contrasenya"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..8c57a79d176b4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Tanca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Obre els paràmetres de cerca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Paràmetres de cerca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Aquesta vegada, cerca en:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Paràmetres de cerca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Aquesta vegada, cerca en:"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/Settings.strings index a81e0582a7e3a..dad81f27c63e1 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motor de cerca per defecte"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Més informació sobre els suggeriments del Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Més informació sobre els suggeriments del Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motors de cerca alternatius"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostra suggeriments de cerca"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra d'adreces - Suggeriments del Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra d'adreces - Suggeriments del Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Més informació sobre els suggeriments del Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Més informació sobre els suggeriments del Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostra suggeriments del Firefox en sessions privades"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostra suggeriments del Ecosia en sessions privades"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Cerca en l’historial de navegació"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/ToolbarLocation.strings index 70c9b53d0ad4e..cc6bb2499ed4f 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/ToolbarLocation.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/ToolbarLocation.strings @@ -1,6 +1,9 @@ /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ "ContextualHints.Toolbar.Bottom.Description.v107" = "Poseu la barra d'eines a la part superior, si és més del vostre estil."; +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "Manteniu premudes les fletxes per saltar entre les pàgines de l’historial d’aquesta pestanya."; + /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ "ContextualHints.Toolbar.Top.Description.v107" = "Poseu la barra d'eines a la part inferior, si és més del vostre estil."; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/co.lproj/AddressToolbar.strings index 3ef7832bbb0bb..4a3f4a4605ada 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Parametri di vita privata è di sicurità"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Apre a selezzione di u mutore di ricerca"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Mutore di ricerca : %@"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Bookmarks.strings index 6f11bc069c5cb..67c90d11dcb00 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/Bookmarks.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Bookmarks.strings @@ -4,9 +4,21 @@ /* Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen */ "Bookmarks.Menu.BookmarksTopLevelTitle.v131" = "Indette"; +/* The title for the Delete Bookmark button, in the Edit Bookmark popup screen which is summoned from the main menu's Save submenu, which will delete the currently bookmarked site from the user's bookmarks. */ +"Bookmarks.Menu.DeleteBookmark.v132" = "Squassà l’indetta"; + +/* Label of toast displayed after a bookmark is deleted in the Bookmarks menu. %@ is the name of the bookmark. */ +"Bookmarks.Menu.DeletedBookmark.v131" = "« %@ » squassatu"; + +/* When a folder is longpressed in the bookmarks menu, a `Delete Folder` button is present. */ +"Bookmarks.Menu.DeleteFolder.v131" = "Squassà u cartulare"; + /* When a bookmark is longpressed in the bookmarks menu, an `Edit Bookmark` button is present. */ "Bookmarks.Menu.EditBookmark.v131" = "Mudificà l’indetta"; +/* When editing a bookmark, you can select the folder that the bookmark will be saved in. The label for this section of the view is `Save in`. */ +"Bookmarks.Menu.EditBookmarkSaveIn.v131" = "Arregistrà in"; + /* Label on the top of the `Edit Bookmarks` screen. */ "Bookmarks.Menu.EditBookmarkTitle.v131" = "Mudificà l’indetta"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings index cf21681d1037f..ef1073132dcc3 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings @@ -139,3 +139,6 @@ /* Toast message confirming that an address has been successfully updated. */ "Addresses.Toast.AddressUpdatedConfirmation.v129" = "Infurmazioni d’indirizzu mudificate"; +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v132.v2" = "Indirizzu arregistratu"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings index 34f02f08068ed..8967641bb93f6 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings @@ -1,6 +1,36 @@ +/* The title for the certificate common name inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.CommonName.v131" = "Nome cumunu"; + /* The title for the certificate issuer country inside the certificate screen. */ "Menu.EnhancedTrackingProtection.Certificates.IssuerCountry.v131" = "Paese"; +/* The title for the certificate issuer name section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.IssuerName.v131" = "Nome di l’emettidore"; + +/* The title for the certificate issuer organization inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.IssuerOrganization.v131" = "Urganismu"; + +/* The title for the certificate subject alt names section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.SubjectAltNames.v131" = "Nomi alternativi di u sughjettu"; + +/* The title for the certificate subject alt names DNS name inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.SubjectAltNamesDNSName.v131" = "Nome DNS"; + +/* The title for the certificate subject name section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.SubjectName.v131" = "Nome di u sughjettu"; + +/* The title for the certificates screen inside the certificates screen. */ +"Menu.EnhancedTrackingProtection.Certificates.Title.v131" = "Certificatu"; + +/* The title for the certificate validity section inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.Validity.v131" = "Validità"; + +/* The title for the certificate validity not after date inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.ValidityNotAfter.v131" = "Micca dopu"; + +/* The title for the certificate validity not before date inside the certificate screen. */ +"Menu.EnhancedTrackingProtection.Certificates.ValidityNotBefore.v131" = "Micca nanzu"; + /* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.ClearData.AlertCancelButton.v128" = "Abbandunà"; @@ -19,6 +49,9 @@ /* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ "Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "Canistrelli è dati di siti sò stati cacciati"; +/* The accessibility label for the close button in the Enhanced Tracking protection menu. */ +"Menu.EnhancedTrackingProtection.CloseButton.AccessibilityLabel.v132" = "Chjode u listinu di vita privata è di sicurità"; + /* Text to let users know that the current website is secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "Cunnessione assicurizata"; @@ -28,6 +61,9 @@ /* Text to let users know that no trackers were found on the current website. */ "Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ùn si trova nisunu perseguitatore"; +/* Text to let users know how many analytics trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Analytics.v132" = "Cuntenutu impiegatu per u spiunagiu : %@"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Canistrelli di spiunagiu intersiti : %@"; @@ -43,6 +79,9 @@ /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ "Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "Verificatu da %@"; +/* The title for the button that allows users to view certificates inside the enhanced tracking protection details screen. */ +"Menu.EnhancedTrackingProtection.Details.ViewCertificatesTitle.v131" = "Affissà u certificatu"; + /* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ "Menu.EnhancedTrackingProtection.Off.Header.v128" = "%@ ùn hè micca in serviziu. Vi ricumandemu d’attivà torna e prutezzioni."; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/co.lproj/FxANotification.strings new file mode 100644 index 0000000000000..c7e6903966488 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Unghjette di %1$@ chjose : %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Affissà l’unghjette chjose pocu fà"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/co.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..b8a1cfcd01ea8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "L’ultima ora"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/co.lproj/MainMenu.strings index a45b0ff5f3fcc..4ad41dd93d7bc 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/MainMenu.strings @@ -1,6 +1,42 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "Truvate più prestu ciò chì vo avete bisognu, da a navigazione privata à l’azzioni d’arregistramentu."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "Novu : listinu simplificatu"; + +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Ritornu"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Chjode"; + +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "Cunnittitevi per sincrunizà e parolle d’intesa, l’unghjette, è ancu di più"; + +/* On the main menu, at the top, when the user is signed out. The description for the sign in action */ +"MainMenu.Account.SignedOut.Description.v131" = "Sincrunizà e parolle d’intesa, l’unghjette, è ancu di più"; + /* On the main menu, at the top, when the user is signed out. The title for the sign in action */ "MainMenu.Account.SignedOut.Title.v131" = "Cunnettesi"; +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state. */ +"MainMenu.Account.SyncError.Description.v131" = "Sincrunizazione messa in pausa"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state. */ +"MainMenu.Account.SyncError.Title.v131" = "Ricunnittitevi per lancià a sincrunizazione"; + +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Indette"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Scaricamenti"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Cronolugia"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Parolle d’intesa"; + /* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ "MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Indette"; @@ -13,6 +49,18 @@ /* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ "MainMenu.PanelLinkSection.Passwords.Title.v131" = "Parolle d’intesa"; +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "Persunalizà a pagina d’accolta"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Ottene aiutu"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Parametri"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "Novu in %@"; + /* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ "MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Persunalizà a pagina d’accolta"; @@ -22,6 +70,48 @@ /* On the main menu, the title for the action that will take the user to the Settings menu. */ "MainMenu.SettingsSection.Settings.Title.v131" = "Parametri"; +/* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.WhatsNew.Title.v131" = "Novu in %@"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Accolta"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "Aghjunghje à u screnu d’accolta"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "Accurtatoghju"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "Aghjunghje à l’accurtatoghji"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "Aghjunghje un’indetta"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Title.v132" = "Indettà sta pagina"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "Mudificà l’indetta"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "Caccià da a lista di lettura"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "Caccià da l’accurtatoghji"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Subtitle.v132" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Title.v132" = "Arregistrà cum’è PDF"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Subtitle.v132" = "Lista di lettura"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Title.v132" = "Arregistrà in a lista di lettura"; + /* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool. */ "MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131" = "Accolta"; @@ -58,6 +148,51 @@ /* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ "MainMenu.Submenus.Save.SaveToReadingList.Subtitle.v131" = "Lista di lettura"; +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.SaveToReadingList.Title.v131" = "Arregistrà in a lista di lettura"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Off.Title.v132" = "Disattivà u modu nutturnu"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.On.Title.v132" = "Attivà u modu nutturnu"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Subtitle.v132" = "Modu nutturnu"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Subtitle.v132" = "Stampà"; + +/* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "Stampà"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "Disattivà u modu di lettura"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "Attivà u modu di lettura"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "Modu di lettura"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Signalà"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Signalà prublemi cù stu situ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Sparte"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Sparte"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Ingrandamentu"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Ingrandamentu (%@)"; + /* On the main menu, the title for the action that will turn Night Mode off in the application. */ "MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Disattivà u modu nutturnu"; @@ -85,21 +220,69 @@ /* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool. */ "MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131" = "Signalà"; +/* On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131" = "Signalà prublemi cù stu situ"; + /* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool. */ "MainMenu.Submenus.Tools.Share.Subtitle.v131" = "Sparte"; /* On the main menu, the title for the action that will take the user to the Share module in the application. */ "MainMenu.Submenus.Tools.Share.Title.v131" = "Sparte"; +/* On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool. */ +"MainMenu.Submenus.Tools.Zoom.Subtitle.v131" = "Ingrandamentu"; + +/* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.Zoom.Title.v131" = "Ingrandamentu (%@)"; + +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Listinu principale"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Nova unghjetta privata"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "Nova unghjetta"; + /* On the main menu, the title for the action that will create a new private tab. */ "MainMenu.TabsSection.NewPrivateTab.Title.v131" = "Unghjetta privata nova"; /* On the main menu, the title for the action that will create a new, non-private, tab. */ "MainMenu.TabsSection.NewTab.Title.v131" = "Unghjetta nova"; +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "Circà in a pagina"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Arregistrà"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sottulistinu Arregistrà"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Passà à a versione per urdinatore"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "Passà à a versione mobile"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Attrezzi"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Sottulistinu Attrezzi"; + +/* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.FindInPage.Title.v131" = "Circà in a pagina…"; + /* On the main menu, the title for the action that will take the user to the Save submenu in the menu. */ "MainMenu.ToolsSection.SaveSubmenu.Title.v131" = "Arregistrà"; +/* On the main menu, the title for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.SwitchToDesktopSite.Title.v131" = "Passà à a versione per urdinatore"; + +/* On the main menu, the title for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.SwitchToMobileSite.Title.v131" = "Passà à a versione mobile"; + /* On the main menu, the title for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.ToolsSubmenu.Title.v131" = "Attrezzi"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings index f93949d32ab0c..0044ecb966333 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings @@ -37,6 +37,24 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ "Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "Logo %@"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Dissatisfied.v132" = "Infelice"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Neutral.v132" = "Indifferente"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.NotApplicable.v132" = "Ùn l’impiegu micca"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Satisfied.v132" = "Felice"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VeryDissatisfied.v132" = "Assai infelice"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VerySatisfied.v132" = "Assai felice"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ "Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ nant’à %2$@"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/PasswordGenerator.strings b/firefox-ios/Shared/Supporting Files/co.lproj/PasswordGenerator.strings new file mode 100644 index 0000000000000..9f009c594cef8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/PasswordGenerator.strings @@ -0,0 +1,27 @@ +/* Accessibility label describing a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.A11yLabel.v132" = "Generatore di parolla d’intesa"; + +/* Accessibility label describing the close button for the popup related to a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.CloseButtonA11ylabel.v132" = "Chjode"; + +/* When a user is in the process of creating an account, they have the option to generate a password. The user is capable of copying this password after long pressing the value of the password displayed to them. This string is the label of the copy button that appears after long pressing the password. */ +"PasswordGenerator.CopyPasswordButtonLabel.v132" = "Cupià"; + +/* Text displayed when a user interacts with the password field in a signup form, as part of a popup. This popup allows the user to generate a password that they have the option to use when signing up for an account. */ +"PasswordGenerator.Description.v132" = "Prutigite u vostru contu impieghendu una parolla d’intesa forte è ingenerata à l’azardu."; + +/* When a user is in the process of creating an account, they have the option to generate a password. The popup displaying the generated password to the user is available by clicking a keyboard accessory button with this label. */ +"PasswordGenerator.KeyboardAccessoryButtonLabel.v132" = "Impiegà una parolla d’intesa forte"; + +/* Prefix to alert accessibility users that a generated password (represented by %@) will be read to them next. */ +"PasswordGenerator.PasswordReadoutPrefaceA11y.v132" = "Parolle d’intesa ingenerata : %@"; + +/* Accessibility label describing a refresh password button belonging to a popup that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.RefreshPasswordButtonA11yLabel.v132" = "Ingenerà una nova parolla d’intesa forte"; + +/* Title text displayed as part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user -- clicking a button fills in the password of the signup form with this generated password. */ +"PasswordGenerator.Title.v132" = "Impiegà una parolla d’intesa forte ?"; + +/* Label of a button that is part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user and clicking this button fills in the password field of the signup form with this generated password. */ +"PasswordGenerator.UsePasswordButtonLabel.v132" = "Impiegà sta parolla d’intesa"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/co.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..f60d0df0d2abe --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Chjode"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Apre i parametri di ricerca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Parametri di ricerca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Sta volta, ricercà cù :"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Parametri di ricerca"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Sta volta, ricercà cù :"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings index c133a9e2ab6b6..89f61fec0d0af 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Mutore di ricerca predefinitu"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Sapene di più nant’à Firefox suggerisce"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Sapene di più nant’à Ecosia suggerisce"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Mutori di ricerca alternativi"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Affissà e suggestioni"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra d’indirizzu - Firefox suggerisce"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra d’indirizzu - Ecosia suggerisce"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Sapene di più nant’à Firefox suggerisce"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Sapene di più nant’à Ecosia suggerisce"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Affissà e suggestioni di Firefox Suggest in e sessioni di navigazione privata"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Affissà e suggestioni di Ecosia Suggest in e sessioni di navigazione privata"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Ricercà in a cronolugia di navigazione"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/co.lproj/ToolbarLocation.strings index 28455e34555d0..29b93a881edbd 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/ToolbarLocation.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/ToolbarLocation.strings @@ -1,6 +1,9 @@ /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ "ContextualHints.Toolbar.Bottom.Description.v107" = "S’è vo preferite, pudete dispiazzà a barra d’attrezzi insù."; +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "Picchichjà è mantene e fleccie per saltà trà e pagine in sta cronolugia d’unghjetta"; + /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ "ContextualHints.Toolbar.Top.Description.v107" = "S’è vo preferite, pudete dispiazzà a barra d’attrezzi inghjò."; diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..fc6dd07ddc973 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Poslední hodina"; + diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/MainMenu.strings index a1863a40fb381..82bdd59a6bf48 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Uložit"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Podnabídka uložení"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Přepnout na plnou verzi webu"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Nástroje"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podnabídka nástrojů"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Najít na stránce…"; diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings index bc16f1c8f97c9..662f5be286633 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Výchozí vyhledávač"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Dozvědět se více o návrzích Firefoxu"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Dozvědět se více o návrzích Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternativní vyhledávače"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Povolit našeptávání"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresní řádek - Návrhy Firefoxu"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresní řádek - Návrhy Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Dozvědět se více o návrzích Firefoxu"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Dozvědět se více o návrzích Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Zobrazovat Návrhy Firefoxu v anonymním prohlížení"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Zobrazovat Návrhy Ecosia v anonymním prohlížení"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Hledat v historii prohlížení"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..77ea1e91c7fef --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Yr Awr Ddiwethaf"; + diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/MainMenu.strings index a4c418a203666..c62d52679999f 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Cadw"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Cadw is-ddewislen"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Newid i wefan bwrdd gwaith"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Offer"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Is-ddewislen offer"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Canfod ar y Dudalen…"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings index b91970353a49f..a2ae3c470cb4b 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Peiriant Chwilio Rhagosodedig"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Darllen rhagor am Awgrymiadau Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Darllen rhagor am Awgrymiadau Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Peiriannau Chwilio Eraill"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Dangos Awgrymiadau Chwilio"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Bar cyfeiriad - Awgrymiadau Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Bar cyfeiriad - Awgrymiadau Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Darllen rhagor am Awgrymiadau Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Darllen rhagor am Awgrymiadau Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Dangos awgrymiadau gan Firefox Suggest mewn sesiynau preifat"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Dangos awgrymiadau gan Ecosia Suggest mewn sesiynau preifat"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Chwilio Hanes Pori"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/Share.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/Share.strings index 338a9f1132d50..770ea4fc2cee6 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/Share.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/Share.strings @@ -1,5 +1,5 @@ /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message.v119" = "Nid oes gennych unrhyw ddyfeisiau eraill wedi eu cysylltu i'r Cyfrif Firefox hwn i'w cydweddu nhw."; +"SendTo.NoDevicesFound.Message.v119" = "Nid oes gennych unrhyw ddyfeisiau eraill wedi eu cysylltu i'r Cyfrif Ecosia hwn i'w cydweddu nhw."; /* This message appears when a user tries to use 'Send Link to Device' action while not logged in */ "SendTo.NotSignedIn.Title.v119" = "Nid ydych wedi mewngofnodi i'ch cyfrif."; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/da.lproj/AddressToolbar.strings index 7d9e8ebc3451b..8c2184b3648fa 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Privatlivs- og sikkerhedsindstillinger"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Åbner valg af søgetjeneste"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Søgetjeneste: %@"; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/da.lproj/FxANotification.strings new file mode 100644 index 0000000000000..ba68e646ae013 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%2$@ faneblade lukket fra %1$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Vis nyligt lukkede faneblade"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/da.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..6e4b0b22e8390 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Den seneste time"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/da.lproj/MainMenu.strings index 9b834ff337cba..10d0b7874633d 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Gem"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Gem undermenu"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Skift til desktop-version"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Funktioner"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Undermenu for værktøj"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Find på side…"; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/da.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..c61f2c4e0711f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Luk"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Åbn indstillinger for søgning"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Søgeindstillinger"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Søg denne gang i:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Søgeindstillinger"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Søg denne gang i:"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings index 353ccfff463d7..eda1d56ed3c15 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardsøgetjeneste"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Læs mere om Firefox-forslag"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Læs mere om Ecosia-forslag"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative søgetjenester"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Vis søgeforslag"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adressefelt - Firefox-forslag"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adressefelt - Ecosia-forslag"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Læs mere om Firefox-forslag"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Læs mere om Ecosia-forslag"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vis forslag fra Firefox-forslag i private sessioner"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vis forslag fra Ecosia-forslag i private sessioner"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Søg i browserhistorik"; diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/de.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..8c0cdc90160b1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/de.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Die letzte Stunde"; + diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/de.lproj/MainMenu.strings index 4308091641d67..bfefcff8d6b35 100644 --- a/firefox-ios/Shared/Supporting Files/de.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/de.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Speichern"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Untermenü speichern"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Zur Website für Desktop-Browser wechseln"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Werkzeuge"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Untermenü „Werkzeuge“"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Seite durchsuchen…"; diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings index 464baee6a5770..eec37f6db483c 100644 --- a/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardsuchmaschine"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Weitere Infos zu Firefox-Vorschlägen"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Weitere Infos zu Ecosia-Vorschlägen"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative Suchmaschinen"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Suchvorschläge anzeigen"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adressleiste – Firefox-Vorschläge"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adressleiste – Ecosia-Vorschläge"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Weitere Infos zu Firefox-Vorschlägen"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Weitere Infos zu Ecosia-Vorschlägen"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vorschläge von Firefox Suggest in privaten Sitzungen anzeigen"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vorschläge von Ecosia Suggest in privaten Sitzungen anzeigen"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Browser-Chronik durchsuchen"; diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..c6279dfcc65bd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Zachadna góźina"; + diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/MainMenu.strings index bf373834b67e2..c95ebb263a01b 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Składowaś"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Pódmeni składowaś"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "K websedłoju za desktop"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Rědy"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Pódmeni Rědy"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Na boku pytaś…"; diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/Settings.strings index 230d492647d9d..131f8b0331ff7 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardna pytnica"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Zgóńśo wěcej wó Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Zgóńśo wěcej wó Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatiwne pytnice"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Pytańske naraźenja pokazaś"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresowe pólo – Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresowe pólo – Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Zgóńśo wěcej wó Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Zgóńśo wěcej wó Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Naraźenja z Firefox Suggest w priwatnych pósejźenjach pokazaś"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Naraźenja z Ecosia Suggest w priwatnych pósejźenjach pokazaś"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Pśeglědowańsku historiju pśepytaś"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/el.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..890cd255db8e2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/el.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Τελευταία ώρα"; + diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/el.lproj/MainMenu.strings index 52a2b0147be14..098fa0920dc6e 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Αποθήκευση"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Υπομενού αποθήκευσης"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Εναλλαγή σε έκδοση για υπολογιστές"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Εργαλεία"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Υπομενού εργαλείων"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Εύρεση στη σελίδα…"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings index 85c55b7140b88..9e4edf58759d7 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Προεπιλεγμένη μηχανή αναζήτησης"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Μάθετε περισσότερα για τις Προτάσεις Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Μάθετε περισσότερα για τις Προτάσεις Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Εναλλακτικές μηχανές αναζήτησης"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Εμφάνιση προτάσεων αναζήτησης"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Γραμμή διευθύνσεων - Προτάσεις Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Γραμμή διευθύνσεων - Προτάσεις Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Μάθετε περισσότερα για τις Προτάσεις Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Μάθετε περισσότερα για τις Προτάσεις Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Εμφάνιση προτάσεων από τις Προτάσεις Firefox σε ιδιωτικές συνεδρίες"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Εμφάνιση προτάσεων από τις Προτάσεις Ecosia σε ιδιωτικές συνεδρίες"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Αναζήτηση ιστορικού περιήγησης"; diff --git a/firefox-ios/Shared/Supporting Files/en-CA.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/en-CA.lproj/Settings.strings index 9e6dd70facb6a..d8b69fc49843a 100644 --- a/firefox-ios/Shared/Supporting Files/en-CA.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/en-CA.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Default Search Engine"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Learn more about Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Learn more about Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative Search Engines"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Show Search Suggestions"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Address bar - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Address bar - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Learn more about Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Learn more about Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Show suggestions from Firefox Suggest in private sessions"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Show suggestions from Ecosia Suggest in private sessions"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Search Browsing History"; diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..83a8994a81581 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Last Hour"; + diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/MainMenu.strings index c1c85378100e0..bc414f48ad3d6 100644 --- a/firefox-ios/Shared/Supporting Files/en-GB.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Save"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Save submenu"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Switch to desktop site"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Tools"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Tools submenu"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Find in Page…"; diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings index cb1c111955d70..b74509d55488b 100644 --- a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Default Search Engine"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Learn more about Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Learn more about Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative Search Engines"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Show Search Suggestions"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Address bar - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Address bar - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Learn more about Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Learn more about Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Show suggestions from Firefox Suggest in private sessions"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Show suggestions from Ecosia Suggest in private sessions"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Search Browsing History"; diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..83a8994a81581 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Last Hour"; + diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/MainMenu.strings index dda724484cf15..47c27770cb699 100644 --- a/firefox-ios/Shared/Supporting Files/en-US.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Save"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Save submenu"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Switch to desktop site"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Tools"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Tools submenu"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Find in Page…"; diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings index 9e6dd70facb6a..d8b69fc49843a 100644 --- a/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Default Search Engine"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Learn more about Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Learn more about Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative Search Engines"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Show Search Suggestions"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Address bar - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Address bar - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Learn more about Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Learn more about Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Show suggestions from Firefox Suggest in private sessions"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Show suggestions from Ecosia Suggest in private sessions"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Search Browsing History"; diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings index 2b08faf8dd7d1..8ff763f48a2b1 100644 --- a/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Norma serĉilo"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Pli da informo pri Sugestoj de Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Pli da informo pri Sugestoj de Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Aliaj serĉiloj"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Montri sugestojn de serĉilo"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresa strio - Sugestoj de Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresa strio - Sugestoj de Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Pli da informo pri Sugestoj de Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Pli da informo pri Sugestoj de Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Montri sugestojn de Firefox Suggest en privataj seancoj"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Montri sugestojn de Ecosia Suggest en privataj seancoj"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Serĉi en retuma historio"; diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..3ec6e063bde95 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "la última hora"; + diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/MainMenu.strings index a700e3f7d39b7..98066cea3ee79 100644 --- a/firefox-ios/Shared/Supporting Files/es-AR.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Guardar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Submenú Guardar"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Cambiar al sitio de escritorio"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Herramientas"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Submenú Herramientas"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Buscar en la página…"; diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings index 9ce09a106ffdd..a680f8d349f68 100644 --- a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Buscador predeterminado"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Más información sobre Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Más información sobre Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Buscadores alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostrar sugerencias de búsqueda"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Conocer más sobre Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Conocer más sobre Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Firefox Suggest en sesiones privadas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Ecosia Suggest en sesiones privadas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Buscar historial de navegación"; diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..e0b301a270585 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Última hora"; + diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/MainMenu.strings index 3002de945e013..f8f4d0d993c8f 100644 --- a/firefox-ios/Shared/Supporting Files/es-CL.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Guardar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Submenú de guardado"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Cambiar al sitio de escritorio"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Herramientas"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Submenú de herramientas"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Buscar en la página…"; diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings index a129aedc8e730..48542834c4c59 100644 --- a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motor de búsqueda predeterminado"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Aprende más sobre Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Aprende más sobre Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motores de búsqueda alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostrar sugerencias de búsqueda"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Sugerencias de Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Sugerencias de Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Aprende más sobre Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Aprende más sobre Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Firefox Suggest en sesiones privadas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Ecosia Suggest en sesiones privadas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Buscar historial de navegación"; diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings index 3826c7e908dc8..6b778b6fe5bf8 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motor de búsqueda predeterminado"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saber más sobre Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saber más sobre Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motores de búsqueda alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostrar sugerencias de búsqueda"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Sugerencias de Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Sugerencias de Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saber más sobre Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saber más sobre Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Firefox Sugerir en sesiones privadas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Ecosia Sugerir en sesiones privadas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Buscar historial de navegación"; diff --git a/firefox-ios/Shared/Supporting Files/es.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/es.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..e0b301a270585 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Última hora"; + diff --git a/firefox-ios/Shared/Supporting Files/es.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/es.lproj/MainMenu.strings index 31bc4266ca873..7551af1f4e488 100644 --- a/firefox-ios/Shared/Supporting Files/es.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/es.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Guardar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Submenú Guardar"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Cambiar al sitio de escritorio"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Herramientas"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Submenú Herramientas"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Buscar en la página…"; diff --git a/firefox-ios/Shared/Supporting Files/es.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es.lproj/Settings.strings index 4fdca071d6eaf..d331d562e0609 100644 --- a/firefox-ios/Shared/Supporting Files/es.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Buscador predeterminado"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saber más sobre Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saber más sobre Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Buscadores alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostrar sugerencias de búsqueda"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de direcciones - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saber más sobre Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saber más sobre Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Firefox Suggest en sesiones privadas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugerencias de Ecosia Suggest en sesiones privadas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Buscar historial de navegación"; diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..1919ee5243727 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Azken ordua"; + diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/MainMenu.strings index 3d2f52d51dfb6..fa06e82d1eac3 100644 --- a/firefox-ios/Shared/Supporting Files/eu.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Gorde"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Gordetzeko azpimenua"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Aldatu mahaigaineko gunera"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Tresnak"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Tresnen azpimenua"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Bilatu orrian…"; diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/Settings.strings index b18e24f8abbfb..825543c9e57c3 100644 --- a/firefox-ios/Shared/Supporting Files/eu.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Bilaketa-motor lehenetsia"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox Suggest-i buruzko argibide gehiago"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia Suggest-i buruzko argibide gehiago"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Bilaketa-motor alternatiboak"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Erakutsi bilaketa-iradokizunak"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Helbide-barra - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Helbide-barra - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox Suggest-i buruzko argibide gehiago"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia Suggest-i buruzko argibide gehiago"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Erakutsi Firefox Suggest-en iradokizunak saio pribatuetan"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Erakutsi Ecosia Suggest-en iradokizunak saio pribatuetan"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Bilatu nabigatze-historia"; diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings index 05680b2ecc8a6..f499217d83e6b 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings @@ -55,12 +55,24 @@ /* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ "Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefektuuri"; +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Maakunta"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Osavaltio"; + /* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ "Addresses.EditAddress.AutofillAddressSuburb.v129" = "Lähiö"; /* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ "Addresses.EditAddress.AutofillAddressTel.v129" = "Puhelin"; +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Alue"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Kylä tai kaupunki"; + /* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ "Addresses.EditAddress.AutofillAddressZip.v129" = "Postinumero"; diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/FxANotification.strings new file mode 100644 index 0000000000000..844f081c0e37c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@-välilehtiä suljettu: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Näytä viimeksi suljetut välilehdet"; + diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..be95d3f4c01c7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Viimeinen tunti"; + diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/SearchEngineSelection.strings index 574d2c5a70bfe..b6b1f4a0e696f 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/SearchEngineSelection.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/SearchEngineSelection.strings @@ -4,3 +4,15 @@ /* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ "UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Avaa hakuasetukset"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Hakuasetukset"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Tällä kertaa hae:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Hakuasetukset"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Tällä kertaa hae:"; + diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings index 44c456932d26f..76fd46481bbea 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Oletushakukone"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Lisätietoja Firefox Suggestista"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Lisätietoja Ecosia Suggestista"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Vaihtoehtoiset hakukoneet"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Näytä hakuehdotukset"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Osoitepalkki - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Osoitepalkki - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Lisätietoja Firefox Suggestista"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Lisätietoja Ecosia Suggestista"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Näytä Firefox Suggestin ehdotukset yksityisissä istunnoissa"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Näytä Ecosia Suggestin ehdotukset yksityisissä istunnoissa"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Etsi selaushistoriasta"; diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..be96c925bd773 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "La dernière heure"; + diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/MainMenu.strings index 36d588e5e2eed..165e0f87df46f 100644 --- a/firefox-ios/Shared/Supporting Files/fr.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Enregistrer"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sous-menu Enregistrer"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Passer en version pour ordinateur"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Outils"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Sous-menu Outils"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Rechercher dans la page…"; diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings index cba0f44989120..74d76b5e3c1fd 100644 --- a/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Moteur de recherche par défaut"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "En savoir plus sur Firefox suggère"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "En savoir plus sur Ecosia suggère"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Moteurs de recherche alternatifs"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Afficher les suggestions"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barre d’adresse - Firefox suggère"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barre d’adresse - Ecosia suggère"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "En savoir plus sur Firefox suggère"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "En savoir plus sur Ecosia suggère"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Afficher les suggestions de Firefox Suggest dans les sessions privées"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Afficher les suggestions de Ecosia Suggest dans les sessions privées"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Rechercher dans l’historique de navigation"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/AddressToolbar.strings index b2de82c8431af..28233d87b7a5e 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Roghainnean prìobhaideachd ⁊ tèarainteachd"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Fosglaidh seo taghadh an einnsein-luirg"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "An t-einnsean-luirg: %@"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/CredentialProvider.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/CredentialProvider.strings index 384cde87574e0..e67260013216d 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/CredentialProvider.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/CredentialProvider.strings @@ -1,3 +1,6 @@ +/* Label shown when there are no logins to list. The placeholder will be replaced with the app name. */ +"LoginsList.NoLoginsFound.Description.v122" = "Nochdaidh na faclan-faire a shàbhaileas tu no a shioncronaicheas tu ri %@ an-seo. Crioptaichidh sinn gach facal-faire a shàbhaileas tu."; + /* Label shown when there are no logins saved in the passwords list */ "LoginsList.NoLoginsFound.Title.v122" = "Cha deach facal-faire sam bith a shàbhaladh"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Credentials.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Credentials.strings index d336c126a27f9..170c5c0f712bb 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/Credentials.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Credentials.strings @@ -1,3 +1,6 @@ +/* Message shown when you enter Logins & Passwords without having a device passcode set. */ +"Logins.DevicePasscodeRequired.Message.v122" = "Airson faclan-faire a shàbhaladh is a lìonadh gu fèin-obrachail, cuir ID aodainn, ID suathaidh no còd-faire uidheim an comas."; + /* Message shown when you enter Payment Methods without having a device passcode set. */ "Logins.PaymentMethods.DevicePasscodeRequired.Message.v124.v2" = "Airson cairtean-creideis a shàbhaladh is a lìonadh gu fèin-obrachail, cuir ID aodainn, ID suathaidh no còd-faire uidheim an comas."; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/EditAddress.strings index be1d024df2ef3..d7b010fa43a05 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/EditAddress.strings @@ -1,6 +1,9 @@ /* Title for the cancel button in the remove address alert. */ "Addresses.EditAddress.Alert.CancelButton.v129" = "Sguir dheth"; +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "Thèid an seòladh a thoirt air falbh o gach uidheam agad a nì sioncronachadh."; + /* Title for the remove button in the remove address alert. */ "Addresses.EditAddress.Alert.RemoveButton.v129" = "Thoir air falbh"; @@ -22,3 +25,120 @@ /* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ "Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Dùthaich no roinn-dùthcha"; +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Siorrachd"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Roinn"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Sgìre"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Éirchód"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Post-d"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Iomarat"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Eilean"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Ainm"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Coimhearsnachd"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Oblast"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Buidheann"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Sgìre"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Còd-puist"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Baile puist"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Maorsainneachd"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Siorrachd/Còigeamh"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Stàit"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Frith-bhaile"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Fòn"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Baile-fearainn"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Baile"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Còd ZIP"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Sguir dheth"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Deasaich an seòladh"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Seòladh sràide"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Sàbhail"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Seall an seòladh"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Dùin"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Deasaich"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Thoir air falbh an seòladh"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Chaidh an seòladh a thoirt air falbh"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Chaidh an seòladh a shàbhaladh"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Cha b’ urrainn dhuinn an seòladh a shàbhaladh"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Cha b’ urrainn dhuinn an seòladh a thoirt air falbh"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Feuch ris a-rithist"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Chaidh fiosrachadh an t-seòlaidh ùrachadh"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v132.v2" = "Chaidh an seòladh a shàbhaladh"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/EditCard.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/EditCard.strings index 8ae61db56e0fa..38d54656ff4b6 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/EditCard.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/EditCard.strings @@ -43,6 +43,9 @@ /* Button label for saving the credit card details user entered in the form */ "CreditCard.EditCard.SaveNavBarButtonLabel.v113" = "Sàbhail"; +/* Title label for user to use the toggle settings to allow saving and autofilling of credit cards for webpages. */ +"CreditCard.EditCard.ToggleToAllowAutofillTitle.v122" = "Sàbhail is lìon dòighean pàighidh"; + /* Title label for the view where user can view their credit card info */ "CreditCard.EditCard.ViewCreditCardTitle.v116" = "Seall a’ chairt"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/EnhancedTrackingProtection.strings index 44c81444a7a69..f434deb72d56c 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/EnhancedTrackingProtection.strings @@ -37,6 +37,9 @@ /* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "Falamhaich"; +/* The text for the clear cookies and site data alert inside the enhanced tracking protection screen. The placeholder will be replaced with the user's currently visited website */ +"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "Ma bheir thu air falbh briosgaidean is dàta na làraich airson %@, dh’fhaoidte gun dèid do chlàradh a-mach à làraichean-lìn agus gun dèid cairtean seopadaireachd fhalamhachadh."; + /* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "Falamhaich na briosgaidean is dàta na làraich"; @@ -46,6 +49,9 @@ /* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ "Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "Chaidh na briosgaidean is dàta nan làraichean a thoirt air falbh"; +/* The accessibility label for the close button in the Enhanced Tracking protection menu. */ +"Menu.EnhancedTrackingProtection.CloseButton.AccessibilityLabel.v132" = "Dùin clàr-taice na prìobhaideachd is tèarainteachd"; + /* Text to let users know that the current website is secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "Ceangal tèarainte"; @@ -76,6 +82,9 @@ /* The title for the button that allows users to view certificates inside the enhanced tracking protection details screen. */ "Menu.EnhancedTrackingProtection.Details.ViewCertificatesTitle.v131" = "Seall an teisteanas"; +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "Chan eil %@ ri caithris. Mholamaid dhut an dìon a chur air a-rithist."; + /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Chuir thu an dìon dheth"; @@ -88,9 +97,21 @@ /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Bi faiceallach air an làrach seo"; +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "Tha %@ ri caithris"; + /* The title for the privacy settings button inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "Roghainnean prìobhaideachd"; /* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.Switch.Title.v128" = "Dìon adhartach o thracadh"; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Tha an dìon dheth. Mholamaid dhut a chur air a-rithist."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Tha an dìon dheth. Mholamaid dhut an cur air a-rithist."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Tha coltas gu bheil rudeigin briste air an làrach seo. Feuch ris a chur dheth."; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxHomepage.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxHomepage.strings new file mode 100644 index 0000000000000..48a573492250d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxHomepage.strings @@ -0,0 +1,12 @@ +/* When the user ends their private session, they are returned to the private mode homepage, and a toastbar popups confirming that their data has been erased. This is the label for that toast. */ +"FirefoxHomepage.FeltDeletion.Link.v122" = "Chaidh dàta a’ bhrabhsaidh phrìobhaidich a shuathadh bàn"; + +/* The body of the message for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. Placeholder refers to app name. */ +"FirefoxHomepage.FeltPrivacyUI.Body.v122" = "Sguabaidh %@ às na briosgaidean, an eachdraidh is dàta nan làraichean nuair a dhùineas tu an uinneag phrìobhaideach mu dheireadh."; + +/* The link for the card that educates users about how private mode works. The link redirects to an external site for more information. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Link.v122" = "Cò chì mo ghnìomhachd?"; + +/* The title for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Title.v122" = "Na fàg lorg sam bith air an uidheam seo"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxLogins.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxLogins.strings index e952b3830e391..221c0b586da3f 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxLogins.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxLogins.strings @@ -10,3 +10,9 @@ /* Prompt for updating a password in the Update Password prompt. */ "LoginsHelper.PromptUpdateLogin.Title.TwoArg.v122" = "A bheil thu airson am facal-faire ùrachadh?"; +/* Placeholder text for search box in logins list view. */ +"LoginsList.LoginsListSearchPlaceholder.v122" = "Lorg sna faclan-faire"; + +/* Title for the list of logins saved by the app */ +"LoginsList.Title.v122" = "Faclan-faire a shàbhail thu"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxSync.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxSync.strings index bec2b7519f27c..848fb7eebc66b 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxSync.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/FirefoxSync.strings @@ -1,3 +1,9 @@ +/* Toggle for address autofill syncing setting */ +"FirefoxSync.AddressAutofillEngine.v124" = "Seòlaidhean"; + /* Toggle for credit cards syncing setting */ -"FirefoxSync.CreditCardsEngine.v115" = "Cairtean-creideis"; +"FirefoxSync.CreditCardsEngine.v122" = "Dòighean pàighidh"; + +/* Toggle passwords syncing setting, in the Settings > Sync Data menu of the app. */ +"Sync.LoginsEngine.Title.v122" = "Faclan-faire"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Footer.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Footer.strings index 463caebeff9d8..0f89f8483f8b5 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/Footer.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Footer.strings @@ -1,3 +1,6 @@ /* This is the learn more text of the Pocket footer on Firefox Homepage. */ "FirefoxHomepage.Pocket.Footer.LearnMore.v115" = "Barrachd fiosrachaidh"; +/* This is the title of the Pocket footer on Firefox Homepage. The first placeholder is for the Pocket app name and the second placeholder for the app name */ +"FirefoxHomepage.Pocket.Footer.Title.v116" = "Le cumhachd %1$@. Pàirt de theaghlach %2$@."; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/FxANotification.strings new file mode 100644 index 0000000000000..7c7c10896ca77 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Chaidh tabaichean, %1$@ dhiubh, a dhùnadh: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Seall tabaichean a dhùin thu o chionn goirid"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/KeyboardAccessory.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/KeyboardAccessory.strings new file mode 100644 index 0000000000000..5e59145ff0b90 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/KeyboardAccessory.strings @@ -0,0 +1,6 @@ +/* Accessibility label for next button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.NextButton.Accessibility.Label.v124" = "Ath-raon an fhoirm"; + +/* Accessibility label for previous button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.PreviousButton.Accessibility.Label.v124" = "Raon roimhe an fhoirm"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/LoginsHelper.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/LoginsHelper.strings new file mode 100644 index 0000000000000..39f868a20ea67 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/LoginsHelper.strings @@ -0,0 +1,9 @@ +/* Button to not save the user's password in the logins helper */ +"LoginsHelper.DontSave.Button.v122" = "Chan ann an-dràsta"; + +/* Button to not update the user's password in the logins helper */ +"LoginsHelper.DontUpdate.Button.v122" = "Chan ann an-dràsta"; + +/* Button to save the user's password */ +"LoginsHelper.SaveLogin.Button.v122" = "Sàbhail"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/MainMenu.strings index 0de41b0b8a89d..d40d4a15abf49 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/MainMenu.strings @@ -1,3 +1,90 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "Faigh lorg air na tha a dhìth ort nas luaithe, eadar brabhsadh prìobhaideach is gnìomhan sàbhalaidh."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "Ùr: clàr-taice nas taine"; + +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Air ais"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Dùin"; + +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "Clàraich a-steach a shioncronachadh fhaclan-faire, thabaichean is mòran a bharrachd"; + +/* On the main menu, at the top, when the user is signed out. The description for the sign in action */ +"MainMenu.Account.SignedOut.Description.v131" = "Sioncronaich faclan-faire, tabaichean is mòran a bharrachd"; + +/* On the main menu, at the top, when the user is signed out. The title for the sign in action */ +"MainMenu.Account.SignedOut.Title.v131" = "Clàraich a-steach"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state. */ +"MainMenu.Account.SyncError.Description.v131" = "Tha an sioncronachadh ’na stad"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state. */ +"MainMenu.Account.SyncError.Title.v131" = "Clàraich a-steach a-rithist a dhèanamh sioncronachadh"; + +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Comharran-lìn"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Luchdaidhean a-nuas"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Eachdraidh"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Faclan-faire"; + +/* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Comharran-lìn"; + +/* On the main menu, the title for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.Downloads.Title.v131" = "Luchdaidhean a-nuas"; + +/* On the main menu, the title for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.History.Title.v131" = "Eachdraidh"; + +/* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.Passwords.Title.v131" = "Faclan-faire"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "Gnàthaich an duilleag-dhachaigh"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Faigh cobhair"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Roghainnean"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "Ùr ann an %@"; + +/* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ +"MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Gnàthaich an duilleag-dhachaigh"; + +/* On the main menu, the title for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.GetHelp.Title.v131" = "Faigh cobhair"; + +/* On the main menu, the title for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.Settings.Title.v131" = "Roghainnean"; + +/* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.WhatsNew.Title.v131" = "Ùr ann an %@"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Dhachaigh"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "Cuir ris an sgrìn mhòr"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "Ath-ghoirid"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "Cuir ris na h-ath-ghoiridean"; + /* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ "MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "Cuir comharra-lìn ris"; @@ -79,3 +166,117 @@ /* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ "MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "Clò-bhuail"; +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "Cuir an sealladh leughaidh dheth"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "Cuir an sealladh leughaidh air"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "An sealladh leughaidh"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Dèan aithris"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Dèan aithris air làrach bhriste"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Co-roinn"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Co-roinn"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Sùm"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Sùm (%@)"; + +/* On the main menu, the title for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Cuir am modh oidhche dheth"; + +/* On the main menu, the title for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.NightMode.On.Title.v131" = "Cuir am modh oidhche air"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.NightMode.Subtitle.v131" = "Am modh oidhche"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.Print.Subtitle.v131" = "Clò-bhuail"; + +/* On the main menu, the title for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.Print.Title.v131" = "Clò-bhuail"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.Off.Title.v131" = "Cuir an sealladh leughaidh dheth"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.On.Title.v131" = "Cuir an sealladh leughaidh air"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.ReaderView.Subtitle.v131" = "An sealladh leughaidh"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131" = "Dèan aithris"; + +/* On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131" = "Dèan aithris air làrach bhriste"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool. */ +"MainMenu.Submenus.Tools.Share.Subtitle.v131" = "Co-roinn"; + +/* On the main menu, the title for the action that will take the user to the Share module in the application. */ +"MainMenu.Submenus.Tools.Share.Title.v131" = "Co-roinn"; + +/* On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool. */ +"MainMenu.Submenus.Tools.Zoom.Subtitle.v131" = "Sùm"; + +/* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.Zoom.Title.v131" = "Sùm (%@)"; + +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Am prìomh-chlàr-taice"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Taba prìobhaideach ùr"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "Taba ùr"; + +/* On the main menu, the title for the action that will create a new private tab. */ +"MainMenu.TabsSection.NewPrivateTab.Title.v131" = "Taba prìobhaideach ùr"; + +/* On the main menu, the title for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.NewTab.Title.v131" = "Taba ùr"; + +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "Lorg air an duilleag"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Sàbhail"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Seall an làrach desktop"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "Seall an làrach mobile"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Innealan"; + +/* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.FindInPage.Title.v131" = "Lorg air an duilleag…"; + +/* On the main menu, the title for the action that will take the user to the Save submenu in the menu. */ +"MainMenu.ToolsSection.SaveSubmenu.Title.v131" = "Sàbhail"; + +/* On the main menu, the title for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.SwitchToDesktopSite.Title.v131" = "Seall an làrach desktop"; + +/* On the main menu, the title for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.SwitchToMobileSite.Title.v131" = "Seall an làrach mobile"; + +/* On the main menu, the title for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.ToolsSubmenu.Title.v131" = "Innealan"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Microsurvey.strings new file mode 100644 index 0000000000000..40b23a53fb608 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Microsurvey.strings @@ -0,0 +1,72 @@ +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the button that appears on the prompt that allows the user to tap on and navigates them to the microsurvey to respond to. */ +"Microsurvey.Prompt.Button.v127" = "Lean air adhart"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.AccessibilityLabel.v127" = "Dùin brodadh na suirbhidh"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "Dùin"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the logo image that appears on the prompt to inform the prompt is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Prompt.LogoImage.AccessibilityLabel.v129" = "Suaicheantas %@"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the text that appears on the prompt to inform the user that this is a prompt to take a survey. Placeholder is for the app name. */ +"Microsurvey.Prompt.TitleLabel.v127" = "Cuidich leinn a’ leasachadh %@. Cha doir e ach mionaid."; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of button on the survey that a user can tap on to submit their responses. */ +"Microsurvey.Survey.Button.v127" = "Cuir a-null"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.AccessibilityLabel.v127" = "Dùin an t-suirbhidh"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.Button.AccessibilityLabel.v127" = "Dùin"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the text shown on the confirmation page when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.ConfirmationLabel.v127" = "Mòran taing airson do bheachdan a chur thugainn! "; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the title for the header on the microsurvey when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.HeaderLabel.v127" = "Tha an t-suirbhidh air a choileanadh"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v127" = "Coilean an t-suirbhidh"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Nach coilean thu an t-suirbhidh?"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "Suaicheantas %@"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Dissatisfied.v132" = "Mì-riaraichte"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Neutral.v132" = "Neodrach"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.NotApplicable.v132" = "Cha chleachd mi e"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Satisfied.v132" = "Riaraichte"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VeryDissatisfied.v132" = "Glè mhì-riaraichte"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VerySatisfied.v132" = "Glè riaraichte"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ à %2$@"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ +"Microsurvey.Survey.PrivacyPolicyLink.v127" = "Aithris na prìobhaideachd"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "Air a thaghadh"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Gun taghadh"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Suirbhidh"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/NativeErrorPage.strings new file mode 100644 index 0000000000000..bd1cc568c493c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Ath-luchdaich"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Thachair mearachd SSL agus chan urrainn dhuinn ceangal tèarainte a dhèanamh ris an fhrithealaiche."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Bidh faiceallach. Tha rud-eigin an-seo nach eil ceart."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Feuch is dèan ceangal air uidheam eile. Thoir sùil air a’ mhòdam no rùtar agad. Dì-cheangail on WiFi is ceangail ris as ùr."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Tha coltas gu bheil duilgheadas ann leis a’ cheangal agad ris an eadar-lìon."; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Onboarding.strings index e207da1b0fc0e..f36a1cb8626db 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Onboarding.strings @@ -1,6 +1,9 @@ /* The title of the button on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ "DefaultBrowserPopup.ButtonTitle.v114" = "Tadhail air na roghainnean"; +/* The footer label on the Default Browser Popup, which is below all the instructions asking the users if their Firefox browser is the default browser. If it is then close this message and tap skip. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.DescriptionFooter.v124" = "*An e %@ am brabhsair bunaiteach agad mu thràth?* Dùin an teachdaireachd seo is thoir gnogag air “Leum seachad air”."; + /* The first label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ "DefaultBrowserPopup.FirstLabel.v114" = "1. Tadhail air na *roghainnean*"; @@ -13,6 +16,57 @@ /* The title on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ "DefaultBrowserPopup.Title.v114" = "Leum dhan bhrabhsair bhunaiteach agad"; +/* String used to describe the option to continue to the next onboarding card in Firefox Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Continue.Action.v123" = "Gnàthaich %@"; + +/* String used to describe the description label of the customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Intro.Description.v123" = "Cuir an t-ùrlar is bàr-inneal agad a-rèir do nòs brabhsaidh fhèin."; + +/* String used to describe the option to skip the customization cards in Firefox Onboarding screens and start browsing. */ +"Onboarding.Customization.Intro.Skip.Action.v123" = "Tòisich air brabhsadh"; + +/* String used to describe the title of the customization onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Title.v123" = "Bheir %@ smachd dhut"; + +/* String used to describe the option to save the user setting and continue to the next onboarding in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Continue.Action.v123" = "Sàbhail is lean air adhart"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to dark theme from the available choices. */ +"Onboarding.Customization.Theme.Dark.Action.v123" = "Dorcha"; + +/* String used to describe the description label of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Description.v123" = "An lìon air do nòs fhèin."; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to light theme from the available choices. */ +"Onboarding.Customization.Theme.Light.Action.v123" = "Soilleir"; + +/* String used to describe the option to skip the theme customization in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Skip.Action.v123" = "Leum seachad"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to system theme from the available choices. */ +"Onboarding.Customization.Theme.System.Action.v123" = "Roghainn an t-siostaim"; + +/* String used to describe the title of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Title.v123" = "Tagh ùrlar"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the bottom of the screen. */ +"Onboarding.Customization.Toolbar.Bottom.Action.v123" = "Bonn"; + +/* String used to describe the option to save set preferences and leave onboarding to start browsing in the app. */ +"Onboarding.Customization.Toolbar.Continue.Action.v123" = "Sàbhail is tòisich air brabhsadh"; + +/* String used to describe the description label of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Description.v123" = "Cùm na lorg thu deas ri do làimh."; + +/* String used to describe the option to skip the toolbar customization in Firefox Onboarding screens and start browisg in the app. */ +"Onboarding.Customization.Toolbar.Skip.Action.v123" = "Leum seachad"; + +/* String used to describe the title of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Title.v123" = "Tagh àite dhan bhàr-inneal"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the top of the screen. */ +"Onboarding.Customization.Toolbar.Top.Action.v123" = "Bàrr"; + /* String used to describes what Firefox is on the first onboarding page in our Onboarding screens. Indie means small independant. */ "Onboarding.IntroDescriptionPart1.v114" = "Neo-eisimeileach, chan ann a chùm prothaid ach math chàch."; @@ -25,18 +79,33 @@ /* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.Continue.Action.v114" = "Lean air adhart"; +/* String used to describe the description of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Description.v120" = "Cuir tabaichean eadar na h-uidheaman agad air dòigh thèarainte is fidir an còrr dhen na gleusan prìobhaideachd aig %@."; + /* String used to describe the option to skip the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.Skip.Action.v115" = "Leum seachad"; +/* String used to describe the title of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Title.v120" = "Cumaidh brathan %@ rudan nas sàbhailte dhut"; + /* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.TurnOnNotifications.Action.v114" = "Cuir na brathan air"; +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v120" = "Dèan clàradh a-steach is sioncronachadh is bidh thu nas sàbhailte. Crioptaichidh %@ na faclan-faire, comharran-lìn is mòran a bharrachd dhut."; + +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v123" = "Crioptaichidh %@ na faclan-faire, comharran-lìn is mòran a bharrachd dhut is tu ’ga shioncronachadh."; + /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.SignIn.Action.v114" = "Clàraich a-steach"; /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.Skip.Action.v114" = "Leum seachad"; +/* String used to describes the title of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. */ +"Onboarding.Sync.Title.v120" = "Cùm rudan crioptaichte is tu a’ gluasad o uidheam gu uidheam"; + /* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ "Onboarding.Wallpaper.Accessibility.Classic.v114" = "Am pàipear-balla clasaigeach"; @@ -61,12 +130,24 @@ /* Describes the action on the first onboarding page in our Onboarding screen. This indicates that the user will set their default browser to Firefox. */ "Onboarding.Welcome.ActionTreatementA.v114" = "Suidhich mar am brabhsair bunaiteach"; +/* Accessibility label for close button that dismisses the welcome onboarding screen. Placeholder is for the app name. */ +"Onboarding.Welcome.Close.AccessibilityLabel.v121" = "Dùin is fàg bòrdachadh %@"; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Description.TreatementA.v120" = "Cuiridh am brabhsair againn - fear nach eil airson prothaid - casg air companaidhean o bhith gad leantainn gu dìomhair air feadh an lìn."; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Welcome.Description.v120" = "Cuiridh am brabhsair againn - fear nach eil airson prothaid - casg air companaidhean o bhith gad leantainn gu dìomhair air feadh an lìn."; + /* String used to describe the title of link button is on the welcome onboarding page for current version in our Onboarding screens. */ "Onboarding.Welcome.Link.Action.v114" = "Faigh barrachd fiosrachaidh ann an aithris na prìobhaideachd againn"; /* Describes the action on the first onboarding page in our Onboarding screen. This string will be on a button so user can skip this onboarding card. */ "Onboarding.Welcome.Skip.v114" = "Leum seachad"; +/* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Title.TreatementA.v120" = "Tha e a’ toirt tlachd dhuinn do chumail sàbhailte"; + /* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ "Onboarding.Welcome.Title.v114" = "Fàilte gu eadar-lìon neo-eisimeileach"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordAutofill.strings new file mode 100644 index 0000000000000..ba6ebafea18c2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordAutofill.strings @@ -0,0 +1,15 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(gun ainm-cleachdaiche)"; + +/* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ +"PasswordAutofill.ManagePasswordsButton.v124" = "Stiùirich na faclan-faire"; + +/* This phrase is used as a subtitle in the header of password list screen, indicating to the user that they will be logging into a specific website (represented by %@) using a saved password. It's providing clarity on which website the saved credentials apply to. */ +"PasswordAutofill.SignInWithSavedPassword.v124" = "Clàraichidh tu a-steach gu %@"; + +/* This label is used in the password list screen header as a question, prompting the user if they want to use a saved password for logging in. */ +"PasswordAutofill.UseSavedPasswordFromHeader.v124" = "A bheil thu airson am facal-faire a shàbhail thu a chleachdadh?"; + +/* Displayed inside the keyboard hint when a user is entering their login credentials and has at least one saved password. Indicates that there are stored passwords available for use in filling out the login form. */ +"PasswordAutofill.UseSavedPasswordFromKeyboard.v124" = "Cleachd am facal-faire a shàbhail thu"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordGenerator.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordGenerator.strings index 4a0d183af2416..8a2625806124d 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordGenerator.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/PasswordGenerator.strings @@ -1,3 +1,24 @@ +/* Accessibility label describing a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.A11yLabel.v132" = "Gineadair nam faclan-faire"; + +/* Accessibility label describing the close button for the popup related to a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.CloseButtonA11ylabel.v132" = "Dùin"; + +/* When a user is in the process of creating an account, they have the option to generate a password. The user is capable of copying this password after long pressing the value of the password displayed to them. This string is the label of the copy button that appears after long pressing the password. */ +"PasswordGenerator.CopyPasswordButtonLabel.v132" = "Dèan lethbhreac"; + +/* Text displayed when a user interacts with the password field in a signup form, as part of a popup. This popup allows the user to generate a password that they have the option to use when signing up for an account. */ +"PasswordGenerator.Description.v132" = "Dìon an cunntas agad le facal-faire làidir a chaidh a ghintinn air thuaiream."; + +/* When a user is in the process of creating an account, they have the option to generate a password. The popup displaying the generated password to the user is available by clicking a keyboard accessory button with this label. */ +"PasswordGenerator.KeyboardAccessoryButtonLabel.v132" = "Cleachd facal-faire làidir"; + +/* Prefix to alert accessibility users that a generated password (represented by %@) will be read to them next. */ +"PasswordGenerator.PasswordReadoutPrefaceA11y.v132" = "Facal-faire a chaidh a ghintinn: %@"; + +/* Accessibility label describing a refresh password button belonging to a popup that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.RefreshPasswordButtonA11yLabel.v132" = "Gin facal-faire làidir ùr"; + /* Title text displayed as part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user -- clicking a button fills in the password of the signup form with this generated password. */ "PasswordGenerator.Title.v132" = "A bheil thu airson facal-faire làidir a chleachdadh?"; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/ScanQRCode.strings index 8e8de8ba94956..1b3209154f226 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/ScanQRCode.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/ScanQRCode.strings @@ -4,3 +4,6 @@ /* Deny button to cancel opening URL from scanned QR Code */ "ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Diùlt"; +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "A bheil thu airson cead fosglaidh a thoirt dha %@?"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..92262e3f7b833 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Dùin"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Fosglaidh seo roghainnean an luirg"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Roghainnean luirg"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Dèan lorg an-seo an turas seo:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Roghainnean luirg"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Dèan lorg an-seo an turas seo:"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/SelectCreditCard.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/SelectCreditCard.strings new file mode 100644 index 0000000000000..284b72db40e99 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/SelectCreditCard.strings @@ -0,0 +1,3 @@ +/* This value is used as the title for the select a credit card from list of available cards. */ +"CreditCard.SelectCreditCard.MainTitle.v122" = "Cleachd cairt a shàbhail thu"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Settings.strings index 57bf41dab4c57..4147a3c3071a3 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Settings.strings @@ -1,15 +1,78 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Stiùirich na seòlaidhean"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "An seòladh airson %@"; + +/* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ +"Addresses.Settings.SavedAddressesSectionTitle.v124" = "Seòlaidhean a shàbhail thu"; + +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Sàbhail seòlaidhean ann am %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Sàbhail am fiosrachadh agad air dòigh thèarainte is faigh grad-ghreim air an ath-thuras."; + +/* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ +"Addresses.Settings.Switch.Description.v124" = "Gabhaidh seo a-staigh àireamhan-fòn is seòlaidhean puist-d"; + +/* Title label for user to use the toggle settings to allow saving and autofilling of addresses for webpages. */ +"Addresses.Settings.Switch.Title.v124" = "Sàbhail is fèin-lìon seòlaidhean"; + +/* Displayed inside the keyboard hint when a user is entering their address and has at least one saved address. Indicates that there are stored addresses available for use in filling out a form. */ +"Addresses.Settings.UseSavedAddressFromKeyboard.v124" = "Cleachd seòladh a shàbhail thu"; + +/* Accessibility label for the add button in autofill settings screen. Pressing this button presents a modal that allows users to add a card by entering the credit card information. */ +"CreditCard.Settings.AddCard.AccessibilityLabel.v121" = "Cuir cairt ris"; + /* Description label for when there are no credit cards shown in credit card list in autofill settings screen. */ "CreditCard.Settings.EmptyListDescription.v112" = "Sàbhail fiosrachadh na cairt agad ann an àite tèarainte airson seacadh a-mach nas luaithe an ath-thuras."; +/* Title label for when there are no credit cards shown in credit card list in autofill settings screen. %@ is the product name and should not be altered. */ +"CreditCard.Settings.EmptyListTitle.v122" = "Sàbhail cairtean ann am %@"; + +/* Accessibility label for a credit card list item in autofill settings screen. The first parameter is the credit card issuer (e.g. Visa). The second parameter is the name of the credit card holder. The third parameter is the last 4 digits of the credit card. The fourth parameter is the card's expiration date. */ +"CreditCard.Settings.ListItemA11y.v118" = "%1$@, air fhoillseachadh dha %2$@, a’ crìochnachadh le %3$@, falbhaidh an ùine air %4$@"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string indicates to users that they can deny Firefox from remembering the card that is being used. */ +"CreditCard.Settings.NotNow.v122" = "Chan ann an-dràsta"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string is a title string of the overall message that asks the user if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.RememberThisCard.v122" = "A bheil thu airson a’ chairt seo a shàbhaladh air dòigh thèarainte?"; + +/* When a user is in the process or has finished making a purchase with a remembered card, and if the credit card information doesn't match the contents of the stored information of that card, we show this string. We ask this user if they would like Firefox update the staled information of that credit card. */ +"CreditCard.Settings.UpdateThisCard.v122" = "A bheil thu airson a’ chairt ùrachadh?"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string asks users to confirm if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.Yes.v122" = "Ùraich"; + /* When a user is in the process of making a purchase and has at least one saved credit card, a view above the keyboard shows actions a user can take. When tapping this label, the keyboard will dismiss from view. */ "CreditCards.Settings.Done.v114" = "Deiseil"; /* When a user is in the process or has finished making a purchase, and has at least one card saved, we show this tappable string. This indicates to users that they can navigate to their list of stored credit cards in the app's credit card list screen. */ "CreditCards.Settings.ManageCards.v112" = "Stiùirich na cairtean"; +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label used as a title. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseASavedCard.v122" = "Cleachd cairt a shàbhail thu"; + /* When a user is in the process of making a purchase, and has at least one saved card, we show this label inside the keyboard hint. This indicates to the user that there are stored cards available for use on this pending purchase. */ "CreditCards.Settings.UseSavedCardFromKeyboard.v112" = "Cleachd cairt a shàbhail thu"; +/* Settings section title for the old Firefox account */ +"FxA.FirefoxAccount.v119" = "Cunntas"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v124" = "Fèin-lìon seòlaidhean"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v126" = "Seòlaidhean"; + +/* Label used as an item in Settings screen. When touched, it will take user to credit card settings page to that will allows to add or modify saved credit cards to allow for autofill in a webpage. */ +"Settings.AutofillCreditCard.Title.v122" = "Dòighean pàighidh"; + +/* Title displayed in header of the FxA settings panel. */ +"Settings.FxA.Title.v119" = "Cunntas"; + /* This is the description for the setting that toggles Sync related notifications in the settings menu under the Notifications section. */ "Settings.Notifications.SyncNotificationsStatus.v112" = "Feumaidh seo a bhith air mus fhaigh thu tabaichean is mus fhaigh thu brath nuair a chlàraicheas tu a-steach air uidheam eile."; @@ -34,3 +97,78 @@ /* This is the title informing the user needs to turn on notifications in iOS Settings. */ "Settings.Notifications.TurnOnNotificationsTitle.v112" = "Cuir na brathan air"; +/* Accessibility label for default search engine setting. */ +"Settings.Search.Accessibility.DefaultSearchEngine.v121" = "An t-einnsean-luirg bunaiteach"; + +/* Accessibility label for Learn more about Firefox Suggest. */ +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Barrachd fiosrachaidh mu Ecosia Suggest"; + +/* Title for alternate search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Einnseanan-luirg eile"; + +/* Footer for the `default search engine` settings section in the Search Settings page, which explains in more details what the `Show Search Suggestions` setting includes. */ +"Settings.Search.DefaultSearchEngine.Footer.v122" = "Toraidhean o luirg, on eachdraidh, comharran-lìn is mòran a bharrachd"; + +/* Title for the `default search engine` settings section in the Search page in the Settings menu. */ +"Settings.Search.DefaultSearchEngine.Title.v121" = "An t-einnsean-luirg bunaiteach"; + +/* Title for the `Suggestions from Search Engines` settings section in the Search page in the Settings menu. */ +"Settings.Search.EnginesSuggestions.Title.v124" = "Molaidhean o einnseanan-luirg"; + +/* Navigation title for search page in the Settings menu. */ +"Settings.Search.PageTitle.v121" = "Lorg"; + +/* Description for `Show in Private Sessions` toggle, located in `Suggestions from Search Engines` section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Description.v125" = "Seall molaidhean o einnseanan-luirg ann an seiseanan prìobhaideach"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in the Private Session section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v122" = "Seall molaidhean ann am brabhsadh prìobhaideach"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in `Suggestions from Search Engines` and `Address Bar - Firefox Suggest` sections in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v124" = "Seall ann an seiseanan prìobhaideach"; + +/* Title for the `Private Browsing` settings section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Title.v122" = "Brabhsadh prìobhaideach"; + +/* Title for quick-search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.QuickEnginesTitle.v121" = "Einnseanan grad-luirg"; + +/* Label for the `show search suggestions` setting, in the Search Settings page. */ +"Settings.Search.ShowSuggestions.v121" = "Seall molaidhean-luirg"; + +/* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Bàr an t-seòlaidh – Ecosia Suggest"; + +/* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Barrachd fiosrachaidh mu Ecosia Suggest"; + +/* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Seall molaidhean o Ecosia Suggest ann an seiseanan prìobhaideach"; + +/* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ +"Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Lorg san eachdraidh bhrabhsaidh"; + +/* In the Search page of the Settings menu, the title for the setting to enable search bookmarks. */ +"Settings.Search.Suggest.SearchSearchBookmarks.Title.v124" = "Lorg sna comharran-lìn"; + +/* In the Search page of the Settings menu, the title for the setting to enable synced tabs. */ +"Settings.Search.Suggest.SearchSyncedTabs.Title.v124" = "Lorg am measg nan tabaichean sioncronaichte"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124" = "Faigh molaidhean on lìon co-cheangailte ris na lorg thu"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124.v2" = "Faigh molaidhean o %@ co-cheangailte ris na lorg thu"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124" = "Molaidhean o %@"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from the web in Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124.v2" = "Molaidhean on lìon"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from sponsors. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Description.v124" = "Cùm taic ri %@ ’s tu ag aontachadh ri molaidhean sponsairichte o àm gu àm"; + +/* In the Search page of the Settings menu, the title for the setting to enable Suggestions from sponsors. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Title.v124" = "Molaidhean o na sponsairean"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Share.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Share.strings new file mode 100644 index 0000000000000..6c314d284df16 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Share.strings @@ -0,0 +1,6 @@ +/* Error message shown in the remote tabs panel */ +"SendTo.NoDevicesFound.Message.v119" = "Cha do cheangail thu uidheam sam bith eile ris a’ chunntas seo as urrainn dhut sioncronachadh."; + +/* This message appears when a user tries to use 'Send Link to Device' action while not logged in */ +"SendTo.NotSignedIn.Title.v119" = "Cha do rinn thu clàradh a-steach dhan chunntas agad."; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Shopping.strings new file mode 100644 index 0000000000000..933f943c88094 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Shopping.strings @@ -0,0 +1,252 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates that a user can tap on the shopping button to start using the Shopping feature. */ +"ContextualHints.Shopping.NotOptedIn.v120" = "Feuch a bheil lèirmheasan a’ bhathair seo earbsach mus ceannaich thu dad."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing the Shopping feature. It indicates that a user can go directly to the Shopping feature by tapping the text of the action. */ +"ContextualHints.Shopping.NotOptedInAction.v120" = "Feuch dearbhair nan lèirmheasan"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears after the user has opted in for the Shopping feature. It indicates that a user can directly open the review checker by tapping the text of the action. */ +"ContextualHints.Shopping.OptedInAction.v120" = "Fosgail dearbhair nan lèirmheasan"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one appears after the user has opted in and informs him if he wants use the review checker by tapping the Shopping button. */ +"ContextualHints.Shopping.OptedInBody.v120" = "A bheil na lèirmheasan seo earbsach? Sgrùd iad an-dràsta ’s faic rangachadh air a ghleusadh."; + +/* Footer label from the Fakespot Ad card displayed for the related product we advertise. This is displayed below the ad card, suggested as an alternative to the product reviewed. The first parameter will be replaced by the Fakespot app name */ +"Shopping.AdCard.Footer.v121" = "An t-sanasachd le %@"; + +/* Title label for the Fakespot Ad card. This is displayed above a product image, suggested as an alternative to the product reviewed. */ +"Shopping.AdCard.Title.v121" = "Barrachd ri chnuasachadh"; + +/* Description adjusted of the rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Description.v121" = "Stèidhichte air lèirmheasan earbsach"; + +/* Accessibility label, associated to adjusted rating stars. %@ is a decimal value from 0 to 5 that will only use a tenth (example: 3.5). */ +"Shopping.AdjustedRating.StarsAccessibilityLabel.v120" = "%@ à 5 rionnagan"; + +/* Title of the adjusted rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Title.v120" = "Rangachadh air a ghleusadh"; + +/* Button text of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Button.Text.v120" = "Tha mi agaibh"; + +/* Title of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Title.v120" = "Tha an anailis cho ùr ’s a ghabhas"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Competitiveness.Title.v120" = "Farpaiseachd"; + +/* Title of the button that shows less reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.LessButton.Title.v120" = "Seall nas lugha"; + +/* Title of the button that shows more reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.MoreButton.Title.v120" = "Seall an còrr"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet, specifically focusing on the quality, design, and condition of the product's packaging. This may include details about the box, protective materials, presentation, and overall packaging experience. */ +"Shopping.HighlightsCard.Packaging.Title.v120" = "Pacaideadh"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Price.Title.v120" = "A’ phrìs"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Quality.Title.v120" = "A’ chàileachd"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Shipping.Title.v120" = "Lìbhrigeadh"; + +/* Title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Title.v120" = "Brod nan lèirmheasan o chionn goirid"; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Description.v120" = "Gu mì-fhortanach, chan urrainn dhuinn càileachd nan lèirmheasan aig cuid a bhathar a sgrùdradh, mar eisimpleir cairtean prèasaint, sruthan video, ceòl is geamannan."; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "Chan urrainn dhuinn na lèirmheasan seo a sgrùdadh"; + +/* Description text for an information card used in the review checker section. This message is displayed when the reviews for a product are not yet available but are expected to be provided within the next 24 hours. It serves to inform users of the short wait for reviews and encourages them to return soon for the updated information. */ +"Shopping.InfoCard.InfoComingSoon.Description.v121" = "Bu chòir gum bi fiosrachadh againn mu lèirmheasan a’ bhathair seo am broinn 24 uair a thìde. Na bi fada gun tilleadh."; + +/* Title for an information card that is displayed in the review checker section when certain details about a product or feature are not currently available but are expected to be provided soon. The message should imply that the user can look forward to receiving more information shortly. */ +"Shopping.InfoCard.InfoComingSoon.Title.v121" = "Tha fiosrachadh air an t-slighe"; + +/* Primary action title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.PrimaryAction.v120" = "Thoir sùil an-dràsta"; + +/* Title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.Title.v120" = "Fiosrachadh ùr ri sgrùdadh"; + +/* Description for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Description.v120" = "Tha sinn ag obair air an duilgheadas seo. Na bi fada gun duilgheadas."; + +/* Title for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Title.v120" = "Chan eil fiosrachadh ann an-dràsta fhèin"; + +/* Description for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Description.v120" = "Turas a bhios barrachd lèirmheasan aig a’ bhathar seo, ’s urrainn dhuinn sgrùdadh dè cho math ’s a tha iad."; + +/* Title for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Title.v120" = "Chan eil lèirmheasan gu leòr ann fhathast"; + +/* Description for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This description is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Description.v121" = "Ma chì thu gu bheil am bathar seo air ais sna bùthan, innis dhuinn is bheir sinn sùil air na lèirmheasan."; + +/* Primary action label for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This primary action label is used for info card button where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.PrimaryAction.v121" = "Cuir fios gu bheil stoca ann a-rithist"; + +/* Title for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This title is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Title.v121" = "Chan eil am bathar ri fhaighinn"; + +/* Description for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Description.v120" = "Feumaidh seo suas ri 60 diog."; + +/* Title for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Title.v120" = "A’ sgrùdadh càileachd nan lèirmheasan"; + +/* Title for info card when the product is in analysis mode. The placeholder represents the percentage of the analysis progress, ranging between 1 and 100. */ +"Shopping.InfoCard.ProgressAnalysis.Title.v123" = "A’ sgrùdadh càileachd nan lèirmheasan (%@)"; + +/* This description appears beneath the confirmation title on the information card to inform the user that their report regarding the product stock status has been received and is being processed. It serves to set the expectation that the review information will be updated within 24 hours and invites the user to revisit the product page for updates. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Description.v121" = "Bu chòir gum bi fiosrachadh againn mu lèirmheasan a’ bhathair seo am broinn 24 uair a thìde. Na bi fada gun tilleadh."; + +/* This title is displayed on the information card as a confirmation message after a user reports that a previously out-of-stock product is now available. It's meant to acknowledge the user's contribution and encourage community engagement by letting them know their report has been successfully submitted. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Title.v121" = "Mòran taing airson innse dhuinn!"; + +/* Text for the analyzer button displayed when an analysis can be updated for a product. */ +"Shopping.NoAnalysisCard.AnalyzerButton.Title.v120" = "Sgrùd càileachd an lèirmheis"; + +/* Text for the body label, to check the reliability of a product. */ +"Shopping.NoAnalysisCard.BodyLabel.Title.v120" = "Feuch càileachd an lèirmheis ma tha thu airson fiosrachadh dè cho earbsach ’s a tha lèirmheasan a’ bhathair seo. Cha doir e ach mionaid."; + +/* Title for card displayed when a shopping product has not been analysed yet. */ +"Shopping.NoAnalysisCard.HeadlineLabel.Title.v120" = "Chan eil fiosrachadh againn mu na lèirmheasan seo fhathast"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replaced by the Fakespot app name. After the colon, what appears are two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v120" = "Le bhith a’ taghadh “Tha, feuchaidh mi e”, bidh tu ag aontachadh ris na leanas o %@:"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). After the colon, there will be two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v123" = "Le bhith a’ taghadh “Tha, feuchaidh mi e”, bidh tu ag aontachadh ris na leanas:"; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. This string is almost identical with 'Shopping.OptInCard.FirstParagraph.Description', but without Best Buy and Walmart websites, which are not available in many locales. */ +"Shopping.OptInCard.FirstParagraph.AmazonOnly.Description.v122" = "Faic dè cho earbsach ’s a tha lèirmheasan bathair air %1$@ mus ceannaich thu rud. Tha dearbhair nan lèirmheasan, gleus probhail le %2$@, am broinn a’ bhrabhsair o thùs."; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "Faic dè cho earbsach ’s a tha lèirmheasan bathair air %1$@ mus ceannaich thu rud. Tha dearbhair nan lèirmheasan, gleus probhail le %2$@, am broinn a’ bhrabhsair o thùs. Tha e ag obair air %3$@ agus %4$@ cuideachd."; + +/* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.HeaderLabel.Title.v120" = "Feuch an treòir mu lèirmheasan bathair againn"; + +/* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "Barrachd fiosrachaidh"; + +/* Text for the main button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.MainButton.Title.v120" = "Tha, feuchaidh mi e"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v120" = "Am poileasaidh prìobhaideachd"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Firefox app name. */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v123" = "Aithris na prìobhaideachd aig %@"; + +/* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.SecondaryButton.Title.v120" = "Chan ann an-dràsta"; + +/* Label for the second paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be replaced by the Fakespot app name. The second parameter will be replaced the company name of Mozilla. */ +"Shopping.OptInCard.SecondParagraph.Description.v120" = "Le cumhachd %1$@ le %2$@, is urrainn dhut lèirmheasan claon is neo-fhìor a sheachnadh. Tha sinn a’ sìor-leasachadh a’ mhodail AI againn gus do dhìon fhad ’s a nì thusa seopadaireachd."; + +/* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "Teirmichean a’ chleachdaidh"; + +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "Teirmichean a’ chleachdaidh aig %@"; + +/* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ +"Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "Ìre %@"; + +/* Title of the reliability card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityCardTitle.v120" = "Dè cho earbsach ’s a tha na lèirmheasan seo?"; + +/* Description of the reliability ratings for rating 'A' and 'B' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.AB.Description.v120" = "Lèirmheasan earbsach"; + +/* Description of the reliability rating 'C' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.C.Description.v120" = "Measgachadh de lèirmheasan earbsach is neo-earbsach"; + +/* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Lèirmheasan neo-earbsach"; + +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "Tha an *rangachadh gleusta* stèidhichte dìreach air lèirmheasan a tha earbsach ’nar beachd."; + +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Co-theannaich a’ chairt “Mar a dhearbhas sinn càileachd nan lèirmheasan”"; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Leudaich a’ chairt “Mar a dhearbhas sinn càileachd nan lèirmheasan”"; + +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "Cleachdaidh sinn teicneolas AI o %1$@ le %2$@ airson earbsachd nan lèirmheasan a sgrùdadh. Cha dèan sin measadh air càileachd a’ bhathair, dìreach càileachd nan lèirmheasan."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "Tha na *bàrr-phuingean* o lèirmheasan %1@ on 80 latha seo chaidh is a tha earbsach ’nar beachd."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "Tha na *bàrr-phuingean* o lèirmheasan %@ on 80 latha seo chaidh is a tha earbsach ’nar beachd."; + +/* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Label.Title.v120" = "Mar a dhearbhas sinn càileachd nan lèirmheasan"; + +/* The title of the learn more button from How we determine review quality card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the Fakespot app name. */ +"Shopping.ReviewQualityCard.LearnMoreButton.Title.v120" = "Barrachd fiosrachaidh mun dòigh air an sgrùd %@ càileachd nan lèirmheasan"; + +/* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "Tha sinn a’ creidsinn gur e measgachadh de lèirmheasan earbsach is neo-earbsach a th’ annta"; + +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "Lèirmheasan earbsach. Tha sinn a’ creidsinn gur e lèirmheasan onarach neo-chlaon a th’ annta a chaidh a sgrìobhadh le fìor-chustamairean."; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "Bheir sinn *litir measaidh* dha na lèirmheasach aig gach bathar, eadar A is F."; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "Lèirmheasan neo-earbsach. Tha sinn a’ creidsinn gur e lèirmheasan fuadain a th’ annta no feadhainn le lèirmheasaichean claona."; + +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "Co-theannaich cairt nan roghainnean"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "Leudaich cairt nan roghainnean"; + +/* Accessibility hint for the recommended products label and switch, grouped together. When the group is selected in VoiceOver mode, the hint is read to help the user understand what action can be performed. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityHint.v123" = "Thoir gnogag dhùbailte airson an roghainn a thoglachadh."; + +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "Tha cumhachd %1$@ le %2$@ ann an dearbhair nan lèirmheasan"; + +/* Title of the settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.Label.Title.v120" = "Roghainnean"; + +/* Label of the switch from settings card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the app name. */ +"Shopping.SettingsCard.RecommendedProducts.Label.v120" = "Seall bathar a mholadh %@"; + +/* Toggled Off accessibility switch value from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOff.AccessibilityLabel.v123" = "Dheth"; + +/* Toggled On accessibility value, from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOn.AccessibilityLabel.v123" = "Air"; + +/* Label of the button from settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.TurnOffButton.Title.v120" = "Cuir dheth dearbhair nan lèirmheasan"; + +/* Beta label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Beta.Title.v120" = "Beta"; + +/* Accessibility label for close button that dismisses the Shopping Experience (Fakespot) sheet. */ +"Shopping.Sheet.Close.AccessibilityLabel.v121" = "Dùin dearbhair nan lèirmheasan"; + +/* Label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Title.v120" = "Dearbhair nan lèirmheasan"; + +/* Text for body of error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Description.v120" = "Thoir sùil air a’ cheangal ris an lìonra is feuch ris an duilgheadas ath-luchdadh an uair sin."; + +/* Title for error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Title.v120" = "Gun cheangal ris an eadar-lìon"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/SnackBar.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/SnackBar.strings index 880371d431c6b..a6e6577d4d0f8 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/SnackBar.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/SnackBar.strings @@ -4,3 +4,6 @@ /* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets saved successfully */ "CreditCard.SnackBar.SavedCardLabel.v112" = "Chaidh a’ chairt ùr a shàbhaladh"; +/* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets updated successfully */ +"CreditCard.SnackBar.UpdatedCardLabel.v122" = "Chaidh fiosrachadh na cairt ùrachadh"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/SuggestedSites.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/SuggestedSites.strings new file mode 100644 index 0000000000000..471f9fc3c84b0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/SuggestedSites.strings @@ -0,0 +1,3 @@ +/* Title for X (formerly Twitter) tile in the suggested sites section of the homepage. */ +"SuggestedSites.X.Title.v131" = "X"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/TabLocation.strings new file mode 100644 index 0000000000000..e3565e5e8cd5f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/TabLocation.strings @@ -0,0 +1,36 @@ +/* Accessibility hint for the reload button */ +"Address.Bar.Reload.A11y.Hint.v124" = "Thoir gnogag dhùbailte ’s cumail airson barrachd roghainnean"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.NotSecure.A11y.Label.v119" = "Chan eil an ceangal tèarainte. Tha an dìon adhartach o thracadh dheth."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.Secure.A11y.Label.v119" = "Tha an ceangal tèarainte. Tha an dìon adhartach o thracadh dheth."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.NotSecure.A11y.Label.v119" = "Chan eil an ceangal tèarainte"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.Secure.A11y.Label.v119" = "Ceangal tèarainte"; + +/* Accessibility label for the lock / tracking protection button on the URL bar */ +"TabLocation.LockButton.AccessibilityLabel.v122" = "Dìon o thracadh"; + +/* Large content title for the lock button. This title is displayed when accessible font sizes are enabled */ +"TabLocation.LockButton.LargeContentTitle.v122" = "Dìon o thracadh"; + +/* Accessibility label for the share button in url bar */ +"TabLocation.Share.A11y.Label.v119" = "Co-roinn an duilleag seo"; + +/* Large content title for the share button. This title is displayed when using accessible font sizes is enabled */ +"TabLocation.ShareButton.AccessibilityLabel.v122" = "Co-roinn"; + +/* Accessibility label for the shopping button in url bar */ +"TabLocation.Shopping.A11y.Label.v120" = "Dearbhair nan lèirmheasan"; + +/* Large content title for the tabs button. The argument is the number of open tabs or an infinity symbol. This title is displayed when using accessible font sizes is enabled. */ +"TabsButton.Accessibility.LargeContentTitle.v122" = "Seall tabaichean: %@"; + +/* Large content title for the button shown in editing mode to remove this site from the top sites panel. */ +"TopSites.RemoveButton.LargeContentTitle.v122" = "Thoir an duilleag air falbh"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/TabToolbar.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/TabToolbar.strings new file mode 100644 index 0000000000000..707e9beae1633 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/TabToolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the tab toolbar fire button in private mode, used to provide users a way to end and delete their private session data. */ +"TabToolbar.Accessibility.DataClearance.v122" = "Falamhachadh dàta"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/TabsTray.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/TabsTray.strings index d38fda6db338c..160aa53a5a29f 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/TabsTray.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/TabsTray.strings @@ -10,3 +10,9 @@ /* This is the swipe action title for closing an inactive tab by swiping, located in the Inactive Tabs section of the Tabs Tray */ "InactiveTabs.TabTray.CloseSwipeActionTitle.v115" = "Dùin"; +/* Users can disable syncing tabs from other devices. In the Sync Tabs panel of the Tab Tray, we inform the user tab syncing can be switched back on to view those tabs. */ +"TabsTray.Sync.SyncTabsDisabled.v116" = "Cuir air sioncronachadh nan tabaichean airson cothrom fhaighinn air na tabaichean agad o uidheaman eile."; + +/* Button label to sync tabs in your account */ +"TabsTray.SyncTabs.SyncTabsButton.Title.v119" = "Sioncronaich na tabaichean"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/Toolbar.strings new file mode 100644 index 0000000000000..34d2b4e9a4098 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Taba ùr"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Dùin an taba seo"; + diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/ToolbarLocation.strings index f04e804784bfe..a2c78f8637a39 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/ToolbarLocation.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/ToolbarLocation.strings @@ -1,6 +1,9 @@ /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ "ContextualHints.Toolbar.Bottom.Description.v107" = "Gluais am bàr-inneal gun bhàrr mas fheàrr leat an-sin e."; +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "Thoir gnogag air na saighdean is cùm iad airson leum a ghearradh eadar duilleagan eachdraidh an taba seo."; + /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ "ContextualHints.Toolbar.Top.Description.v107" = "Gluais am bàr-inneal gun bhonn mas fheàrr leat an-sin e."; diff --git a/firefox-ios/Shared/Supporting Files/gd.lproj/UpdateCard.strings b/firefox-ios/Shared/Supporting Files/gd.lproj/UpdateCard.strings index 39e503de59f20..67e0578bbfe1f 100644 --- a/firefox-ios/Shared/Supporting Files/gd.lproj/UpdateCard.strings +++ b/firefox-ios/Shared/Supporting Files/gd.lproj/UpdateCard.strings @@ -1,6 +1,15 @@ +/* This value is used as the toast message for the saving success alert in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v116" = "Chaidh fiosrachadh na cairt ùrachadh"; + +/* This value is used as the title for the update card page */ +"CreditCard.UpdateCard.MainTitle.v122" = "A bheil thu airson a’ chairt ùrachadh?"; + /* This value is used as the title for the Manage Cards button from the update credit card page */ "CreditCard.UpdateCard.ManageCardsButtonTitle.v115" = "Stiùirich na cairtean"; /* This value is used as the title for the Not Now button in the update credit card page */ "CreditCard.UpdateCard.NotNowButtonTitle.v115" = "Chan ann an-dràsta"; +/* This value is used as the title for the button in the update credit card page. It indicates the action to update the details f9 the card. */ +"CreditCard.UpdateCard.YesButtonTitle.v122" = "Ùraich"; + diff --git a/firefox-ios/Shared/Supporting Files/gl.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/gl.lproj/Settings.strings index 11c9500ac672e..e19c22cef868e 100644 --- a/firefox-ios/Shared/Supporting Files/gl.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/gl.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Buscador predeterminado"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Más información sobre as suxerencias de Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Más información sobre as suxerencias de Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Buscadores alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Amosar suxestións de busca"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de enderezos - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de enderezos - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Máis información sobre Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Máis información sobre Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar suxestións de Firefox Suggest en sesións privadas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar suxestións de Ecosia Suggest en sesións privadas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Buscar no historial de navegación"; diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/he.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..5a89ca4f5e037 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/he.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "שעה אחרונה"; + diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/he.lproj/MainMenu.strings index 5ed23ecd963a2..9aca38ea5fb57 100644 --- a/firefox-ios/Shared/Supporting Files/he.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/he.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "שמירה"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "תפריט משנה של שמירה"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "מעבר לאתר למחשבים"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "כלים"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "תפריט משנה של כלים"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "חיפוש בדף…"; diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings index c629fb89c3917..d6c8d1a60e3f9 100644 --- a/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "מנוע חיפוש ברירת מחדל"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "מידע נוסף על Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "מידע נוסף על Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "מנועי חיפוש חלופיים"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "הצגת הצעות חיפוש"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "שורת הכתובת - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "שורת הכתובת - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "מידע נוסף על Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "מידע נוסף על Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "הצגת הצעות מ־Firefox Suggest בהפעלות פרטיות"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "הצגת הצעות מ־Ecosia Suggest בהפעלות פרטיות"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "חיפוש בהיסטוריית הגלישה"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings index 5c80c5e72b6ea..086d6139b89d0 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Postavke privatnosti i sigurnosti"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Otvara popis tražilica"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Tražilica: %@"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/FxANotification.strings new file mode 100644 index 0000000000000..a833e2c6d6076 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Broj zatvorenih %1$@ kartica: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Pogledaj nedavno zatvorene kartice"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..695c6987d3b87 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "U zadnjih sat vremena"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/MainMenu.strings index f245547835a3c..0e4c85b3b7a9d 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Spremi"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Podizbornik „Spremi”"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Prebaci na prikaz web stranice za računala"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Alati"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podizbornik „Alati”"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Pronađi na stranici …"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..e0253f97fa744 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Zatvori"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Otvara postavke pretrage"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Postavke pretrage"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Ovaj put traži pomoću:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Postavke pretrage"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Ovaj put traži pomoću:"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings index 3bd291f1159a7..a302750c219c0 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardna tražilica"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saznaj više o funkciji Firefox prijedlozi"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saznaj više o funkciji Ecosia prijedlozi"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternativne tražilice"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Prikaži prijedloge pretraživanja"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresna traka – Firefox prijedlozi"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresna traka – Ecosia prijedlozi"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saznaj više o funkciji Firefox prijedlozi"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saznaj više o funkciji Ecosia prijedlozi"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikaži prijedloge od „Firefox prijedlozi” u privatnim sesijama"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikaži prijedloge od „Ecosia prijedlozi” u privatnim sesijama"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Pretraži povijest pregledavanja"; diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..d2868cf9ae16b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Zańdźena hodźina"; + diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/MainMenu.strings index ba272906d4a14..fbc988bd2db98 100644 --- a/firefox-ios/Shared/Supporting Files/hsb.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Składować"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Podmeni składować"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "K websydłu za desktop"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Nastroje"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podmeni Nastroje"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Na stronje pytać…"; diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings index 05b87ceae4a0b..dc2c3cbcc4195 100644 --- a/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardna pytawa"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Zhońće wjace wo Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Zhońće wjace wo Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatiwne pytawy"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Pytanske namjety pokazać"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresowe polo – Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresowe polo – Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Zhońće wjace wo Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Zhońće wjace wo Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Namjety z Firefox Suggest w priwatnych posedźenjach pokazać"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Namjety z Ecosia Suggest w priwatnych posedźenjach pokazać"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Přehladowansku historiju přepytać"; diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/AddressToolbar.strings index c53ced5ebaeba..76e56a5e9f90a 100644 --- a/firefox-ios/Shared/Supporting Files/hu.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Adatvédelmi és biztonsági beállítások"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Megnyitja a keresőszolgáltatás-választást"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Keresőszolgáltatás: %@"; diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/FxANotification.strings new file mode 100644 index 0000000000000..8ba6cbfc4f0dd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ lap bezárva: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Nemrég bezárt lapok megtekintése"; + diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..b28733575d698 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Utolsó óra"; + diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/MainMenu.strings index 377aa783e24e5..8ff71708cf897 100644 --- a/firefox-ios/Shared/Supporting Files/hu.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Mentés"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Mentés almenü"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Váltás az asztali oldalra"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Eszközök"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Eszközök almenü"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Keresés az oldalon…"; diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..9d843df5ef4e4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Bezárás"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Megnyitja a keresési beállításokat"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Keresési beállítások"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Ezúttal keresés ebben:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Keresési beállítások"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Ezúttal keresés ebben:"; + diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/Settings.strings index 93b673b414ffb..4bda52becb530 100644 --- a/firefox-ios/Shared/Supporting Files/hu.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Alapértelmezett keresőszolgáltatás"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ismerje meg a Firefox Suggestet"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ismerje meg a Ecosia Suggestet"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatív keresőszolgáltatások"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Keresési javaslatok megjelenítése"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Címsáv – Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Címsáv – Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ismerje meg a Firefox Suggestet"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ismerje meg a Ecosia Suggestet"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "A Firefox Suggest javaslatainak megjelenítése a privát munkamenetekben"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "A Ecosia Suggest javaslatainak megjelenítése a privát munkamenetekben"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Böngészési előzmények keresése"; diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..24f0cf2dedbe2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Վերջին ժամի"; + diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/MainMenu.strings index 7ed1559f813c0..a9bc71ab28118 100644 --- a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Պահել"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Պահել ենթացանկը"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Փոխարկել ամբողջական կայքին"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Գործիքներ"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Գործիքների ենթացանկ"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Գտնել էջում…"; diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings index e2c33eeacc2a1..88d4f9ad8aa82 100644 --- a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Հիմնական որոնիչը"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Իմացեք ավելին Firefox-ի առաջարկների մասին"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Իմացեք ավելին Ecosia-ի առաջարկների մասին"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Այլընտրանքային որոնիչներ"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Ցուցադրել որոնման առաջարկները"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Հասցեների տող - Firefox-ն առաջարկում է"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Հասցեների տող - Ecosia-ն առաջարկում է"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Իմացեք ավելին Firefox-ի առաջարկների մասին"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Իմացեք ավելին Ecosia-ի առաջարկների մասին"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Ցուցադրել Firefox Suggest-ի առաջարկները գաղտնի աշխատաշրջաններում"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Ցուցադրել Ecosia Suggest-ի առաջարկները գաղտնի աշխատաշրջաններում"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Որոնման զննարկման պատմություն"; diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..02434624c9390 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Ultime hora"; + diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/MainMenu.strings index f3b155aeb968b..4fcb2bf4230e5 100644 --- a/firefox-ios/Shared/Supporting Files/ia.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Salvar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sub-menu Salvar"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Commutar al sito del scriptorio"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Utensiles"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Sub-menu Utensiles"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Cercar in le pagina…"; diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings index bbf259b21582d..68aefb58f8e86 100644 --- a/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motor de recerca predefinite"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saper plus re Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saper plus re Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motores de recerca alternative"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Monstrar suggestiones de recerca"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de adresses - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de adresses - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saper plus re Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saper plus re Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Monstrar suggestiones ab Firefox Suggest in sessiones private"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Monstrar suggestiones ab Ecosia Suggest in sessiones private"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Cercar in le chronologia de navigation"; diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/ActivityStream.strings b/firefox-ios/Shared/Supporting Files/id.lproj/ActivityStream.strings new file mode 100644 index 0000000000000..5ef64d346a9cd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/ActivityStream.strings @@ -0,0 +1,6 @@ +/* String used in the section title of the Bookmarks section on Home Screen. */ +"ActivityStream.Bookmarks.Title.v128" = "Markah"; + +/* Show all button text for Bookmarks items on the home page, which opens the Bookmarks panel when tapped. */ +"Bookmarks.Actions.More.v128" = "Tampilkan Semua"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/id.lproj/AddressToolbar.strings new file mode 100644 index 0000000000000..dbd66988479ea --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/AddressToolbar.strings @@ -0,0 +1,15 @@ +/* Accessibility label for the address field in the address toolbar. */ +"AddressToolbar.Location.A11y.Label.v128" = "Cari atau masukkan alamat"; + +/* Placeholder for the address field in the address toolbar. */ +"AddressToolbar.Location.Placeholder.v128" = "Cari atau masukkan alamat"; + +/* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ +"AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Pengaturan Privasi & Keamanan"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Buka pilihan mesin pencari"; + +/* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ +"AddressToolbar.SearchEngine.A11y.Label.v128" = "Mesin Pencari: %@"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/Alert.strings b/firefox-ios/Shared/Supporting Files/id.lproj/Alert.strings new file mode 100644 index 0000000000000..53866b80f034c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/Alert.strings @@ -0,0 +1,12 @@ +/* Button text to dismiss the dialog box that gets presented as a confirmation to remove card and cancel the operation. */ +"CreditCard.SnackBar.CancelRemoveCardButton.v112" = "Batal"; + +/* Sub label for the dialog box that gets presented as a confirmation to ask user if they would like to remove the saved credit card from local as well as all their synced devices */ +"CreditCard.SnackBar.RemoveCardSublabel.v112" = "Ini akan menghapus kartu dari semua perangkat Anda yang disinkronkan."; + +/* Title label for the dialog box that gets presented as a confirmation to ask user if they would like to remove the saved credit card */ +"CreditCard.SnackBar.RemoveCardTitle.v122" = "Hapus Kartu?"; + +/* Button text to dismiss the dialog box that gets presented as a confirmation to remove card and perform the operation of removing the credit card. */ +"CreditCard.SnackBar.RemovedCardButton.v112" = "Hapus"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/id.lproj/Alerts.strings index 8ff1a8cdf274b..79f5c98573218 100644 --- a/firefox-ios/Shared/Supporting Files/id.lproj/Alerts.strings +++ b/firefox-ios/Shared/Supporting Files/id.lproj/Alerts.strings @@ -1,3 +1,15 @@ +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the body text for the alert. */ +"Alerts.FeltDeletion.Body.v122" = "Tutup semua tab pribadi dan hapus riwayat, kuki, dan semua data situs lainnya."; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the cancel action for the alert, cancelling ending your session. */ +"Alerts.FeltDeletion.Button.Cancel.v122" = "Batal"; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the affirmative action for the alert, confirming that you do want to do that. */ +"Alerts.FeltDeletion.Button.Confirm.v122" = "Hapus data sesi"; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the title for the alert. */ +"Alerts.FeltDeletion.Title.v122" = "Akhiri sesi pribadi Anda?"; + /* The title for the negative action of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed, and will reject the action of restoring tabs. */ "Alerts.RestoreTabs.Button.No.v109" = "Tidak"; diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/BiometricAuthentication.strings b/firefox-ios/Shared/Supporting Files/id.lproj/BiometricAuthentication.strings new file mode 100644 index 0000000000000..8b87b1211efac --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/BiometricAuthentication.strings @@ -0,0 +1,6 @@ +/* Biometric authentication is when the system prompts users for Face ID or fingerprint before accessing protected information. This string asks the user to enter their device passcode to access the protected screen. */ +"Biometry.Screen.UniversalAuthenticationReason.v115" = "Otentikasi untuk mengakses sandi."; + +/* Biometric authentication is when the system prompts users for Face ID or fingerprint before accessing protected information. This string asks the user to enter their device passcode to access the protected screen for logins and encrypted cards. */ +"Biometry.Screen.UniversalAuthenticationReason.v122" = "Otentikasi untuk mengakses sandi tersimpan dan metode pembayaran Anda."; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/id.lproj/Bookmarks.strings new file mode 100644 index 0000000000000..47efd5f79445b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/Bookmarks.strings @@ -0,0 +1,30 @@ +/* When navigating through the bookmarks menu and bookmark folders, a back button with an `All` (bookmarks) label is present to take the user to the top level bookmarks menu. */ +"Bookmarks.Menu.AllBookmarks.v131" = "Semua"; + +/* Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen */ +"Bookmarks.Menu.BookmarksTopLevelTitle.v131" = "Markah"; + +/* The title for the Delete Bookmark button, in the Edit Bookmark popup screen which is summoned from the main menu's Save submenu, which will delete the currently bookmarked site from the user's bookmarks. */ +"Bookmarks.Menu.DeleteBookmark.v132" = "Hapus Markah"; + +/* Label of toast displayed after a bookmark is deleted in the Bookmarks menu. %@ is the name of the bookmark. */ +"Bookmarks.Menu.DeletedBookmark.v131" = "“%@” Dihapus"; + +/* When a folder is longpressed in the bookmarks menu, a `Delete Folder` button is present. */ +"Bookmarks.Menu.DeleteFolder.v131" = "Hapus Folder"; + +/* When a bookmark is longpressed in the bookmarks menu, an `Edit Bookmark` button is present. */ +"Bookmarks.Menu.EditBookmark.v131" = "Edit Markah"; + +/* When editing a bookmark, you can select the folder that the bookmark will be saved in. The label for this section of the view is `Save in`. */ +"Bookmarks.Menu.EditBookmarkSaveIn.v131" = "Simpan di"; + +/* Label on the top of the `Edit Bookmarks` screen. */ +"Bookmarks.Menu.EditBookmarkTitle.v131" = "Edit Markah"; + +/* When a folder is longpressed in the bookmarks menu, an `Edit Folder` button is present. */ +"Bookmarks.Menu.EditFolder.v131" = "Edit Folder"; + +/* The title for the Edit context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.Edit.v131" = "Edit"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/BottomSheet.strings b/firefox-ios/Shared/Supporting Files/id.lproj/BottomSheet.strings new file mode 100644 index 0000000000000..a9b6860e2c505 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/BottomSheet.strings @@ -0,0 +1,3 @@ +/* When a user is in the process of entering an address, a screen pops up prompting the user if they want to use a saved address. This string is used as the title label of the screen. */ +"Addresses.BottomSheet.UseSavedAddressBottomSheet.v124" = "Gunakan alamat tersimpan?"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/id.lproj/EditAddress.strings new file mode 100644 index 0000000000000..78e1bb82b0ad5 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/EditAddress.strings @@ -0,0 +1,108 @@ +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Departemen"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Distrik"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Surel"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirat"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Pulau"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Nama"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Lingkungan"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Oblast"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organisasi"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Paroki"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Kode Pos"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Kode kota"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefektur"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Provinsi"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Negara Bagian"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telepon"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Desa atau Kotapraja"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Kode Pos"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Batal"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Edit alamat"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Jalan"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Simpan"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Lihat alamat"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Tutup"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Edit"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Hapus Alamat"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Alamat Dihapus"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Alamat Disimpan"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Alamat Tak Bisa Disimpan"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Alamat Tak Bisa Dihapus"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Coba lagi"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Informasi Alamat Diperbarui"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v132.v2" = "Alamat Disimpan"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/EditCard.strings b/firefox-ios/Shared/Supporting Files/id.lproj/EditCard.strings new file mode 100644 index 0000000000000..f40b7044a0ee6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/EditCard.strings @@ -0,0 +1,21 @@ +/* Title label for the view where user can add their credit card info */ +"CreditCard.EditCard.AddCreditCardTitle.v122" = "Tambah Kartu"; + +/* Button label for cancelling editing of the credit card details shown in the form */ +"CreditCard.EditCard.CancelNavBarButtonLabel.v113" = "Batal"; + +/* Title label for user to input their credit card Expiration date in the format MM / YY printed on their credit card in the text box below. */ +"CreditCard.EditCard.CardExpirationDateTitle.v112" = "Kedaluwarsa BB / TT"; + +/* Title label for user to input their credit card number printed on their credit card in the text box below. */ +"CreditCard.EditCard.CardNumberTitle.v112" = "Nomor Kartu"; + +/* Button label for closing the view where user can view their credit card info */ +"CreditCard.EditCard.CloseNavBarButtonLabel.v113" = "Tutup"; + +/* Label for concealing contents of the credit card number */ +"CreditCard.EditCard.ConcealLabel.v114" = "Sembunyikan"; + +/* Label for copying contents of the form */ +"CreditCard.EditCard.CopyLabel.v113" = "Salin"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/FirefoxLogins.strings b/firefox-ios/Shared/Supporting Files/id.lproj/FirefoxLogins.strings new file mode 100644 index 0000000000000..8dd51fc19402c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/FirefoxLogins.strings @@ -0,0 +1,6 @@ +/* Placeholder text for search box in logins list view. */ +"LoginsList.LoginsListSearchPlaceholder.v122" = "Cari kata sandi"; + +/* Title for the list of logins saved by the app */ +"LoginsList.Title.v122" = "SANDI TERSIMPAN"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/FirefoxSync.strings b/firefox-ios/Shared/Supporting Files/id.lproj/FirefoxSync.strings new file mode 100644 index 0000000000000..ccdb43b2d305d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/FirefoxSync.strings @@ -0,0 +1,9 @@ +/* Toggle for address autofill syncing setting */ +"FirefoxSync.AddressAutofillEngine.v124" = "Alamat"; + +/* Toggle for credit cards syncing setting */ +"FirefoxSync.CreditCardsEngine.v122" = "Metode Pembayaran"; + +/* Toggle passwords syncing setting, in the Settings > Sync Data menu of the app. */ +"Sync.LoginsEngine.Title.v122" = "Sandi"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/Footer.strings b/firefox-ios/Shared/Supporting Files/id.lproj/Footer.strings new file mode 100644 index 0000000000000..7f856c7db7d53 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/Footer.strings @@ -0,0 +1,3 @@ +/* This is the learn more text of the Pocket footer on Firefox Homepage. */ +"FirefoxHomepage.Pocket.Footer.LearnMore.v115" = "Pelajari lebih lanjut"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/id.lproj/MainMenu.strings new file mode 100644 index 0000000000000..448a65e49fa75 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/MainMenu.strings @@ -0,0 +1,183 @@ +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Markah"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Unduhan"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Riwayat"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Sandi"; + +/* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Markah"; + +/* On the main menu, the title for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.Downloads.Title.v131" = "Unduhan"; + +/* On the main menu, the title for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.History.Title.v131" = "Riwayat"; + +/* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.Passwords.Title.v131" = "Sandi"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Dapatkan Bantuan"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Pengaturan"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "Baru di %@"; + +/* On the main menu, the title for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.GetHelp.Title.v131" = "Dapatkan Bantuan"; + +/* On the main menu, the title for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.Settings.Title.v131" = "Pengaturan"; + +/* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.WhatsNew.Title.v131" = "Baru di %@"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Beranda"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "Tambahkan ke Layar Beranda"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "Pintasan"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "Tambahkan ke Pintasan"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "Tambah Markah"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Title.v132" = "Markahi Laman ini"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "Edit Markah"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "Hapus dari Daftar Bacaan"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "Hapus dari Pintasan"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Subtitle.v132" = "PDF"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Laporkan"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Laporkan Situs yang Rusak"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Bagikan"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Bagikan"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Perbesaran"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Perbesar (%@)"; + +/* On the main menu, the title for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Nonaktifkan Mode Malam"; + +/* On the main menu, the title for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.NightMode.On.Title.v131" = "Aktifkan Mode Malam"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.NightMode.Subtitle.v131" = "Mode Malam"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.Print.Subtitle.v131" = "Cetak"; + +/* On the main menu, the title for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.Print.Title.v131" = "Cetak"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.Off.Title.v131" = "Matikan Tampilan Pembaca"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.On.Title.v131" = "Aktifkan Tampilan Pembaca"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.ReaderView.Subtitle.v131" = "Tampilan Pembaca"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131" = "Laporkan"; + +/* On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131" = "Laporkan Situs yang Rusak"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool. */ +"MainMenu.Submenus.Tools.Share.Subtitle.v131" = "Bagikan"; + +/* On the main menu, the title for the action that will take the user to the Share module in the application. */ +"MainMenu.Submenus.Tools.Share.Title.v131" = "Bagikan"; + +/* On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool. */ +"MainMenu.Submenus.Tools.Zoom.Subtitle.v131" = "Perbesaran"; + +/* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.Zoom.Title.v131" = "Perbesar (%@)"; + +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Menu Utama"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Tab pribadi baru"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "Tab baru"; + +/* On the main menu, the title for the action that will create a new private tab. */ +"MainMenu.TabsSection.NewPrivateTab.Title.v131" = "Tab Pribadi Baru"; + +/* On the main menu, the title for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.NewTab.Title.v131" = "Tab Baru"; + +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "Temukan di laman"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Simpan"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Simpan sub menu"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Beralih ke situs desktop"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "Beralih ke situs seluler"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Alat"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Sub menu alat"; + +/* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.FindInPage.Title.v131" = "Temukan di Laman…"; + +/* On the main menu, the title for the action that will take the user to the Save submenu in the menu. */ +"MainMenu.ToolsSection.SaveSubmenu.Title.v131" = "Simpan"; + +/* On the main menu, the title for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.SwitchToDesktopSite.Title.v131" = "Beralih ke Situs Desktop"; + +/* On the main menu, the title for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.SwitchToMobileSite.Title.v131" = "Beralih ke Situs Seluler"; + +/* On the main menu, the title for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.ToolsSubmenu.Title.v131" = "Alat"; + diff --git a/firefox-ios/Shared/Supporting Files/id.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/id.lproj/Microsurvey.strings new file mode 100644 index 0000000000000..01daa0790a1c9 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/id.lproj/Microsurvey.strings @@ -0,0 +1,36 @@ +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the button that appears on the prompt that allows the user to tap on and navigates them to the microsurvey to respond to. */ +"Microsurvey.Prompt.Button.v127" = "Lanjutkan"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.AccessibilityLabel.v127" = "Tutup Permintaan Survei"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "Tutup"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the logo image that appears on the prompt to inform the prompt is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Prompt.LogoImage.AccessibilityLabel.v129" = "Logo %@"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of button on the survey that a user can tap on to submit their responses. */ +"Microsurvey.Survey.Button.v127" = "Kirim"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.AccessibilityLabel.v127" = "Tutup Survei"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.Button.AccessibilityLabel.v127" = "Tutup"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the text shown on the confirmation page when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.ConfirmationLabel.v127" = "Terima kasih atas masukan Anda!"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the title for the header on the microsurvey when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.HeaderLabel.v127" = "Survei selesai"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v127" = "Lengkapi survei ini"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Silakan lengkapi survei"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "Logo %@"; + diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/is.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f0b7cd90a52f3 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/is.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Seinasta klukkustund"; + diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/is.lproj/MainMenu.strings index 8abd034e84b46..8b9b015b0e4b8 100644 --- a/firefox-ios/Shared/Supporting Files/is.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/is.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Vista"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Vista undirvalmynd"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Skipta yfir á venjulega vefsvæðið"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Verkfæri"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Undirvalmynd verkfæra"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Finna á síðu…"; diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings index eaf0c6c7e89ed..74769b1790560 100644 --- a/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Sjálfgefin leitarvél"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Frekari upplýsingar um tillögur frá Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Frekari upplýsingar um tillögur frá Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Aðrar leitarvélar"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Sýna leitartillögur"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Vistfangastika - Tillögur frá Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Vistfangastika - Tillögur frá Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Frekari upplýsingar um tillögur frá Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Frekari upplýsingar um tillögur frá Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Birta tillögur frá Firefox Suggest í huliðslotum"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Birta tillögur frá Ecosia Suggest í huliðslotum"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Leita í vafurferli"; diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/it.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..80e13f7b14feb --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/it.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Ultima ora"; + diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/it.lproj/MainMenu.strings index 4065761338fbe..0c2c389c4d07d 100644 --- a/firefox-ios/Shared/Supporting Files/it.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/it.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Salva"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Menu secondario Salva"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Passa al sito per desktop"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Strumenti"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Menu secondario Strumenti"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Trova nella pagina…"; diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings index 7208691826673..4ce43e0e17a74 100644 --- a/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motore di ricerca predefinito"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ulteriori informazioni su Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ulteriori informazioni su Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motori di ricerca alternativi"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Visualizza suggerimenti"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra degli indirizzi - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra degli indirizzi - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ulteriori informazioni su Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ulteriori informazioni su Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostra suggerimenti da Firefox Suggest in sessioni anonime"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostra suggerimenti da Ecosia Suggest in sessioni anonime"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Cerca nella cronologia di navigazione"; diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/AddressToolbar.strings index f5244a7e1da1b..6d0e24f52e72f 100644 --- a/firefox-ios/Shared/Supporting Files/ja.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "プライバシーとセキュリティの設定"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "検索エンジン選択リストを開きます"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "検索エンジン: %@"; diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/FxANotification.strings new file mode 100644 index 0000000000000..d5491993f2391 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ のタブを %2$@ 個閉じました"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "最近閉じたタブを表示"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f286728307791 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "1 時間以内"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/MainMenu.strings index b32fea8f064a6..4641c0e848a8b 100644 --- a/firefox-ios/Shared/Supporting Files/ja.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "保存"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "保存サブメニュー"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "PC 版サイトに切り替える"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "ツール"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "ツールサブメニュー"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "ページ内を検索..."; diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..dcf031233c711 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "閉じる"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "検索設定を開きます"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "検索設定"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "今回だけ使う検索エンジン:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "検索設定"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "今回だけ使う検索エンジン:"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/Settings.strings index 80583d3523c6d..e7b6d408d46d9 100644 --- a/firefox-ios/Shared/Supporting Files/ja.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "デフォルト検索エンジン"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox Suggest について"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia Suggest について"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "代替検索エンジン"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "検索語句の候補を表示"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "アドレスバー - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "アドレスバー - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox Suggest について"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia Suggest について"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "プライベートセッションで Firefox Suggest からの候補を表示します"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "プライベートセッションで Ecosia Suggest からの候補を表示します"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "閲覧履歴を検索する"; diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..6dc5c0baaa435 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Asrag aneggaru"; + diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/MainMenu.strings index bb4a888cd648d..7a2a7e4f8b332 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Sekles"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sekles umuɣ arnaw"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Ddu ɣer usmel n tnarit"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Ifecka"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Umuɣ arnaw n yifecka"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Af deg usebter…"; diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings index 849e78baacce6..c858448b5746f 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Amsedday n unadi amezwer"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Issin ugar ɣef Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Issin ugar ɣef Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Imseddayen n unadi niḍen"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Sken isumar n unadi"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Afeggag n tansiwin - Firefox isumer"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Afeggag n tansiwin - Ecosia isumer"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Issin ugar ɣef Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Issin ugar ɣef Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Sken isumar seg yisumar n Firefox deg tɣimiyin tusligin"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Sken isumar seg yisumar n Ecosia deg tɣimiyin tusligin"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Nadi deg uzray n tunigin"; diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..e31455617d1dd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Соңғы сағат"; + diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/MainMenu.strings index 426ec848ca36e..eef850d180a9b 100644 --- a/firefox-ios/Shared/Supporting Files/kk.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Сақтау"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Ішкі мәзірді сақтау"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Жұмыс үстелі сайтына ауысу"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Құралдар"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Құралдар ішкі мәзірі"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Беттен табу…"; diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings index e503f9211ee29..c1f0c68f64e64 100644 --- a/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Негізгі іздеу жүйесі"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox Suggest туралы көбірек білу"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia Suggest туралы көбірек білу"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Баламалы іздеу жүйелері"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Іздеу ұсыныстарын көрсету"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адрес жолағы - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адрес жолағы - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox Suggest туралы көбірек білу"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia Suggest туралы көбірек білу"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Жекелік сеанстарда Firefox Suggest ұсыныстарын көрсету"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Жекелік сеанстарда Ecosia Suggest ұсыныстарын көрсету"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Шолу тарихынан іздеу"; diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..fa8204d128b39 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "최근 1시간"; + diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/MainMenu.strings index 48134c87456d2..288c1419f85c3 100644 --- a/firefox-ios/Shared/Supporting Files/ko.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "저장"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "하위 메뉴 저장"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "데스크톱으로 전환"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "도구"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "도구 하위 메뉴"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "페이지에서 찾기…"; diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/Settings.strings index c58dcf6bcbe04..3a9c1eaa96799 100644 --- a/firefox-ios/Shared/Supporting Files/ko.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "기본 검색 엔진"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox 제안에 대해 더 알아보기"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia 제안에 대해 더 알아보기"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "다른 검색 엔진"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "검색 추천 보기"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "주소 표시줄 - Firefox 제안"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "주소 표시줄 - Ecosia 제안"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox 제안에 대해 더 알아보기"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia 제안에 대해 더 알아보기"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "비공개 세션에서 Firefox 제안 표시"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "비공개 세션에서 Ecosia 제안 표시"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "방문 기록 검색"; diff --git a/firefox-ios/Shared/Supporting Files/lo.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/lo.lproj/AddressToolbar.strings index 90afb067b33e1..350c36f8cb8eb 100644 --- a/firefox-ios/Shared/Supporting Files/lo.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/lo.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "ການຕັ້ງຄ່າຄວາມເປັນສ່ວນຕົວ ແລະ ຄວາມປອດໄພ"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "ເປີດການເລືອກເຄື່ອງມືການຄົ້ນຫາ"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "ເຄື່ອງມືການຊອກຫາ: %@"; diff --git a/firefox-ios/Shared/Supporting Files/lo.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/lo.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..08972e411284b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/lo.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "ຊົ່ວໂມງທີ່ຜ່ານມາ"; + diff --git a/firefox-ios/Shared/Supporting Files/lo.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/lo.lproj/MainMenu.strings index 666eeb7068424..90250d35e73ac 100644 --- a/firefox-ios/Shared/Supporting Files/lo.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/lo.lproj/MainMenu.strings @@ -4,6 +4,12 @@ /* The accessibility label for the close button in the Main menu. */ "MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "ປິດ"; +/* On the main menu, at the top, when the user is signed out. The title for the sign in action */ +"MainMenu.Account.SignedOut.Title.v131" = "ລົງຊື່ເຂົ້າໃຊ້"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state. */ +"MainMenu.Account.SyncError.Description.v131" = "ຢຸດການຊິງຄ໌ໄວ້ຊົ່ວຄາວ"; + /* On the main menu, the title for the action that will take the user to a website to get help from Mozilla. */ "MainMenu.SettingsSection.GetHelp.Title.v131" = "ຂໍຄວາມຊ່ວຍເຫລືອ"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/AddressToolbar.strings index 77cf573f88ea9..891ea1e3a0f2a 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Innstillinger for personvern og sikkerhet"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Åpner søkemotorvalg"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Søkemotor: %@"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/Bookmarks.strings index b958a9f49b2c7..e0e82307607ff 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/Bookmarks.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/Bookmarks.strings @@ -4,6 +4,9 @@ /* Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen */ "Bookmarks.Menu.BookmarksTopLevelTitle.v131" = "Bokmerker"; +/* The title for the Delete Bookmark button, in the Edit Bookmark popup screen which is summoned from the main menu's Save submenu, which will delete the currently bookmarked site from the user's bookmarks. */ +"Bookmarks.Menu.DeleteBookmark.v132" = "Slett bokmerke"; + /* Label of toast displayed after a bookmark is deleted in the Bookmarks menu. %@ is the name of the bookmark. */ "Bookmarks.Menu.DeletedBookmark.v131" = "Slettet «%@»"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings index 5c4ce4c0665f9..b230c5adcc31e 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings @@ -139,3 +139,6 @@ /* Toast message confirming that an address has been successfully updated. */ "Addresses.Toast.AddressUpdatedConfirmation.v129" = "Adresseinformasjon oppdatert"; +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v132.v2" = "Adresse lagret"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings index 5343d22ab3dab..dea0604d9aad7 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings @@ -61,6 +61,9 @@ /* Text to let users know that no trackers were found on the current website. */ "Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Fant ingen sporere"; +/* Text to let users know how many analytics trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Analytics.v132" = "Sporings-innhold: %@"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sporingsinfokapsler på tvers av nettsteder: %@"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/FxANotification.strings new file mode 100644 index 0000000000000..9c43d6792fc5e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/FxANotification.strings @@ -0,0 +1,3 @@ +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Vis nylig lukkede faner"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f045bbd2023f5 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Den siste timen"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/MainMenu.strings index c6a64b031fb71..eb37ebf7cf46b 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/MainMenu.strings @@ -1,3 +1,18 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "Finn det du trenger raskere, fra privat nettlesing til lagre-handlinger."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "Nytt: strømlinjeformet meny"; + +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "Tilbake"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "Lukk"; + +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "Logg inn for å synkronisere passord, faner, med mer"; + /* On the main menu, at the top, when the user is signed out. The description for the sign in action */ "MainMenu.Account.SignedOut.Description.v131" = "Synkroniser passord, faner og mer"; @@ -10,6 +25,18 @@ /* On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state. */ "MainMenu.Account.SyncError.Title.v131" = "Logg inn igjen for å synkronisere"; +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "Bokmerker"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "Nedlastinger"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "Historikk"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "Passord"; + /* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ "MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "Bokmerker"; @@ -22,6 +49,18 @@ /* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ "MainMenu.PanelLinkSection.Passwords.Title.v131" = "Passord"; +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "Tilpass startsiden"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "Få hjelp"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "Innstillinger"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "Nytt i %@"; + /* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ "MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "Tilpass startsiden"; @@ -34,6 +73,45 @@ /* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ "MainMenu.SettingsSection.WhatsNew.Title.v131" = "Nytt i %@"; +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "Hjem"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "Legg til på startskjermen"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "Snarvei"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "Legg til i snarveier"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "Legg til bokmerke"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Title.v132" = "Bokmerk denne siden"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "Rediger bokmerke"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "Fjern fra leselisten"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "Fjern fra snarveier"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Subtitle.v132" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Title.v132" = "Lagre som PDF"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Subtitle.v132" = "Leseliste"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Title.v132" = "Legg til i leselisten"; + /* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool. */ "MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131" = "Hjem"; @@ -73,6 +151,48 @@ /* On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list. */ "MainMenu.Submenus.Save.SaveToReadingList.Title.v131" = "Lagre til leselisten"; +/* On the main menu, the accessibility label for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Off.Title.v132" = "Slå av nattmodus"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.On.Title.v132" = "Slå på nattmodus"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Subtitle.v132" = "Nattmodus"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Subtitle.v132" = "Skriv ut"; + +/* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "Skriv ut"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "Slå av lesevisning"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "Slå på lesevisning"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "Lesevisning"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "Rapporter"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "Rapporter problem med et nettsted"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "Del"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "Del"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "Zoom"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "Zoom (%@)"; + /* On the main menu, the title for the action that will turn Night Mode off in the application. */ "MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "Slå av nattmodus"; @@ -115,12 +235,42 @@ /* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ "MainMenu.Submenus.Tools.Zoom.Title.v131" = "Skalering (%@)"; +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "Hovedmeny"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "Ny privat fane"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "Ny fane"; + /* On the main menu, the title for the action that will create a new private tab. */ "MainMenu.TabsSection.NewPrivateTab.Title.v131" = "Ny privat fane"; /* On the main menu, the title for the action that will create a new, non-private, tab. */ "MainMenu.TabsSection.NewTab.Title.v131" = "Ny fane"; +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "Søk på siden"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Lagre"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Undermeny lagre"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Bytt til datamaskinversjon"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "Bytt til mobilt nettsted"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Verktøy"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Undermeny verktøy"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Søk på siden …"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..3caabc4be444f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Lukk"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Åpner søkeinnstillinger"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Søkeinnstillinger"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Søk denne gangen i:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Søkeinnstillinger"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Søk denne gangen i:"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/Settings.strings index 1b19614a4f8d7..bfeec440d9685 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standard søkemotor"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Les mer om Firefox-forslag"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Les mer om Ecosia-forslag"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative søkemotorer"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Vis søkeforslag"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresselinje - Firefox-forslag"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresselinje - Ecosia-forslag"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Les mer om Firefox-forslag"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Les mer om Ecosia-forslag"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vis forslag fra Firefox Suggest i private økter"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vis forslag fra Ecosia Suggest i private økter"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Søk i nettleserhistorikk"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/ToolbarLocation.strings index ed6bf57a8b10a..0945e58c2f802 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/ToolbarLocation.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/ToolbarLocation.strings @@ -1,6 +1,9 @@ /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ "ContextualHints.Toolbar.Bottom.Description.v107" = "Flytt verktøylinjen til toppen hvis det er mer din stil."; +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "Trykk og hold pilene for å hoppe mellom sidene i denne fanens historikk."; + /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ "ContextualHints.Toolbar.Top.Description.v107" = "Flytt verktøylinjen til bunnen hvis det er mer din stil."; diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..7fbde33c70ada --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Laatste uur"; + diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/MainMenu.strings index ef5062673696e..d36ef7951ad62 100644 --- a/firefox-ios/Shared/Supporting Files/nl.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Opslaan"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Submenu opslaan"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Naar desktopwebsite"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Hulpmiddelen"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Submenu Extra"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Zoeken op pagina…"; diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/Settings.strings index 7caa2ce0673b0..41973d8a3f403 100644 --- a/firefox-ios/Shared/Supporting Files/nl.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standaardzoekmachine"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Meer info over Firefox Suggesties"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Meer info over Ecosia Suggesties"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatieve zoekmachines"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Zoeksuggesties tonen"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresbalk – Firefox Suggesties"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresbalk – Ecosia Suggesties"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Meer info over Firefox Suggesties"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Meer info over Ecosia Suggesties"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Suggesties van Firefox Suggesties tonen in privésessies"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Suggesties van Ecosia Suggesties tonen in privésessies"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Navigatiegeschiedenis doorzoeken"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f045bbd2023f5 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Den siste timen"; + diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/MainMenu.strings index d304ac074128c..874308b3a6a54 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Lagre"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Undermeny lagre"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Byt til datamaskinversjon"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Verktøy"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Undermeny verktøy"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Finn på sida…"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings index 650ace073f0a1..126e1760076cf 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standard søkjemotor"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Les meir om Firefox forslag"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Les meir om Ecosia forslag"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternative søkjemotorar"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Vis søkjeforslag"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresselinje - Firefox forslag"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresselinje - Ecosia forslag"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Les meir om Firefox forslag"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Les meir om Ecosia forslag"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vis forslag frå Firefox Suggest i private økter"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Vis forslag frå Ecosia Suggest i private økter"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Søk i nettlesarhistorikk"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/AddressToolbar.strings index 9d42eed32a07b..a9ff8f8087c09 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "ਪਰਦੇਦਾਰੀ ਅਤੇ ਸੁਰੱਖਿਆ ਸੈਟਿੰਗਾਂ"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "ਖੋਜ ਇੰਜਣ ਦੀ ਚੋਣ ਨੂੰ ਖੋਲ੍ਹਦਾ ਹੈ"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "ਖੋਜ ਇੰਜਣ: %@"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EngagementNotification.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EngagementNotification.strings index 439a568935eaf..a6da84956e955 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EngagementNotification.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EngagementNotification.strings @@ -5,7 +5,7 @@ "Engagement.Notification.Title.v112" = "ਆਪਣੀ ਪਹਿਲੀ ਖੋਜ ਕਰੋ"; /* Body of notification send to user after inactivity to encourage them to use the private browsing feature. Placeholder is app name. */ -"Engagement.Notification.Treatment.A.Body.v114" = "%@ ਵਿੱਚ ਨਿੱਜੀ ਬਰਾਊਜ਼ ਕਰਨਾ ਤੁਹਾਡੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸੰਭਾਲਦਾ ਨਹੀ ਅਤੇ ਲੁਕਵੇਂ ਟਰੈਕਰਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਂਦਾ ਹੈ।"; +"Engagement.Notification.Treatment.A.Body.v114" = "%@ ਵਿੱਚ ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ ਕਰਨਾ ਤੁਹਾਡੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸੰਭਾਲਦਾ ਨਹੀ ਅਤੇ ਲੁਕਵੇਂ ਟਰੈਕਰਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਂਦਾ ਹੈ।"; /* Title of notification send to user after inactivity to encourage them to use the private browsing feature. */ "Engagement.Notification.Treatment.A.Title.v114" = "ਬਿਨਾਂ ਪੈੜਾਂ ਛੱਡੇ ਬਰਾਊਜ਼ ਕਰੋ"; @@ -14,5 +14,5 @@ "Engagement.Notification.Treatment.B.Body.v114" = "%@ ਵਿੱਚ ਬਿਨਾਂ ਕਿਸੇ ਸੰਭਾਲੇ ਕੂਕੀ ਜਾਂ ਅਤੀਤ ਦੇ ਬਰਾਊਜ਼ ਕਰੋ"; /* Title of notification send to user after inactivity to encourage them to use the private browsing feature. */ -"Engagement.Notification.Treatment.B.Title.v114" = "ਨਿੱਜੀ ਬਰਾਊਜ਼ ਕਰਨ ਨੂੰ ਅਜ਼ਮਾਓ"; +"Engagement.Notification.Treatment.B.Title.v114" = "ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ ਕਰਨ ਨੂੰ ਅਜ਼ਮਾਓ"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/FxANotification.strings new file mode 100644 index 0000000000000..2c26061fdc7c9 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ ਟੈਬਾਂ ਬੰਦ ਕੀਤੀਆਂ: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "ਸੱਜਰੀਆਂ ਬੰਦ ਕੀਤੀਆਂ ਟੈਬਾਂ ਨੂੰ ਵੇਖੋ"; + diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..0ceb85c022ca5 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "ਆਖਰੀ ਘੰਟਾ"; + diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/MainMenu.strings index 19162603b13e6..e5604c3643cad 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "ਸੰਭਾਲੋ"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "ਸਬ-ਮੇਨੂ ਸੰਭਾਲੋ"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "ਡੈਸਕਟਾਪ ਸਾਈਟ ਲਈ ਸਵਿੱਚ ਕਰੋ"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "ਟੂਲ"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "ਟੂਲ ਸਬ-ਮੇਨੂ"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "…ਸਫ਼ੇ ਵਿੱਚ ਲੱਭੋ"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..c768bffa959fe --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "ਬੰਦ ਕਰੋ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "ਖੋਜ ਸੈਟਿੰਗਾਂ ਨੂੰ ਖੋਲ੍ਹਦਾ ਹੈ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "ਖੋਜ ਸੈਟਿੰਗਾਂ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "ਇਸ ਵਾਰ ਇਸ ਵਿੱਚ ਖੋਜੋ:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "ਖੋਜ ਸੈਟਿੰਗਾਂ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "ਇਸ ਵਾਰ ਇਸ ਵਿੱਚ ਖੋਜੋ:"; + diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings index 1846d5c389b5c..68555022d742e 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "ਮੂਲ ਖੋਜ ਇੰਜਣ"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox ਸੁਝਾਅ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia ਸੁਝਾਅ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "ਬਦਲਵੇਂ ਖੋਜ ਇੰਜਣ"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "ਖੋਜ ਲਈ ਸੁਝਾਆਵਾਂ ਨੂੰ ਵੇਖੋ"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "ਸਿਰਨਾਵਾਂ ਪੱਟੀ - Firefox ਸੁਝਾਅ"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "ਸਿਰਨਾਵਾਂ ਪੱਟੀ - Ecosia ਸੁਝਾਅ"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox ਸੁਝਾਅ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia ਸੁਝਾਅ ਬਾਰੇ ਹੋਰ ਜਾਣੋ"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "ਪ੍ਰਾਈਵੇਟ ਸ਼ੈਸ਼ਨਾਂ ਵਿੱਚ Firefox ਸੁਝਾਅ ਵਲੋਂ ਸੁਝਾਆਵਾਂ ਨੂੰ ਵੇਖਾਓ"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "ਪ੍ਰਾਈਵੇਟ ਸ਼ੈਸ਼ਨਾਂ ਵਿੱਚ Ecosia ਸੁਝਾਅ ਵਲੋਂ ਸੁਝਾਆਵਾਂ ਨੂੰ ਵੇਖਾਓ"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "ਬਰਾਊਜ਼ਿੰਗ ਅਤੀਤ ਖੋਜੋ"; diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/AddressToolbar.strings index 6fab94203d5d4..f5ce504385a2b 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Ustawienia prywatności i bezpieczeństwa"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Otwiera wybór wyszukiwarki"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Wyszukiwarka: %@"; diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/FxANotification.strings new file mode 100644 index 0000000000000..2ace7db93e156 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Zamknięto karty przeglądarki %1$@: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Wyświetl ostatnio zamknięte karty"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..54414161e808d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Ostatnia godzina"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/MainMenu.strings index 44eb7a4092c0a..76571263ede53 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Zapisz"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Podmenu Zachowaj"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Wersja na komputery"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Narzędzia"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podmenu Narzędzia"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Znajdź na stronie…"; diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..f75b14ba38053 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Zamknij"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Otwiera ustawienia wyszukiwania"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Ustawienia wyszukiwania"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Tym razem szukaj w:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Ustawienia wyszukiwania"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Tym razem szukaj w:"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings index c3c65f001ff1c..35ce1a09290c0 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Domyślna wyszukiwarka"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Więcej informacji o podpowiedziach Firefoksa"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Więcej informacji o podpowiedziach Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatywne wyszukiwarki"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Podpowiedzi wyszukiwania"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Pasek adresu – podpowiedzi Firefoksa"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Pasek adresu – podpowiedzi Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Więcej informacji o podpowiedziach Firefoksa"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Więcej informacji o podpowiedziach Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Podpowiedzi Firefoksa w prywatnych sesjach"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Podpowiedzi Ecosia w prywatnych sesjach"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Historia"; diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..e0b301a270585 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Última hora"; + diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/MainMenu.strings index bcc6c08c0f96d..553b429162033 100644 --- a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Salvar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Submenu de salvar"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Mudar para site de computador"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Ferramentas"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Submenu de ferramentas"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Procurar na página…"; diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings index 28cf8903ebf40..6454b59702fd5 100644 --- a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Mecanismo de pesquisa padrão"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saiba mais sobre o Sugestões Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saiba mais sobre o Sugestões Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Mecanismos de pesquisa alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostrar sugestões de pesquisa"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de endereços - Sugestões Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de endereços - Sugestões Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saiba mais sobre o Sugestões Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saiba mais sobre o Sugestões Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugestões do Firefox em sessões privativas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugestões do Ecosia em sessões privativas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Pesquisar no histórico de navegação"; diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..e0b301a270585 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Última hora"; + diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/MainMenu.strings index 00560cc11d279..427402783cc9b 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Guardar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sub-menu Guardar"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Mudar para a versão de computador"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Ferramentas"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Sub-menu Ferramentas"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Localizar na página…"; diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings index 4e9b687930009..5257b39f4264a 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motor de pesquisa predefinido"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saiba mais sobre o Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saiba mais sobre o Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motores de pesquisa alternativos"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mostrar sugestões de pesquisa"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de endereços – Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Barra de endereços – Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saiba mais sobre o Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saiba mais sobre o Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugestões do Firefox Suggest em sessões privadas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mostrar sugestões do Ecosia Suggest em sessões privadas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Pesquisar no histórico de navegação"; diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/AddressToolbar.strings index 13143b2a3d42a..251b69184211b 100644 --- a/firefox-ios/Shared/Supporting Files/rm.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Parameters per sfera privata e segirezza"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Avra la selecziun da la maschina da tschertgar"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Maschina da tschertgar: %@"; diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/FxANotification.strings new file mode 100644 index 0000000000000..738fb4730776c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Tabs da %1$@ serrads: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Mussar ils tabs serrads dacurt"; + diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f5f6191675ca1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "L’ultima ura"; + diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/MainMenu.strings index 55f050d84c5b8..2d1355e68c3c0 100644 --- a/firefox-ios/Shared/Supporting Files/rm.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Memorisar"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Sutmenu «Memorisar»"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "A la versiun per visurs gronds"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Utensils"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Sutmenu «Utensils»"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Tschertgar en la pagina…"; diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..984a42134d7f3 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Serrar"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Avra ils parameters da tschertga"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Parameters da tschertga"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Questa giada tschertgar en:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Parameters da tschertga"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Questa giada tschertgar en:"; + diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/Settings.strings index ac67fa12e21aa..b24bf05081cc1 100644 --- a/firefox-ios/Shared/Supporting Files/rm.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Maschina da tschertgar da standard"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ulteriuras infurmaziuns davart propostas da Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ulteriuras infurmaziuns davart propostas da Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Maschinas da tschertgar alternativas"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Mussar propostas da tschertga"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Trav d’adressas – propostas da Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Trav d’adressas – propostas da Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ulteriuras infurmaziuns davart propostas da Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ulteriuras infurmaziuns davart propostas da Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mussar las propostas da Firefox Suggest en sesidas privatas"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Mussar las propostas da Ecosia Suggest en sesidas privatas"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Tschertgar en la cronologia da navigaziun"; diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..a10ce92aaa55f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "За последний час"; + diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/MainMenu.strings index 00bcef7d1923d..02a5acb8064fb 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Сохранить"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Подменю Сохранить"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Перейти на версию для ПК"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Инструменты"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Подменю Инструменты"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Найти на странице…"; diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings index ee1358dca57b2..23bbf650a1a3d 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Поисковая система по умолчанию"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Узнайте больше о Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Узнайте больше о Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Альтернативные поисковые системы"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Показывать поисковые предложения"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адресная строка - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Адресная строка - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Узнайте больше о Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Узнайте больше о Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Отображать предложения из Firefox Suggest в приватных сеансах"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Отображать предложения из Ecosia Suggest в приватных сеансах"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Поиск по истории просмотра"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/AddressToolbar.strings index 9033397d74a0e..aad7a5813bb59 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "ᱯᱨᱟᱭᱣᱮᱥᱤ ᱟᱨ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱥᱟᱡᱟᱣᱠᱚ"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ ᱵᱟᱪᱷᱟᱣ ᱡᱷᱤᱡᱽ ᱟ"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ: %@"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EnhancedTrackingProtection.strings index 15e636656488e..ce91747a259cb 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EnhancedTrackingProtection.strings @@ -73,3 +73,45 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "ᱥᱟᱸᱣᱛᱟ ᱢᱤᱰᱤᱭᱟ ᱯᱟᱧᱡᱟ ᱫᱟᱱᱟᱲ ᱠᱚ ᱺ %@"; +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ +"Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "ᱴᱨᱮᱠᱟᱨ ᱠᱚ ᱵᱞᱚᱠ ᱮᱱᱟ: %@"; + +/* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ +"Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "ᱫᱟᱨᱟᱭ ᱛᱮ ᱧᱮᱞ ᱢᱮᱲᱟᱣ %@"; + +/* The title for the button that allows users to view certificates inside the enhanced tracking protection details screen. */ +"Menu.EnhancedTrackingProtection.Details.ViewCertificatesTitle.v131" = "ᱯᱚᱨᱢᱟᱱ ᱥᱟᱠᱟᱢ ᱧᱮᱞ ᱢᱮ"; + +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "%@ ᱫᱚ ᱵᱟᱭ ᱠᱟᱹᱢᱤ ᱠᱟᱱᱟᱭ ᱾ ᱟᱞᱮ ᱫᱚ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱠᱚ ᱫᱚᱦᱲᱟ ᱪᱟᱹᱞᱩ ᱞᱟᱹᱜᱤᱫ ᱞᱮ ᱥᱚᱞᱦᱟ ᱮᱫᱟ᱾"; + +/* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ +"Menu.EnhancedTrackingProtection.Off.Title.v128" = "ᱟᱢ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱠᱚ ᱵᱚᱱᱫᱚ ᱠᱮᱫᱟᱢ"; + +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ +"Menu.EnhancedTrackingProtection.On.Header.v128" = "ᱟᱢ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱜᱮᱭᱟᱢ ᱾ ᱡᱩᱫᱤ ᱟᱞᱮ ᱡᱟᱦᱟᱱᱟᱜ ᱞᱮ ᱧᱮᱞ ᱧᱟᱢ ᱠᱮᱫᱟ, ᱟᱞᱮ ᱟᱢ ᱞᱮ ᱵᱟᱰᱟᱭ ᱚᱪᱚ ᱢᱮᱭᱟ ᱾"; + +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "ᱟᱢᱟᱜ ᱡᱩᱲᱟᱣ ᱫᱚ ᱵᱟᱝ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱜᱮᱭᱟ ᱾"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "ᱱᱟᱶᱟ ᱥᱟᱭᱤᱴ ᱨᱮ ᱥᱟᱵᱫᱷᱟᱱ ᱛᱟᱦᱮᱸᱱ ᱢᱮ ᱾"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "%@ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱨᱮ ᱢᱮᱱᱟᱜ ᱠᱟᱹᱫᱮᱭᱟ"; + +/* The title for the privacy settings button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "ᱱᱤᱥᱚᱱ ᱥᱟᱡᱟᱣᱠᱚ"; + +/* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.Switch.Title.v128" = "ᱵᱟᱲᱦᱟᱣᱟᱠᱟᱱ ᱯᱟᱸᱡᱟ ᱨᱚᱯᱷᱟ"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "ᱨᱩᱠᱷᱤᱭᱟᱹ ᱫᱚ ᱵᱚᱱᱫᱚ ᱜᱮᱭᱟ ᱾ ᱟᱞᱮ ᱫᱚ ᱱᱚᱣᱟ ᱫᱚᱦᱲᱟ ᱪᱟᱹᱞᱩ ᱞᱟᱹᱜᱤᱫ ᱥᱚᱞᱦᱟ ᱞᱮ ᱮᱢᱟᱭᱟ ᱾"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "ᱨᱩᱠᱷᱤᱭᱟᱹ ᱫᱚ ᱵᱚᱱᱫᱚ ᱜᱮᱭᱟ ᱾ ᱟᱞᱮ ᱩᱱᱠᱩ ᱫᱚᱦᱲᱟ ᱪᱟᱹᱞᱩ ᱞᱟᱹᱜᱤᱫ ᱥᱚᱞᱦᱟ ᱞᱮ ᱮᱢᱟᱫ ᱠᱚᱣᱟ ᱾"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "ᱡᱩᱫᱤ ᱱᱚᱶᱟ ᱥᱟᱭᱤᱴ ᱨᱮ ᱡᱟᱦᱟᱱᱟᱜ ᱨᱟᱹᱯᱩᱫ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ, ᱮᱱᱠᱷᱟᱱ ᱚᱱᱟ ᱵᱚᱱᱫᱚ ᱨᱮᱭᱟᱜ ᱠᱩᱨᱩᱢᱩᱴᱩᱭ ᱢᱮ ᱾"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxHomepage.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxHomepage.strings new file mode 100644 index 0000000000000..d496e05fa8315 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxHomepage.strings @@ -0,0 +1,12 @@ +/* When the user ends their private session, they are returned to the private mode homepage, and a toastbar popups confirming that their data has been erased. This is the label for that toast. */ +"FirefoxHomepage.FeltDeletion.Link.v122" = "ᱱᱤᱡᱚᱨᱟᱜ ᱵᱽᱨᱟᱣᱩᱡᱤᱝ ᱰᱟᱴᱟ ᱢᱮᱴᱟᱣᱮᱱᱟ"; + +/* The body of the message for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. Placeholder refers to app name. */ +"FirefoxHomepage.FeltPrivacyUI.Body.v122" = "%@ ᱟᱢᱟᱜ ᱠᱩᱠᱤ, ᱱᱟᱜᱟᱢ, ᱟᱨ ᱥᱟᱭᱤᱴ ᱰᱟᱴᱟ ᱢᱮᱴᱟᱣ ᱟ, ᱡᱟᱦᱟᱸ ᱛᱤᱱ ᱨᱮ ᱟᱢ ᱟᱢᱟᱜ ᱡᱷᱚᱛᱚ ᱯᱨᱟᱭᱣᱮᱴ ᱴᱮᱵᱽ ᱠᱚ ᱵᱚᱸᱫᱚᱭ ᱟ ᱾"; + +/* The link for the card that educates users about how private mode works. The link redirects to an external site for more information. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Link.v122" = "ᱚᱠᱚᱭ ᱫᱚ ᱤᱧᱟᱜ ᱠᱟᱹᱢᱤ ᱦᱚᱨᱟ ᱠᱚ ᱧᱮᱞ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ?"; + +/* The title for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Title.v122" = "ᱱᱚᱶᱟ ᱥᱟᱫᱷᱚᱱ ᱨᱮ ᱡᱟᱦᱟᱱ ᱪᱤᱱᱦᱟᱹ ᱟᱞᱚᱢ ᱫᱚᱦᱚᱭᱟ ᱾"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxLogins.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxLogins.strings new file mode 100644 index 0000000000000..7776bf45ee720 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxLogins.strings @@ -0,0 +1,18 @@ +/* Prompt for saving the username in the Save Logins prompt. */ +"LoginsHelper.PromptSaveLogin.Title.v122" = "ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ ᱥᱟᱧᱪᱟᱣ ᱟ ?"; + +/* Prompt for saving a password in the Save Logins prompt. */ +"LoginsHelper.PromptSavePassword.Title.v122" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱚ ᱥᱟᱺᱪᱟᱣᱟ ?"; + +/* Prompt for updating the password in the Update Password prompt. */ +"LoginsHelper.PromptUpdateLogin.Title.OneArg.v122" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱦᱟᱹᱞᱤᱭᱟᱹᱠ ᱟ ?"; + +/* Prompt for updating a password in the Update Password prompt. */ +"LoginsHelper.PromptUpdateLogin.Title.TwoArg.v122" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱦᱟᱹᱞᱤᱭᱟᱹᱠ ᱟ ?"; + +/* Placeholder text for search box in logins list view. */ +"LoginsList.LoginsListSearchPlaceholder.v122" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱚ ᱫᱮᱠᱷᱟᱣ ᱢᱮ"; + +/* Title for the list of logins saved by the app */ +"LoginsList.Title.v122" = "ᱥᱟᱧᱪᱟᱣ ᱠᱟᱱ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱠᱚ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxSync.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxSync.strings index 57416441164fa..1cb31b67c70bb 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxSync.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FirefoxSync.strings @@ -1,3 +1,9 @@ +/* Toggle for address autofill syncing setting */ +"FirefoxSync.AddressAutofillEngine.v124" = "ᱴᱷᱤᱠᱬᱟᱹᱠᱚ"; + /* Toggle for credit cards syncing setting */ -"FirefoxSync.CreditCardsEngine.v115" = "ᱠᱨᱮᱰᱤᱴ ᱠᱟᱰ"; +"FirefoxSync.CreditCardsEngine.v122" = "ᱜᱚᱱᱚᱝ ᱦᱚᱨᱟᱠᱚ"; + +/* Toggle passwords syncing setting, in the Settings > Sync Data menu of the app. */ +"Sync.LoginsEngine.Title.v122" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱚ"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FxANotification.strings new file mode 100644 index 0000000000000..9171f7138830d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ ᱴᱮᱵᱽᱠᱚ ᱵᱚᱸᱫ ᱠᱟᱱᱟ : %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "ᱱᱤᱛᱚᱜᱽᱼᱟᱜ ᱵᱚᱸᱫᱚᱼᱟᱜ ᱴᱮᱵᱽ ᱠᱚ ᱫᱮᱠᱷᱟᱣ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/KeyboardAccessory.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/KeyboardAccessory.strings new file mode 100644 index 0000000000000..036dcf9d1d1b6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/KeyboardAccessory.strings @@ -0,0 +1,6 @@ +/* Accessibility label for next button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.NextButton.Accessibility.Label.v124" = "ᱤᱱᱟᱹ ᱛᱟᱭᱚᱢᱟᱜ ᱯᱷᱚᱨᱢ ᱯᱮᱨᱮᱡ ᱡᱟᱭᱜᱟ"; + +/* Accessibility label for previous button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.PreviousButton.Accessibility.Label.v124" = "ᱢᱟᱲᱟᱝᱟᱜ ᱯᱷᱚᱨᱢ ᱯᱮᱨᱮᱡ ᱡᱟᱭᱜᱟ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..1435bfedc8b40 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "ᱢᱩᱪᱟᱹᱫ ᱴᱟᱲᱟᱝ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/LoginsHelper.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/LoginsHelper.strings new file mode 100644 index 0000000000000..7683870e05ca5 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/LoginsHelper.strings @@ -0,0 +1,9 @@ +/* Button to not save the user's password in the logins helper */ +"LoginsHelper.DontSave.Button.v122" = "ᱱᱤᱛᱚᱜ ᱫᱚ ᱵᱟᱝᱟ"; + +/* Button to not update the user's password in the logins helper */ +"LoginsHelper.DontUpdate.Button.v122" = "ᱱᱤᱛᱚᱜ ᱫᱚ ᱵᱟᱝᱟ"; + +/* Button to save the user's password */ +"LoginsHelper.SaveLogin.Button.v122" = "ᱥᱟᱺᱪᱟᱣ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/MainMenu.strings new file mode 100644 index 0000000000000..6bc9412be6d6e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/MainMenu.strings @@ -0,0 +1,288 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the body text for the hint. */ +"ContextualHints.MainMenu.NewMenu.Body.v132" = "ᱯᱨᱟᱭᱣᱮᱴ ᱵᱽᱨᱟᱣᱡᱤᱝ ᱠᱷᱚᱱ ᱠᱟᱹᱴᱷᱤᱦᱚᱨᱟᱠᱚ ᱥᱟᱧᱪᱟᱣ ᱫᱷᱟᱹᱵᱤᱡ, ᱯᱟᱱᱛᱮ ᱢᱮ ᱡᱟᱦᱟᱸ ᱫᱚ ᱟᱢ ᱞᱟᱹᱠᱛᱤᱭᱟᱜ ᱠᱟᱱᱟ ᱾"; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user opens the new menu design for the first time, this contextual hint appears. This is the title for the hint. */ +"ContextualHints.MainMenu.NewMenu.Title.v132" = "ᱱᱟᱶᱟ ᱺ ᱡᱟᱣᱨᱟ ᱠᱟᱱ ᱢᱮᱱᱩ"; + +/* The accessibility label for the back button in the Main menu header navigation view. */ +"MainMenu.Account.AccessibilityLabels.BackButton.v132" = "ᱛᱟᱭᱚᱢ"; + +/* The accessibility label for the close button in the Main menu. */ +"MainMenu.Account.AccessibilityLabels.CloseButton.v132" = "ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* The accessibility label for the sign in button in the Main menu header view. */ +"MainMenu.Account.AccessibilityLabels.MainButton.v132" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ, ᱴᱮᱵᱽ ᱟᱨ ᱟᱨᱦᱚᱸ ᱟᱭᱢᱟ ᱡᱤᱱᱤᱥᱠᱚ ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱞᱟᱹᱜᱤᱫ ᱥᱩᱦᱤ ᱮᱢ ᱢᱮ"; + +/* On the main menu, at the top, when the user is signed out. The description for the sign in action */ +"MainMenu.Account.SignedOut.Description.v131" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ, ᱴᱮᱵᱽ ᱟᱨ ᱟᱨᱦᱚᱸ ᱟᱭᱢᱟ ᱡᱤᱱᱤᱥᱠᱚ ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱢᱮ"; + +/* On the main menu, at the top, when the user is signed out. The title for the sign in action */ +"MainMenu.Account.SignedOut.Title.v131" = "ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state. */ +"MainMenu.Account.SyncError.Description.v131" = "ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱛᱷᱩᱢ ᱠᱟᱱᱟ"; + +/* On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state. */ +"MainMenu.Account.SyncError.Title.v131" = "ᱥᱭᱝᱠ ᱞᱟᱹᱜᱤᱫ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Bookmarks.v132" = "ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Downloads.v132" = "ᱰᱟᱣᱱᱞᱚᱰ ᱠᱚ"; + +/* On the main menu, the accessibility label for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.History.v132" = "ᱱᱟᱜᱟᱢ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.AccessibilityLabels.Passwords.v132" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱚ"; + +/* On the main menu, the title for the action that will take the user to the Bookmarks panel. */ +"MainMenu.PanelLinkSection.Bookmarks.Title.v131" = "ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ"; + +/* On the main menu, the title for the action that will take the user to the Downloads panel. */ +"MainMenu.PanelLinkSection.Downloads.Title.v131" = "ᱰᱟᱣᱱᱞᱚᱰ ᱠᱚ"; + +/* On the main menu, the title for the action that will take the user to the History panel. */ +"MainMenu.PanelLinkSection.History.Title.v131" = "ᱱᱟᱜᱟᱢ"; + +/* On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. */ +"MainMenu.PanelLinkSection.Passwords.Title.v131" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱚ"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Customize Homepage section in the settings screen. */ +"MainMenu.SettingsSection.AccessibilityLabels.CustomizeHomepage.v132" = "ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱠᱩᱥᱤᱛᱮ ᱫᱚᱦᱚᱭ ᱢᱮ"; + +/* On the main menu, the accessibility labels for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.AccessibilityLabels.GetHelp.v132" = "ᱜᱚᱲᱚ ᱤᱫᱤ ᱢᱮ"; + +/* On the main menu, the accessibility labels for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.AccessibilityLabels.Settings.v132" = "ᱥᱟᱡᱟᱣᱠᱚ"; + +/* On the main menu, the accessibility labels for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.AccessibilityLabels.WhatsNew.v132" = "%@ ᱨᱮ ᱱᱟᱶᱟ ᱡᱤᱱᱤᱥ"; + +/* On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen. */ +"MainMenu.SettingsSection.CustomizeHomepage.Title.v131" = "ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱠᱩᱥᱤᱛᱮ ᱫᱚᱦᱚᱭ ᱢᱮ"; + +/* On the main menu, the title for the action that will take the user to a website to get help from Mozilla. */ +"MainMenu.SettingsSection.GetHelp.Title.v131" = "ᱜᱚᱲᱚ ᱤᱫᱤ ᱢᱮ"; + +/* On the main menu, the title for the action that will take the user to the Settings menu. */ +"MainMenu.SettingsSection.Settings.Title.v131" = "ᱥᱟᱡᱟᱣ ᱠᱚ"; + +/* On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name. */ +"MainMenu.SettingsSection.WhatsNew.Title.v131" = "%@ ᱨᱮ ᱱᱟᱶᱟ ᱡᱤᱱᱤᱥ"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Add to Home screen tool for iOS Home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Subtitle.v132" = "ᱚᱲᱟᱜ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add a website to the iOS home screen. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToHomeScreen.Title.v132" = "ᱚᱲᱟᱜ ᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫᱽ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Subtitle.v132" = "ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.AddToShortcuts.Title.v132" = "ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱥᱮᱞᱮᱫᱽ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Subtitle.v132" = "ᱵᱩᱠᱢᱟᱨᱠ ᱥᱮᱞᱮᱫᱢᱮ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.AccessibilityLabels.BookmarkThisPage.Title.v132" = "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱵᱩᱩᱠᱢᱟᱨᱠ ᱢᱮ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.AccessibilityLabels.EditBookmark.Title.v132" = "ᱵᱩᱠᱢᱟᱨᱠ ᱥᱟᱯᱲᱟᱣ ᱢᱮ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromReadingList.Title.v132" = "ᱯᱟᱰᱦᱟᱣ ᱞᱤᱥᱴᱤ ᱠᱷᱚᱱ ᱚᱪᱚᱜ ᱢᱮ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AccessibilityLabels.RemoveFromShortcuts.Title.v132" = "ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱠᱷᱚᱱ ᱚᱪᱚᱜᱽ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Subtitle.v132" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveAsPDF.Title.v132" = "PDF ᱞᱮᱠᱷᱟ ᱥᱟᱧᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Subtitle.v132" = "ᱯᱟᱲᱦᱟᱣ ᱞᱤᱥᱴᱤ"; + +/* On the main menu, in the Save submenu, the accessibility label for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.AccessibilityLabels.SaveToReadingList.Title.v132" = "ᱯᱟᱰᱦᱟᱣ ᱞᱤᱥᱴᱤ ᱨᱮ ᱥᱟᱧᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool. */ +"MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131" = "ᱚᱲᱟᱜ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to add a website to the home screen. */ +"MainMenu.Submenus.Save.AddToHomeScreen.Title.v131" = "ᱚᱲᱟᱜ ᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫᱽ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool. */ +"MainMenu.Submenus.Save.AddToShortcuts.Subtitle.v131" = "ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to add the current website to the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.AddToShortcuts.Title.v131" = "ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱥᱮᱞᱮᱫᱽ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool. */ +"MainMenu.Submenus.Save.BookmarkThisPage.Subtitle.v131" = "ᱵᱩᱠᱢᱟᱨᱠ ᱥᱟᱯᱲᱟᱣ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to save a bookmark for this particular page.. */ +"MainMenu.Submenus.Save.BookmarkThisPage.Title.v131" = "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱵᱩᱩᱠᱢᱟᱨᱠ ᱢᱮ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to edit the bookmark for this particular page. */ +"MainMenu.Submenus.Save.EditBookmark.Title.v131" = "ᱵᱩᱠᱢᱟᱨᱠ ᱥᱟᱯᱲᱟᱣ ᱢᱮ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to remove this site from the reading list. */ +"MainMenu.Submenus.Save.RemoveFromReadingList.Title.v131" = "ᱯᱟᱰᱦᱟᱣ ᱞᱤᱥᱴᱤ ᱠᱷᱚᱱ ᱚᱪᱚᱜ ᱢᱮ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows a user to remove the current website from the shortcuts on the homepage. */ +"MainMenu.Submenus.Save.RemoveFromShortcuts.Title.v131" = "ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱠᱷᱚᱱ ᱚᱪᱚᱜᱽ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool. */ +"MainMenu.Submenus.Save.SaveAsPDF.Subtitle.v131" = "PDF"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool. */ +"MainMenu.Submenus.Save.SaveAsPDF.Title.v131" = "PDF ᱞᱮᱠᱷᱟ ᱥᱟᱧᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Reading List tool. */ +"MainMenu.Submenus.Save.SaveToReadingList.Subtitle.v131" = "ᱯᱟᱲᱦᱟᱣ ᱞᱤᱥᱴᱤ"; + +/* On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list. */ +"MainMenu.Submenus.Save.SaveToReadingList.Title.v131" = "ᱯᱟᱰᱦᱟᱣ ᱞᱤᱥᱴᱤ ᱨᱮ ᱥᱟᱧᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Off.Title.v132" = "ᱧᱩᱛ ᱢᱳᱰ ᱵᱚᱱᱫᱚᱭ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.On.Title.v132" = "ᱧᱩᱛ ᱢᱳᱰ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.NightMode.Subtitle.v132" = "ᱧᱩᱛ ᱢᱳᱰ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Subtitle.v132" = "ᱪᱷᱟᱯᱟ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Print.Title.v132" = "ᱪᱷᱟᱯᱟ"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Off.Title.v132" = "ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞᱡᱚᱝ ᱞᱮᱠᱷᱟ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.On.Title.v132" = "ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞᱡᱚᱝ ᱞᱮᱠᱷᱟ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReaderView.Subtitle.v132" = "ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool and is indicating that under the Tools submenu, a Report (Report Broken Site) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Subtitle.v132" = "ᱠᱷᱚᱵᱚᱨ"; + +/* On the main menu, the accessibility label for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.ReportBrokenSite.Title.v132" = "ᱢᱤᱫᱴᱟᱝ ᱨᱟᱹᱯᱩᱫ ᱥᱟᱭᱤᱴ ᱠᱷᱚᱵᱚᱨ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Share tool and is indicating that under the Tools submenu, a Share (to someone else) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Subtitle.v132" = "ᱦᱟᱹᱴᱤᱧ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action (Share with others) that will take the user/open (to) the Share submenu. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Share.Title.v132" = "ᱦᱟᱹᱴᱤᱧ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu accessibility label, indicating what kind of tools are available in that menu. This string is for the Zoom tool and is indicating that under the Tools submenu, a Zoom (apply zoom on a page) action is available. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Subtitle.v132" = "ᱡᱩᱢ"; + +/* On the main menu, in the tools submenu, the accessibility label for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.AccessibilityLabels.Zoom.Title.v132" = "ᱡᱩᱢ (%@)"; + +/* On the main menu, the title for the action that will turn Night Mode off in the application. */ +"MainMenu.Submenus.Tools.NightMode.Off.Title.v131" = "ᱧᱩᱛ ᱢᱳᱰ ᱵᱚᱱᱫᱚᱭ ᱢᱮ"; + +/* On the main menu, the title for the action that will turn Night Mode on in the application. */ +"MainMenu.Submenus.Tools.NightMode.On.Title.v131" = "ᱧᱩᱛ ᱢᱳᱰ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Night Mode tool. */ +"MainMenu.Submenus.Tools.NightMode.Subtitle.v131" = "ᱧᱩᱛ ᱢᱳᱰ"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Print tool. */ +"MainMenu.Submenus.Tools.Print.Subtitle.v131" = "ᱪᱷᱟᱯᱟ"; + +/* On the main menu, the title for the action that will take the user to the Print module in the application. */ +"MainMenu.Submenus.Tools.Print.Title.v131" = "ᱪᱷᱟᱯᱟ"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.Off.Title.v131" = "ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞᱡᱚᱝ ᱞᱮᱠᱷᱟ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* On the main menu, the title for the action that will turn the reader view on for the current website. */ +"MainMenu.Submenus.Tools.ReaderView.On.Title.v131" = "ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞᱡᱚᱝ ᱞᱮᱠᱷᱟ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool. */ +"MainMenu.Submenus.Tools.ReaderView.Subtitle.v131" = "ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞ"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131" = "ᱠᱷᱚᱵᱚᱨ"; + +/* On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team. */ +"MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131" = "ᱢᱤᱫᱴᱟᱝ ᱨᱟᱹᱯᱩᱫ ᱥᱟᱭᱤᱴ ᱠᱷᱚᱵᱚᱨ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool. */ +"MainMenu.Submenus.Tools.Share.Subtitle.v131" = "ᱦᱟᱹᱴᱤᱧ ᱢᱮ"; + +/* On the main menu, the title for the action that will take the user to the Share module in the application. */ +"MainMenu.Submenus.Tools.Share.Title.v131" = "ᱦᱟᱹᱴᱤᱧ ᱢᱮ"; + +/* On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool. */ +"MainMenu.Submenus.Tools.Zoom.Subtitle.v131" = "ᱡᱩᱢ"; + +/* On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. */ +"MainMenu.Submenus.Tools.Zoom.Title.v131" = "ᱡᱩᱢ (%@)"; + +/* The accessibility label for the Main Menu. */ +"MainMenu.TabsSection.AccessibilityLabels.MainMenu.v132" = "ᱢᱩᱞ ᱢᱮᱱᱩ"; + +/* On the main menu, the accessibility label for the action that will create a new private tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewPrivateTab.v132" = "ᱱᱟᱶᱟ ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵᱽ"; + +/* On the main menu, the accessibility label for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.AccessibilityLabels.NewTab.v132" = "ᱱᱟᱶᱟ ᱴᱮᱵᱽ"; + +/* On the main menu, the title for the action that will create a new private tab. */ +"MainMenu.TabsSection.NewPrivateTab.Title.v131" = "ᱱᱟᱶᱟ ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵ"; + +/* On the main menu, the title for the action that will create a new, non-private, tab. */ +"MainMenu.TabsSection.NewTab.Title.v131" = "ᱱᱟᱶᱟ ᱴᱮᱵᱽ"; + +/* On the main menu, the accessibility label for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.AccessibilityLabels.FindInPage.v132" = "ᱥᱟᱦᱴᱟ ᱨᱮ ᱯᱟᱱᱛᱮ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "ᱥᱟᱺᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "ᱥᱚᱵᱢᱮᱱᱩ ᱥᱟᱧᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "ᱰᱮᱥᱠᱴᱚᱯ ᱥᱟᱭᱤᱴ ᱛᱮ ᱪᱟᱞᱟᱜ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.AccessibilityLabels.SwitchToMobileSite.v132" = "ᱢᱳᱵᱟᱭᱤᱞ ᱥᱟᱭᱤᱴ ᱛᱮ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱚ"; + +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "ᱢᱩᱞ ᱥᱚᱵᱢᱮᱱᱩ"; + +/* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ +"MainMenu.ToolsSection.FindInPage.Title.v131" = "ᱥᱟᱠᱟᱢ ᱨᱮ ᱯᱟᱱᱛᱮ ᱢᱮ …"; + +/* On the main menu, the title for the action that will take the user to the Save submenu in the menu. */ +"MainMenu.ToolsSection.SaveSubmenu.Title.v131" = "ᱥᱟᱺᱪᱟᱣ ᱢᱮ"; + +/* On the main menu, the title for the action that will switch a site from mobile version to the desktop version, if available. */ +"MainMenu.ToolsSection.SwitchToDesktopSite.Title.v131" = "ᱰᱮᱥᱠᱴᱚᱯ ᱥᱟᱭᱤᱴ ᱛᱮ ᱪᱟᱞᱟᱜ ᱢᱮ"; + +/* On the main menu, the title for the action that will switch a site from the desktop version to the mobile version. */ +"MainMenu.ToolsSection.SwitchToMobileSite.Title.v131" = "ᱢᱳᱵᱟᱭᱤᱞ ᱥᱟᱭᱤᱴ ᱛᱮ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ"; + +/* On the main menu, the title for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.ToolsSubmenu.Title.v131" = "ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱠᱚ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Microsurvey.strings new file mode 100644 index 0000000000000..9db95fbc8c26b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Microsurvey.strings @@ -0,0 +1,72 @@ +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the button that appears on the prompt that allows the user to tap on and navigates them to the microsurvey to respond to. */ +"Microsurvey.Prompt.Button.v127" = "ᱞᱮᱛᱟᱲ"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.AccessibilityLabel.v127" = "ᱥᱚᱨᱵᱷᱮ ᱴᱟᱠᱟ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the logo image that appears on the prompt to inform the prompt is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Prompt.LogoImage.AccessibilityLabel.v129" = "%@ ᱪᱤᱱᱦᱟ."; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the text that appears on the prompt to inform the user that this is a prompt to take a survey. Placeholder is for the app name. */ +"Microsurvey.Prompt.TitleLabel.v127" = "%@ ᱵᱮᱥ ᱛᱮᱭᱟᱨ ᱨᱮ ᱜᱚᱲᱚ ᱮᱢᱟ ᱞᱮᱢ ᱾ ᱱᱚᱶᱟ ᱫᱚ ᱞᱚᱜᱚᱱ ᱜᱮ ᱦᱩᱭᱩᱜᱼᱟ ᱾"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of button on the survey that a user can tap on to submit their responses. */ +"Microsurvey.Survey.Button.v127" = "ᱡᱚᱢᱟᱭ ᱢᱮ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.AccessibilityLabel.v127" = "ᱥᱚᱨᱵᱷᱮ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.Button.AccessibilityLabel.v127" = "ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the text shown on the confirmation page when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.ConfirmationLabel.v127" = "ᱟᱢᱟᱜ ᱴᱤᱯᱚᱬᱤ ᱞᱟᱹᱜᱤᱫ ᱥᱟᱯᱲᱟᱣ!"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the title for the header on the microsurvey when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.HeaderLabel.v127" = "ᱥᱚᱨᱵᱷᱮ ᱯᱩᱨᱟᱹᱣ ᱮᱱᱟ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v127" = "ᱱᱚᱶᱟ ᱥᱚᱨᱵᱷᱮ ᱯᱩᱨᱟᱹᱣ ᱢᱮ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "ᱫᱟᱭᱟᱠᱟᱛᱮ ᱱᱚᱶᱟ ᱥᱚᱨᱵᱷᱮ ᱯᱩᱨᱟᱹᱣ ᱢᱮ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "%@ ᱪᱤᱱᱦᱟ."; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Dissatisfied.v132" = "ᱵᱟᱱᱴᱷᱤᱠ"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Neutral.v132" = "ᱱᱤᱣᱴᱨᱟᱞ"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.NotApplicable.v132" = "ᱤᱧ ᱚᱱᱟ ᱵᱟᱹᱧ ᱵᱮᱣᱦᱟᱨ ᱮᱫᱟ"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.Satisfied.v132" = "ᱨᱟᱹᱥᱠᱟᱹ"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VeryDissatisfied.v132" = "ᱟᱹᱰᱤ ᱵᱟᱱᱴᱷᱤᱠ"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.VerySatisfied.v132" = "ᱟᱹᱰᱤ ᱨᱟᱹᱥᱠᱟᱹ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%2$@ ᱠᱷᱚᱱ %1$@"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ +"Microsurvey.Survey.PrivacyPolicyLink.v127" = "ᱱᱤᱥᱚᱱ ᱨᱮᱭᱟᱜ ᱱᱚᱴᱤᱥ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "ᱵᱟᱪᱷᱟᱣᱮᱱᱟ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "ᱵᱟᱝ ᱵᱟᱪᱷᱟᱣ ᱟᱠᱟᱱᱟ"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "ᱥᱚᱨᱵᱷᱮ\n"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/NativeErrorPage.strings new file mode 100644 index 0000000000000..a18c7ed1e22e2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "ᱫᱚᱦᱲᱟ ᱞᱟᱫᱤ"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "ᱢᱤᱫ SSL ᱵᱷᱩᱞ ᱦᱩᱭ ᱟᱠᱟᱱᱟ ᱟᱨ ᱥᱚᱨᱵᱷᱟᱨ ᱥᱟᱶ ᱨᱟᱠᱷᱟ ᱡᱚᱯᱲᱟᱣ ᱵᱟᱝ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾"; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "ᱦᱩᱥᱤᱭᱟᱹᱨ ᱛᱟᱦᱮᱱ ᱢᱮ ᱾ ᱡᱟᱦᱟᱱᱟᱜ ᱜᱮ ᱴᱷᱤᱠ ᱵᱟᱝ ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ ᱾"; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "ᱮᱴᱟᱜ ᱰᱤᱵᱷᱟᱭᱤᱥ ᱨᱮ ᱡᱚᱯᱲᱟᱣ ᱨᱮᱭᱟᱜ ᱠᱩᱨᱩᱢᱩᱴᱩᱭ ᱢᱮ᱾ ᱟᱢᱟᱜ ᱢᱚᱰᱮᱢ ᱵᱟᱝᱠᱷᱟᱱ ᱨᱟᱣᱴᱟᱨ ᱧᱮᱞ ᱵᱤᱰᱟᱹᱣ ᱢᱮ ᱾ Wi-Fi ᱥᱟᱶ ᱰᱤᱥᱠᱚᱱᱮᱠᱴ ᱟᱨ ᱫᱚᱦᱲᱟ ᱡᱚᱯᱲᱟᱣ ᱢᱮ ᱾"; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "ᱧᱮᱞᱚᱜ ᱠᱟᱱᱟ ᱟᱢᱟᱜ ᱤᱱᱴᱟᱨᱱᱮᱴ ᱠᱚᱱᱮᱠᱥᱚᱱ ᱨᱮ ᱮᱴᱠᱮᱴᱚᱬᱮ ᱢᱮᱱᱟᱜᱼᱟ ᱾"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Onboarding.strings index ecea46c9e0bd9..d51f9667a84f6 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Onboarding.strings @@ -1,6 +1,9 @@ /* The title of the button on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ "DefaultBrowserPopup.ButtonTitle.v114" = "ᱥᱟᱡᱟᱣ ᱠᱚ ᱨᱮ ᱪᱟᱞᱟᱜ ᱢᱮ"; +/* The footer label on the Default Browser Popup, which is below all the instructions asking the users if their Firefox browser is the default browser. If it is then close this message and tap skip. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.DescriptionFooter.v124" = "*ᱪᱮᱫ %@ ᱞᱟᱦᱟ ᱠᱷᱚᱱ ᱜᱮ ᱟᱢᱟᱜ ᱰᱤᱯᱷᱚᱞᱴ ᱠᱟᱱᱟ?* ᱱᱚᱶᱟ ᱠᱷᱚᱵᱚᱨ ᱵᱚᱱᱫᱚ ᱢᱮ ᱟᱨ ᱵᱟᱹᱜᱤ ᱨᱮ ᱞᱤᱱ ᱢᱮ ᱾"; + /* The first label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ "DefaultBrowserPopup.FirstLabel.v114" = "1. *ᱥᱟᱡᱟᱣ* ᱛᱮ ᱪᱟᱞᱟᱜ ᱢᱮ"; @@ -13,6 +16,57 @@ /* The title on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ "DefaultBrowserPopup.Title.v114" = "ᱟᱢᱟᱜ ᱢᱩᱞ ᱯᱟᱱᱛᱮᱭᱟᱜ ᱵᱚᱫᱚᱞ ᱢᱮ"; +/* String used to describe the option to continue to the next onboarding card in Firefox Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Continue.Action.v123" = "%@ ᱠᱩᱥᱤᱭᱟᱜ ᱛᱮᱭᱟᱨ"; + +/* String used to describe the description label of the customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Intro.Description.v123" = "ᱟᱢᱟᱜ ᱵᱷᱮᱜᱟᱨ ᱵᱽᱨᱟᱣᱩᱡᱤᱝ ᱥᱴᱟᱭᱤᱞ ᱥᱟᱶ ᱢᱤᱞᱟᱹᱣ ᱞᱟᱹᱜᱤᱫ ᱟᱢᱟᱜ ᱛᱷᱤᱢ ᱟᱨ ᱴᱩᱞᱵᱟᱨ ᱥᱮᱴ ᱢᱮ ᱾"; + +/* String used to describe the option to skip the customization cards in Firefox Onboarding screens and start browsing. */ +"Onboarding.Customization.Intro.Skip.Action.v123" = "ᱵᱽᱨᱟᱣᱩᱡᱤᱝ ᱮᱦᱚᱵᱽ ᱢᱮ"; + +/* String used to describe the title of the customization onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Title.v123" = "%@ ᱫᱚ ᱠᱚᱵᱚᱡᱽ ᱨᱮ ᱫᱚᱦᱚ ᱢᱮᱭᱟᱭ"; + +/* String used to describe the option to save the user setting and continue to the next onboarding in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Continue.Action.v123" = "ᱥᱟᱧᱪᱟᱣ ᱟᱨ ᱞᱟᱦᱟᱜ ᱢᱮ"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to dark theme from the available choices. */ +"Onboarding.Customization.Theme.Dark.Action.v123" = "ᱧᱩᱛ"; + +/* String used to describe the description label of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Description.v123" = "ᱣᱮᱵᱽ ᱥᱚᱨᱮᱥ ᱢᱟᱨᱥᱟᱞ ᱨᱮ ᱧᱮᱞ ᱢᱮ᱾"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to light theme from the available choices. */ +"Onboarding.Customization.Theme.Light.Action.v123" = "ᱢᱟᱨᱥᱟᱞ"; + +/* String used to describe the option to skip the theme customization in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Skip.Action.v123" = "ᱟᱲᱟᱜ"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to system theme from the available choices. */ +"Onboarding.Customization.Theme.System.Action.v123" = "ᱥᱤᱥᱴᱟᱢ ᱚᱴᱚ"; + +/* String used to describe the title of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Title.v123" = "ᱢᱤᱫᱴᱟᱹᱝ ᱛᱷᱤᱢ ᱵᱟᱪᱷᱟᱣ ᱢᱮ"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the bottom of the screen. */ +"Onboarding.Customization.Toolbar.Bottom.Action.v123" = "ᱞᱟᱛᱟᱨ"; + +/* String used to describe the option to save set preferences and leave onboarding to start browsing in the app. */ +"Onboarding.Customization.Toolbar.Continue.Action.v123" = "ᱥᱟᱸᱪᱟᱣ ᱢᱮ ᱟᱨ ᱵᱽᱨᱟᱣᱩᱡᱤᱝ ᱮᱦᱚᱵ ᱢᱮ"; + +/* String used to describe the description label of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Description.v123" = "ᱥᱮᱸᱫᱽᱨᱟ ᱫᱚ ᱥᱮᱴᱮᱨ ᱵᱷᱤᱛᱨᱤ ᱨᱮ ᱫᱚᱦᱚ ᱠᱟᱜ ᱢᱮ᱾"; + +/* String used to describe the option to skip the toolbar customization in Firefox Onboarding screens and start browisg in the app. */ +"Onboarding.Customization.Toolbar.Skip.Action.v123" = "ᱟᱲᱟᱜ"; + +/* String used to describe the title of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Title.v123" = "ᱢᱤᱫᱴᱟᱹᱝ ᱴᱩᱞᱵᱟᱨ ᱯᱞᱮᱥᱢᱮᱱᱴ ᱨᱟᱠᱟᱵᱽ ᱢᱮ"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the top of the screen. */ +"Onboarding.Customization.Toolbar.Top.Action.v123" = "ᱪᱮᱛᱟᱱ"; + /* String used to describes what Firefox is on the first onboarding page in our Onboarding screens. Indie means small independant. */ "Onboarding.IntroDescriptionPart1.v114" = "ᱥᱟᱹᱫᱷᱤᱱ ᱾ ᱵᱟᱝ ᱞᱟᱵᱷ ᱾ ᱵᱮᱥ ᱞᱟᱹᱜᱤᱫ ᱾"; @@ -25,18 +79,33 @@ /* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.Continue.Action.v114" = "ᱞᱮᱛᱟᱲ"; +/* String used to describe the description of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Description.v120" = "ᱟᱢᱟᱜ ᱥᱟᱫᱷᱚᱱ ᱠᱚ ᱛᱟᱞᱟ ᱛᱮ ᱨᱩᱠᱷᱤᱭᱟ ᱛᱮ ᱴᱮᱵᱽ ᱠᱚ ᱵᱚᱫᱚᱞ ᱢᱮ ᱟᱨ %@ ᱨᱮ ᱮᱴᱟᱜ ᱯᱨᱟᱭᱵᱷᱮᱴ ᱯᱷᱤᱪᱚᱨ ᱠᱚ ᱯᱟᱱᱛᱮ ᱧᱟᱢ ᱢᱮ ᱾"; + /* String used to describe the option to skip the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.Skip.Action.v115" = "ᱟᱲᱟᱜ"; +/* String used to describe the title of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Title.v120" = "%@ ᱮᱛᱞᱟᱤᱭᱟᱹ ᱥᱟᱞᱟᱜ ᱟᱢ ᱴᱷᱤᱠ ᱨᱮ ᱫᱚᱦᱚᱢᱮ ᱞᱟᱹᱜᱤᱫ ᱜᱚᱲᱚ ᱮᱢᱚᱜᱼᱟᱭ"; + /* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.TurnOnNotifications.Action.v114" = "ᱠᱷᱚᱵᱚᱨ ᱠᱚ ᱦᱮᱡ ᱪᱷᱚᱭ ᱢᱮ"; +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v120" = "ᱡᱟᱦᱟᱸ ᱛᱤ ᱨᱮ ᱟᱢ ᱥᱩᱦᱤ ᱮᱢ ᱠᱟᱜᱼᱟᱢ ᱟᱨ ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱮᱢ ᱛᱟᱦᱮᱸᱱᱟ, ᱟᱢ ᱫᱚ ᱴᱷᱤᱴ ᱨᱮᱢ ᱛᱟᱦᱮᱸᱟ ᱾ %@ ᱟᱢᱟᱜ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫ, ᱯᱩᱛᱷᱤᱪᱤᱱᱦᱟᱹ, ᱟᱨᱦᱚᱸ ᱟᱭᱢᱟ ᱮᱴᱟᱜ ᱠᱚ ᱡᱤᱱᱤᱥ ᱠᱟᱨᱮ ᱫᱟᱱᱟᱝ ᱨᱮ ᱫᱟᱦᱚ ᱢᱮᱭᱟᱭ ᱾"; + +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v123" = "ᱟᱢᱟᱜ ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱡᱷᱚᱜ %@ ᱫᱚ ᱟᱢᱟᱜ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ, ᱵᱩᱩᱠᱢᱟᱨᱠ ᱟᱨ ᱰᱷᱮᱨ ᱡᱤᱱᱤᱥ ᱥᱟᱣᱛᱤ ᱫᱚᱦᱚᱭᱟᱭ ᱾"; + /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.SignIn.Action.v114" = "ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ"; /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.Skip.Action.v114" = "ᱟᱲᱟᱜ"; +/* String used to describes the title of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. */ +"Onboarding.Sync.Title.v120" = "ᱮᱴᱟᱜ ᱥᱟᱫᱷᱟᱱ ᱛᱮ ᱫᱚᱱ ᱡᱷᱚᱜ ᱫᱟᱱᱟᱝ ᱨᱮ ᱛᱟᱦᱮᱸᱱ ᱢᱮ"; + /* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ "Onboarding.Wallpaper.Accessibility.Classic.v114" = "ᱠᱞᱟᱥᱤᱠ ᱠᱟᱱᱛᱷᱪᱤᱛᱟᱹᱨ"; @@ -61,12 +130,24 @@ /* Describes the action on the first onboarding page in our Onboarding screen. This indicates that the user will set their default browser to Firefox. */ "Onboarding.Welcome.ActionTreatementA.v114" = "ᱢᱩᱞ ᱯᱟᱱᱛᱮᱭᱟᱜ ᱞᱮᱠᱟ ᱥᱟᱡᱟᱣ ᱢᱮ"; +/* Accessibility label for close button that dismisses the welcome onboarding screen. Placeholder is for the app name. */ +"Onboarding.Welcome.Close.AccessibilityLabel.v121" = "ᱵᱚᱱᱫᱚ ᱢᱮ ᱟᱨ ᱚᱰᱚᱠᱚᱜ ᱢᱮ %@ ᱚᱱᱵᱚᱨᱰᱤᱝ"; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Description.TreatementA.v120" = "ᱟᱞᱮᱭᱟᱜ ᱵᱮᱼᱯᱷᱟᱭᱫᱟ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱫᱚ ᱵᱟᱦᱨᱮ ᱠᱚᱢᱯᱟᱱᱤ ᱠᱚ ᱣᱮᱵᱽ ᱨᱮ ᱟᱢ ᱯᱟᱸᱡᱟ ᱠᱚ ᱵᱚᱸᱫᱚᱜᱼᱟ ᱾"; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Welcome.Description.v120" = "ᱟᱞᱮᱭᱟᱜ ᱵᱮᱼᱯᱷᱟᱭᱫᱟ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱫᱚ ᱵᱟᱦᱨᱮ ᱠᱚᱢᱯᱟᱱᱤ ᱠᱚ ᱣᱮᱵᱽ ᱨᱮ ᱟᱢ ᱯᱟᱸᱡᱟ ᱠᱚ ᱵᱚᱸᱫᱚᱜᱼᱟ ᱾"; + /* String used to describe the title of link button is on the welcome onboarding page for current version in our Onboarding screens. */ "Onboarding.Welcome.Link.Action.v114" = "ᱟᱞᱮᱭᱟᱜ ᱱᱤᱥᱚᱱ ᱱᱚᱴᱤᱥ ᱨᱮ ᱰᱷᱮᱨ ᱵᱟᱲᱟᱭ ᱢᱮ"; /* Describes the action on the first onboarding page in our Onboarding screen. This string will be on a button so user can skip this onboarding card. */ "Onboarding.Welcome.Skip.v114" = "ᱟᱲᱟᱜ"; +/* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Title.TreatementA.v120" = "ᱟᱢ ᱴᱷᱤᱠ ᱨᱮ ᱫᱚᱦᱚᱢᱮᱪᱷᱚ ᱞᱟᱹᱜᱤᱫ ᱠᱩᱥᱤᱭᱟᱜᱼᱟ ᱞᱮ"; + /* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ "Onboarding.Welcome.Title.v114" = "ᱥᱟᱹᱫᱷᱤᱱ ᱤᱱᱴᱚᱨᱱᱮᱴ ᱨᱮ ᱟᱢᱟᱜ ᱥᱟᱹᱜᱩᱱ ᱫᱟᱨᱟᱢ"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/PasswordAutofill.strings new file mode 100644 index 0000000000000..ae910a076f5ed --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/PasswordAutofill.strings @@ -0,0 +1,15 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱧᱩᱛᱩᱢ ᱵᱟᱹᱱᱩᱜᱼᱟ)"; + +/* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ +"PasswordAutofill.ManagePasswordsButton.v124" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫ ᱢᱮᱱᱮᱡᱽ ᱢᱮ"; + +/* This phrase is used as a subtitle in the header of password list screen, indicating to the user that they will be logging into a specific website (represented by %@) using a saved password. It's providing clarity on which website the saved credentials apply to. */ +"PasswordAutofill.SignInWithSavedPassword.v124" = "ᱟᱢ ᱫᱚ %@ ᱨᱮ ᱵᱟᱞᱚᱱ ᱞᱟᱹᱜᱤᱫ"; + +/* This label is used in the password list screen header as a question, prompting the user if they want to use a saved password for logging in. */ +"PasswordAutofill.UseSavedPasswordFromHeader.v124" = "ᱥᱟᱺᱪᱟᱣ ᱠᱟᱱ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; + +/* Displayed inside the keyboard hint when a user is entering their login credentials and has at least one saved password. Indicates that there are stored passwords available for use in filling out the login form. */ +"PasswordAutofill.UseSavedPasswordFromKeyboard.v124" = "ᱥᱟᱺᱪᱟᱣ ᱠᱟᱱ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱮᱵᱷᱟᱨ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/PasswordGenerator.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/PasswordGenerator.strings new file mode 100644 index 0000000000000..96f81d7cf4a21 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/PasswordGenerator.strings @@ -0,0 +1,27 @@ +/* Accessibility label describing a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.A11yLabel.v132" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫ ᱛᱮᱭᱟᱨᱤᱭᱟᱹ"; + +/* Accessibility label describing the close button for the popup related to a feature that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.CloseButtonA11ylabel.v132" = "ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* When a user is in the process of creating an account, they have the option to generate a password. The user is capable of copying this password after long pressing the value of the password displayed to them. This string is the label of the copy button that appears after long pressing the password. */ +"PasswordGenerator.CopyPasswordButtonLabel.v132" = "ᱱᱚᱠᱚᱞ"; + +/* Text displayed when a user interacts with the password field in a signup form, as part of a popup. This popup allows the user to generate a password that they have the option to use when signing up for an account. */ +"PasswordGenerator.Description.v132" = "ᱢᱤᱫ ᱠᱮᱴᱮᱡ, ᱟᱛᱷᱟᱹᱣᱲᱤ ᱛᱮ ᱥᱤᱨᱡᱟᱣ ᱟᱠᱟᱱ ᱯᱟᱥᱣᱟᱨᱰ ᱵᱮᱵᱷᱟᱨ ᱠᱟᱛᱮ ᱟᱢᱟᱜ ᱮᱠᱟᱣᱩᱱᱴ ᱨᱟᱠᱷᱟᱭ ᱢᱮ᱾"; + +/* When a user is in the process of creating an account, they have the option to generate a password. The popup displaying the generated password to the user is available by clicking a keyboard accessory button with this label. */ +"PasswordGenerator.KeyboardAccessoryButtonLabel.v132" = "ᱟᱸᱴᱷ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; + +/* Prefix to alert accessibility users that a generated password (represented by %@) will be read to them next. */ +"PasswordGenerator.PasswordReadoutPrefaceA11y.v132" = "ᱛᱮᱭᱟᱨ ᱠᱟᱱ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱠᱚ : %@"; + +/* Accessibility label describing a refresh password button belonging to a popup that generates a password when the password field of a signup form is interacted with. */ +"PasswordGenerator.RefreshPasswordButtonA11yLabel.v132" = "ᱢᱤᱫ ᱱᱟᱶᱟ ᱠᱮᱴᱮᱡ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫ ᱵᱮᱱᱟᱣ ᱢᱮ"; + +/* Title text displayed as part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user -- clicking a button fills in the password of the signup form with this generated password. */ +"PasswordGenerator.Title.v132" = "ᱟᱸᱴᱷ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱮᱵᱷᱟᱨ ᱟ ᱥᱮ ?"; + +/* Label of a button that is part of a popup displayed when a user interacts with the password field in a signup form. A random password has been generated for the user and clicking this button fills in the password field of the signup form with this generated password. */ +"PasswordGenerator.UsePasswordButtonLabel.v132" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/QRCode.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/QRCode.strings new file mode 100644 index 0000000000000..e94e83c350c90 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/QRCode.strings @@ -0,0 +1,3 @@ +/* Accessibility label of the QR code button in the toolbar */ +"QRCode.Toolbar.Button.A11y.Title.v128" = "QR ᱠᱳᱰ ᱥᱠᱟᱱ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/RememberCard.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/RememberCard.strings index 2cf7d6bbf7406..bca7bc6c2e863 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/RememberCard.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/RememberCard.strings @@ -1,3 +1,12 @@ +/* This value is used as the header for the remember card page. The placeholder is for the app name. */ +"CreditCard.RememberCard.Header.v122" = "%@ ᱫᱚ ᱟᱢᱟᱜ ᱠᱟᱰ ᱱᱚᱢᱵᱚᱨ ᱮ ᱮᱱᱠᱨᱤᱯᱴ ᱟ ᱾ ᱟᱢᱟᱜ ᱥᱤᱠᱭᱚᱨᱤᱴᱤ ᱠᱳᱰ ᱵᱟᱭ ᱥᱟᱧᱪᱟᱣᱜ ᱟ ᱾"; + +/* This value is used as the title for the Yes button in the remember credit card page */ +"CreditCard.RememberCard.MainButtonTitle.v122" = "ᱥᱟᱺᱪᱟᱣ ᱢᱮ"; + +/* This value is used as the title for the remember credit card page */ +"CreditCard.RememberCard.MainTitle.v122" = "ᱱᱚᱶᱟ ᱠᱟᱰ ᱫᱚ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱥᱟᱹᱦᱤᱡ ᱫᱚᱦᱚᱭ ᱟᱢ ᱥᱮ ?"; + /* This value is used as the title for the Not Now button in the remember credit card page */ "CreditCard.RememberCard.SecondaryButtonTitle.v115" = "ᱱᱤᱛᱚᱜ ᱫᱚ ᱵᱟᱝᱟ"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ScanQRCode.strings new file mode 100644 index 0000000000000..b1fab2ca23637 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "ᱦᱮᱥᱟᱨᱤᱭᱟᱹ"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "ᱢᱟᱱᱟ"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "%@ ᱡᱷᱤᱡ ᱪᱷᱚᱣᱟᱭᱟ ᱥᱮ ?"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..32f529b5ff64c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "ᱥᱮᱸᱫᱽᱨᱟ ᱥᱟᱡᱟᱣ ᱠᱚ ᱠᱷᱩᱞᱟᱹᱭᱟ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "ᱥᱮᱸᱫᱽᱨᱟ ᱥᱟᱡᱟᱣ ᱠᱚ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "ᱱᱤᱛᱚᱜ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ :"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "ᱥᱮᱸᱫᱽᱨᱟ ᱥᱟᱡᱟᱣ ᱠᱚ"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "ᱱᱤᱛᱚᱜ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ :"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SelectCreditCard.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SelectCreditCard.strings index 5769a824f5a65..bdc82d95d7d79 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SelectCreditCard.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SelectCreditCard.strings @@ -1,3 +1,3 @@ /* This value is used as the title for the select a credit card from list of available cards. */ -"CreditCard.SelectCreditCard.MainTitle.v116" = "ᱥᱟᱧᱪᱟᱣ ᱠᱟᱱ ᱠᱟᱰ ᱵᱮᱵᱷᱟᱨ ᱟᱢ ᱥᱮ ?"; +"CreditCard.SelectCreditCard.MainTitle.v122" = "ᱥᱟᱺᱪᱟᱣ ᱠᱟᱱ ᱠᱟᱰ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Settings.strings index 1d690e9b63b45..673e3eb1c5e0d 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Settings.strings @@ -1,18 +1,78 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "ᱴᱷᱤᱠᱬᱟᱹᱤᱭᱟᱹ ᱡᱚᱛᱚᱱ ᱮᱢ"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ ᱞᱟᱹᱜᱤᱫ ᱴᱷᱤᱠᱬᱟᱹ"; + +/* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ +"Addresses.Settings.SavedAddressesSectionTitle.v124" = "ᱥᱟᱧᱪᱟᱣ ᱠᱟᱱ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱠᱚ"; + +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "ᱴᱷᱤᱠᱬᱟᱹ %@ ᱨᱮ ᱥᱟᱧᱪᱟᱣ ᱟ"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "ᱛᱟᱭᱚᱢ ᱛᱮ ᱩᱥᱟᱹᱨᱟ ᱥᱮᱴᱮᱨᱚᱜ ᱞᱟᱹᱜᱤᱫ ᱟᱢᱟᱜ ᱵᱤᱵᱨᱚᱬ ᱨᱟᱠᱷᱟ ᱛᱮ ᱥᱟᱧᱪᱟᱣ ᱢᱮ ᱾"; + +/* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ +"Addresses.Settings.Switch.Description.v124" = "ᱯᱷᱚᱱ ᱱᱚᱢᱵᱚᱨ ᱟᱨ ᱤᱼᱢᱮᱞ ᱴᱷᱤᱠᱬᱟᱹ ᱥᱮᱞᱮᱫ ᱢᱮᱱᱟᱜᱼᱟ"; + +/* Title label for user to use the toggle settings to allow saving and autofilling of addresses for webpages. */ +"Addresses.Settings.Switch.Title.v124" = "ᱴᱷᱤᱬᱟᱹᱤᱭᱟᱹ ᱥᱟᱧᱪᱟᱣ ᱟᱨ ᱟᱡ ᱛᱮ ᱯᱮᱨᱮᱡ ᱢᱮ"; + +/* Displayed inside the keyboard hint when a user is entering their address and has at least one saved address. Indicates that there are stored addresses available for use in filling out a form. */ +"Addresses.Settings.UseSavedAddressFromKeyboard.v124" = "ᱥᱟᱺᱪᱟᱣ ᱠᱟᱱ ᱴᱷᱤᱠᱬᱟᱹ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; + +/* Accessibility label for the add button in autofill settings screen. Pressing this button presents a modal that allows users to add a card by entering the credit card information. */ +"CreditCard.Settings.AddCard.AccessibilityLabel.v121" = "ᱠᱟᱰ ᱥᱮᱞᱮᱫᱽ ᱢᱮ"; + /* Description label for when there are no credit cards shown in credit card list in autofill settings screen. */ "CreditCard.Settings.EmptyListDescription.v112" = "ᱟᱢᱟᱜ ᱠᱟᱨᱰ ᱵᱟᱵᱚᱛ ᱡᱤᱱᱤᱥ ᱠᱚ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱥᱟᱹᱦᱤᱡ ᱫᱚᱦᱚᱭ ᱢᱮ ᱡᱮᱢᱚᱱ ᱛᱟᱭᱚᱢ ᱫᱷᱟᱣ ᱥᱩᱵᱤᱫᱷᱟᱹᱜ ᱟᱢ ᱾"; +/* Title label for when there are no credit cards shown in credit card list in autofill settings screen. %@ is the product name and should not be altered. */ +"CreditCard.Settings.EmptyListTitle.v122" = "%@ ᱛᱮ ᱠᱟᱰ ᱥᱟᱧᱪᱟᱣ ᱢᱮ"; + /* Accessibility label for a credit card list item in autofill settings screen. The first parameter is the credit card issuer (e.g. Visa). The second parameter is the name of the credit card holder. The third parameter is the last 4 digits of the credit card. The fourth parameter is the card's expiration date. */ "CreditCard.Settings.ListItemA11y.v118" = "%1$@, %2$@ ᱛᱮ ᱡᱟᱨᱤ ᱠᱟᱱᱟ, %3$@ ᱨᱮ ᱢᱩᱪᱟᱹᱫᱚᱜ ᱠᱟᱱᱟ, %4$@ ᱨᱮ ᱪᱟᱵᱟᱜ ᱠᱟᱱᱟ"; +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string indicates to users that they can deny Firefox from remembering the card that is being used. */ +"CreditCard.Settings.NotNow.v122" = "ᱱᱤᱛᱚᱜ ᱫᱚ ᱵᱟᱝᱟ"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string is a title string of the overall message that asks the user if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.RememberThisCard.v122" = "ᱱᱚᱶᱟ ᱠᱟᱰ ᱫᱚ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱥᱟᱹᱦᱤᱡ ᱫᱚᱦᱚᱭ ᱟᱢ ᱥᱮ ?"; + +/* When a user is in the process or has finished making a purchase with a remembered card, and if the credit card information doesn't match the contents of the stored information of that card, we show this string. We ask this user if they would like Firefox update the staled information of that credit card. */ +"CreditCard.Settings.UpdateThisCard.v122" = "ᱠᱟᱰ ᱟᱹᱯᱰᱮᱴ ᱟ ᱥᱮ ?"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string asks users to confirm if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.Yes.v122" = "ᱦᱟᱹᱞᱤᱭᱟᱹᱠ"; + /* When a user is in the process of making a purchase and has at least one saved credit card, a view above the keyboard shows actions a user can take. When tapping this label, the keyboard will dismiss from view. */ "CreditCards.Settings.Done.v114" = "ᱦᱩᱭᱮᱱᱟ"; /* When a user is in the process or has finished making a purchase, and has at least one card saved, we show this tappable string. This indicates to users that they can navigate to their list of stored credit cards in the app's credit card list screen. */ "CreditCards.Settings.ManageCards.v112" = "ᱠᱟᱰ ᱢᱮᱱᱮᱡᱽ ᱢᱮ"; +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label used as a title. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseASavedCard.v122" = "ᱥᱟᱺᱪᱟᱣ ᱠᱟᱱ ᱠᱟᱰ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; + /* When a user is in the process of making a purchase, and has at least one saved card, we show this label inside the keyboard hint. This indicates to the user that there are stored cards available for use on this pending purchase. */ "CreditCards.Settings.UseSavedCardFromKeyboard.v112" = "ᱥᱟᱺᱪᱟᱣ ᱠᱟᱱ ᱠᱟᱰ ᱵᱮᱵᱷᱟᱨ ᱢᱮ"; +/* Settings section title for the old Firefox account */ +"FxA.FirefoxAccount.v119" = "ᱠᱷᱟᱛᱟ"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v124" = "ᱴᱷᱤᱠᱬᱟᱹ ᱠᱚ ᱟᱡ ᱛᱮ ᱯᱮᱨᱮᱡ ᱢᱮ"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v126" = "ᱴᱷᱤᱠᱬᱟᱹᱠᱚ"; + +/* Label used as an item in Settings screen. When touched, it will take user to credit card settings page to that will allows to add or modify saved credit cards to allow for autofill in a webpage. */ +"Settings.AutofillCreditCard.Title.v122" = "ᱜᱚᱱᱚᱝ ᱦᱚᱨᱟᱠᱚ"; + +/* Title displayed in header of the FxA settings panel. */ +"Settings.FxA.Title.v119" = "ᱠᱷᱟᱛᱟ"; + /* This is the description for the setting that toggles Sync related notifications in the settings menu under the Notifications section. */ "Settings.Notifications.SyncNotificationsStatus.v112" = "ᱱᱚᱶᱟ ᱫᱚ ᱮᱱᱮᱢ ᱦᱩᱭᱩᱜ ᱞᱟᱹᱜᱤᱫ ᱴᱮᱵᱽ ᱠᱚ ᱧᱟᱢ ᱟᱨ ᱟᱢ ᱮᱴᱟᱜ ᱰᱤᱵᱟᱤᱥ ᱠᱷᱚᱱ ᱞᱚᱜᱤᱱ ᱚᱠᱛᱚ ᱨᱮ ᱵᱟᱰᱟᱭ ᱧᱟᱢ ᱞᱟᱹᱜᱤᱫ ᱮᱱᱮᱢ ᱦᱩᱭᱩᱜ ᱞᱟᱹᱠᱛᱤ ᱾"; @@ -37,3 +97,78 @@ /* This is the title informing the user needs to turn on notifications in iOS Settings. */ "Settings.Notifications.TurnOnNotificationsTitle.v112" = "ᱠᱷᱚᱵᱚᱨ ᱠᱚ ᱦᱮᱡ ᱪᱷᱚᱭ ᱢᱮ"; +/* Accessibility label for default search engine setting. */ +"Settings.Search.Accessibility.DefaultSearchEngine.v121" = "ᱢᱩᱞ ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ"; + +/* Accessibility label for Learn more about Firefox Suggest. */ +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia Suggest ᱵᱟᱵᱚᱛ ᱰᱷᱮᱨ ᱥᱮᱬᱟᱭ ᱢᱮ"; + +/* Title for alternate search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.AlternateSearchEngines.Title.v124.v2" = "ᱵᱤᱠᱚᱞᱯᱚ ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ"; + +/* Footer for the `default search engine` settings section in the Search Settings page, which explains in more details what the `Show Search Suggestions` setting includes. */ +"Settings.Search.DefaultSearchEngine.Footer.v122" = "ᱥᱮᱸᱫᱽᱨᱟ, ᱱᱟᱜᱟᱢ, ᱵᱩᱠᱢᱟᱨᱠ ᱟᱨ ᱟᱭᱢᱟ ᱠᱷᱚᱱ ᱚᱨᱡᱚ"; + +/* Title for the `default search engine` settings section in the Search page in the Settings menu. */ +"Settings.Search.DefaultSearchEngine.Title.v121" = "ᱢᱩᱞ ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ"; + +/* Title for the `Suggestions from Search Engines` settings section in the Search page in the Settings menu. */ +"Settings.Search.EnginesSuggestions.Title.v124" = "ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ ᱠᱷᱚᱱ ᱵᱟᱛᱟᱣᱟᱜᱠᱚ"; + +/* Navigation title for search page in the Settings menu. */ +"Settings.Search.PageTitle.v121" = "ᱥᱮᱸᱫᱽᱨᱟ"; + +/* Description for `Show in Private Sessions` toggle, located in `Suggestions from Search Engines` section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Description.v125" = "ᱵᱷᱤᱛᱨᱤ ᱫᱷᱚᱱ ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟ ᱤᱧᱡᱤᱱ ᱠᱷᱚᱱ ᱥᱚᱞᱦᱟ ᱩᱫᱩᱜ ᱢᱮ"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in the Private Session section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v122" = "ᱯᱨᱟᱭᱣᱮᱴ ᱵᱽᱨᱟᱣᱩᱡᱤᱝ ᱨᱮ ᱥᱚᱞᱦᱟ ᱩᱫᱩᱜ ᱢᱮ"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in `Suggestions from Search Engines` and `Address Bar - Firefox Suggest` sections in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v124" = "ᱱᱤᱡᱮᱨᱟᱜ ᱚᱠᱛᱚ ᱨᱮ ᱩᱫᱩᱜ ᱢᱮ"; + +/* Title for the `Private Browsing` settings section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Title.v122" = "ᱱᱤᱡᱮᱨᱟᱠ ᱯᱟᱱᱛᱮᱭᱟᱠ"; + +/* Title for quick-search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.QuickEnginesTitle.v121" = "ᱞᱚᱜᱚᱱ-ᱯᱟᱱᱛᱮ ᱤᱧᱡᱤᱱ ᱠᱚ"; + +/* Label for the `show search suggestions` setting, in the Search Settings page. */ +"Settings.Search.ShowSuggestions.v121" = "ᱯᱟᱱᱛᱮ ᱦᱩᱫᱤᱥ ᱠᱚ ᱩᱫᱩᱠ"; + +/* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "ᱴᱷᱤᱠᱬᱟᱹ ᱵᱟᱨ - Ecosia Suggest"; + +/* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia Suggest ᱵᱟᱵᱚᱛ ᱰᱷᱮᱨ ᱥᱮᱬᱟᱭ ᱢᱮ"; + +/* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Ecosia Suggest ᱠᱷᱚᱱ ᱥᱚᱞᱦᱟ ᱩᱫᱩᱜ ᱢᱮ ᱯᱨᱟᱭᱣᱮᱴ ᱥᱮᱥᱚᱱ ᱨᱮ ᱥᱚᱞᱦᱟ"; + +/* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ +"Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "ᱵᱽᱨᱟᱣᱩᱡᱤᱝ ᱦᱤᱛᱟᱹᱞ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; + +/* In the Search page of the Settings menu, the title for the setting to enable search bookmarks. */ +"Settings.Search.Suggest.SearchSearchBookmarks.Title.v124" = "ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; + +/* In the Search page of the Settings menu, the title for the setting to enable synced tabs. */ +"Settings.Search.Suggest.SearchSyncedTabs.Title.v124" = "ᱥᱭᱝᱠ ᱟᱠᱟᱱ ᱴᱮᱵᱽ ᱠᱚ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124" = "ᱣᱮᱵᱽ ᱠᱷᱚᱱ ᱟᱢᱟᱜ ᱥᱮᱸᱫᱽᱨᱟ ᱵᱟᱵᱚᱛ ᱵᱟᱛᱟᱣᱠᱚ ᱧᱟᱢ ᱢᱮ"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124.v2" = "%@ ᱠᱷᱚᱱ ᱟᱢᱟᱜ ᱥᱮᱸᱫᱽᱨᱟ ᱵᱟᱵᱚᱛ ᱵᱟᱛᱟᱣᱠᱚ ᱧᱟᱢ ᱢᱮ"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124" = "%@ ᱠᱷᱚᱱ ᱵᱟᱛᱟᱣᱟᱜ"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from the web in Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124.v2" = "ᱣᱮᱵᱽ ᱠᱷᱚᱱ ᱵᱟᱛᱟᱣᱟᱜ"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from sponsors. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Description.v124" = "ᱚᱠᱛᱚ-ᱠᱟᱹᱣᱰᱤ ᱥᱚᱯᱚᱱᱥᱟᱨ ᱥᱟᱠᱟᱢ ᱛᱮ %@ ᱠᱚ ᱥᱚᱦᱚᱫ ᱢᱮ"; + +/* In the Search page of the Settings menu, the title for the setting to enable Suggestions from sponsors. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Title.v124" = "ᱰᱷᱟᱶᱨᱟ ᱠᱚᱣᱟᱜ ᱵᱟᱛᱟᱣᱠᱚ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Share.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Share.strings new file mode 100644 index 0000000000000..ebca560dccde4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Share.strings @@ -0,0 +1,6 @@ +/* Error message shown in the remote tabs panel */ +"SendTo.NoDevicesFound.Message.v119" = "ᱟᱢ ᱴᱷᱮᱱ ᱚᱞᱜᱟ ᱱᱟᱶᱟ ᱠᱷᱟᱛᱟ ᱟᱹᱭᱩᱨᱢᱤᱫ ᱞᱟᱹᱜᱤᱫ ᱟᱞᱜᱟ ᱥᱟᱫᱷᱚᱱ ᱡᱩᱰᱟᱹᱣ ᱵᱟᱹᱱᱩᱜᱟ ᱾"; + +/* This message appears when a user tries to use 'Send Link to Device' action while not logged in */ +"SendTo.NotSignedIn.Title.v119" = "ᱟᱢ ᱟᱢᱟᱜ ᱠᱷᱟᱛᱟ ᱨᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱵᱚᱱᱩᱢ-ᱟ ᱾"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Shopping.strings new file mode 100644 index 0000000000000..ca42375e41c48 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Shopping.strings @@ -0,0 +1,255 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates that a user can tap on the shopping button to start using the Shopping feature. */ +"ContextualHints.Shopping.NotOptedIn.v120" = "ᱯᱟᱱᱛᱮ ᱚᱰᱚᱠ ᱢᱮ ᱡᱩᱫᱤ ᱟᱢ ᱱᱚᱶᱟ ᱡᱤᱱᱤᱥ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱵᱷᱚᱨᱥᱟ ᱦᱟᱜ ᱠᱟᱱᱟᱢ — ᱠᱤᱨᱤᱧ ᱢᱟᱲᱟᱝ ᱨᱮ ᱾"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing the Shopping feature. It indicates that a user can go directly to the Shopping feature by tapping the text of the action. */ +"ContextualHints.Shopping.NotOptedInAction.v120" = "ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ ᱪᱮᱥᱴᱟ ᱵᱤᱲᱟᱹᱣ ᱢᱮ"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears after the user has opted in for the Shopping feature. It indicates that a user can directly open the review checker by tapping the text of the action. */ +"ContextualHints.Shopping.OptedInAction.v120" = "ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one appears after the user has opted in and informs him if he wants use the review checker by tapping the Shopping button. */ +"ContextualHints.Shopping.OptedInBody.v120" = "ᱱᱚᱶᱟ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱵᱷᱚᱨᱥᱟ ᱜᱮᱭᱟ ᱥᱮ ? ᱢᱤᱫᱴᱟᱹᱝ ᱮᱰᱡᱚᱥᱴ ᱫᱚᱨ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱫ ᱱᱤᱛᱚᱜ ᱧᱮᱞ ᱢᱮ ᱾"; + +/* Footer label from the Fakespot Ad card displayed for the related product we advertise. This is displayed below the ad card, suggested as an alternative to the product reviewed. The first parameter will be replaced by the Fakespot app name */ +"Shopping.AdCard.Footer.v121" = "%@ ᱦᱚᱛᱮᱛᱮ ᱮᱰ"; + +/* Title label for the Fakespot Ad card. This is displayed above a product image, suggested as an alternative to the product reviewed. */ +"Shopping.AdCard.Title.v121" = "ᱰᱷᱮᱨ ᱮᱢ ᱧᱮᱞᱟ"; + +/* Description adjusted of the rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Description.v121" = "ᱵᱷᱚᱨᱥᱟᱡᱚᱱᱚᱠ ᱧᱮᱞᱵᱤᱲᱟᱹᱣ ᱪᱮᱛᱟᱱ ᱨᱮ"; + +/* Accessibility label, associated to adjusted rating stars. %@ is a decimal value from 0 to 5 that will only use a tenth (example: 3.5). */ +"Shopping.AdjustedRating.StarsAccessibilityLabel.v120" = "5 ᱤᱯᱤᱞ ᱠᱷᱚᱱ %@"; + +/* Title of the adjusted rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Title.v120" = "ᱨᱮᱴᱤᱝ ᱥᱚᱢᱚᱡᱚᱥ ᱮᱱᱟ"; + +/* Button text of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Button.Text.v120" = "ᱵᱩᱡᱷᱟᱹᱣ ᱠᱮᱫᱟ"; + +/* Title of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Title.v120" = "ᱵᱤᱥᱲᱤ ᱫᱚ ᱱᱟᱦᱟᱜ ᱦᱟᱹᱵᱤᱡ ᱢᱮᱱᱟᱜᱼᱟ"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Competitiveness.Title.v120" = "ᱠᱚᱢᱯᱮᱴᱤᱴᱤᱵᱷ"; + +/* Title of the button that shows less reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.LessButton.Title.v120" = "ᱠᱚᱢ ᱫᱮᱠᱷᱟᱣ ᱢᱮ"; + +/* Title of the button that shows more reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.MoreButton.Title.v120" = "ᱵᱚᱲᱛᱤ ᱩᱫᱩᱜ"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet, specifically focusing on the quality, design, and condition of the product's packaging. This may include details about the box, protective materials, presentation, and overall packaging experience. */ +"Shopping.HighlightsCard.Packaging.Title.v120" = "ᱜᱷᱮᱱᱴ\n"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Price.Title.v120" = "ᱜᱚᱱᱚᱝ"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Quality.Title.v120" = "ᱜᱩᱱ"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Shipping.Title.v120" = "ᱵᱷᱮᱡᱟ"; + +/* Title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Title.v120" = "ᱱᱤᱛᱚᱜᱟᱜ ᱧᱮᱞᱵᱤᱲᱟᱹᱣ ᱠᱷᱚᱱ ᱛᱤᱱᱟᱹᱜ ᱜᱟᱱ ᱪᱤᱱᱦᱟᱹᱦᱟᱜ"; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Description.v120" = "ᱫᱩᱨᱵᱷᱟᱜᱽ ᱛᱮ, ᱟᱞᱮ ᱫᱚ ᱡᱷᱚᱛᱚ ᱡᱤᱱᱤᱥ ᱠᱚᱨᱮᱭᱟᱜ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱵᱟᱞᱮ ᱫᱟᱲᱮᱭᱟᱫᱼᱟ ᱾ ᱡᱮᱢᱚᱱ, ᱜᱤᱯᱷᱴ ᱠᱟᱨᱰ ᱟᱨ ᱥᱴᱨᱤᱢᱤᱝ ᱣᱤᱰᱤᱭᱳ, ᱥᱮᱨᱮᱧ, ᱟᱨ ᱠᱷᱮᱞᱠᱚ ᱾"; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "ᱱᱚᱶᱟ ᱵᱟᱞᱮ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ "; + +/* Description text for an information card used in the review checker section. This message is displayed when the reviews for a product are not yet available but are expected to be provided within the next 24 hours. It serves to inform users of the short wait for reviews and encourages them to return soon for the updated information. */ +"Shopping.InfoCard.InfoComingSoon.Description.v121" = "ᱟᱞᱮ ᱴᱷᱮᱱ ᱫᱚ ᱱᱚᱶᱟ ᱡᱤᱱᱤᱥ ᱵᱚᱵᱛ ᱧᱮᱞ ᱵᱤᱰᱟᱣᱜ 24 ᱴᱟᱲᱟᱝ ᱜᱟᱱ ᱞᱟᱣᱟ ᱞᱮᱭᱟ ᱾ ᱫᱟᱭᱟᱠᱟᱛᱮ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣ ᱢᱮ ᱾"; + +/* Title for an information card that is displayed in the review checker section when certain details about a product or feature are not currently available but are expected to be provided soon. The message should imply that the user can look forward to receiving more information shortly. */ +"Shopping.InfoCard.InfoComingSoon.Title.v121" = "ᱵᱤᱵᱨᱚᱬ ᱞᱚᱜᱚᱱ ᱜᱮ ᱦᱟᱹᱡᱩᱜ ᱠᱟᱱᱟ"; + +/* Primary action title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.PrimaryAction.v120" = "ᱱᱤᱛᱚᱜ ᱧᱮᱞ ᱢᱮ"; + +/* Title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.Title.v120" = "ᱧᱮᱞ ᱞᱟᱹᱜᱤᱫ ᱱᱚᱶᱟ ᱵᱤᱵᱨᱚᱬ"; + +/* Description for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Description.v120" = "ᱱᱚᱶᱟ ᱮᱴᱠᱮᱴᱚᱬᱮ ᱥᱩᱫᱷᱟᱨ ᱞᱟᱹᱜᱤᱫ ᱠᱟᱹᱢᱤ ᱮᱫᱟ ᱞᱮ ᱾ ᱫᱟᱭᱟᱠᱟᱛᱮ ᱛᱟᱭᱚᱢ ᱛᱮ ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱦᱟᱹᱡᱩᱜ ᱢᱮ ᱾"; + +/* Title for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Title.v120" = "ᱱᱤᱛᱚᱜ ᱪᱮᱫ ᱦᱚᱸ ᱵᱤᱵᱨᱚᱬ ᱵᱟᱹᱱᱩᱜ ᱠᱟᱫᱼᱟ"; + +/* Description for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Description.v120" = "ᱛᱤᱧ ᱡᱷᱚᱜ ᱡᱤᱱᱤᱥ ᱨᱮ ᱡᱟᱹᱥᱛᱤ ᱧᱮᱞᱵᱤᱲᱟᱹᱣ ᱛᱟᱦᱮᱸᱱᱟ, ᱟᱞᱮ ᱫᱚ ᱚᱱᱟ ᱨᱮᱭᱟᱜ ᱜᱩᱱ ᱵᱚᱵᱚᱛ ᱞᱮ ᱢᱮᱱ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾"; + +/* Title for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Title.v120" = "ᱩᱱᱟᱹᱜ ᱧᱮᱞᱵᱤᱲᱟᱹᱣ ᱵᱟᱹᱱᱩᱜ ᱠᱟᱫᱟ"; + +/* Description for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This description is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Description.v121" = "ᱡᱩᱫᱤ ᱥᱴᱚᱠ ᱛᱮ ᱡᱤᱱᱤᱥ ᱦᱮᱡ ᱞᱮᱠᱷᱟᱢ ᱵᱩᱡᱷᱟᱹᱣ ᱠᱮᱫ ᱠᱷᱟᱱ, ᱟᱞᱮ ᱴᱷᱮᱱ ᱠᱷᱚᱵᱚᱨ ᱮᱢ ᱠᱟᱜ ᱢᱮ ᱟᱞᱮ ᱫᱚ ᱧᱮᱞᱵᱤᱲᱟᱹᱣᱠᱚᱞᱮ ᱧᱮᱞ ᱟ ᱾"; + +/* Primary action label for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This primary action label is used for info card button where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.PrimaryAction.v121" = "ᱠᱷᱚᱵᱚᱨ ᱢᱮ ᱡᱮ ᱡᱤᱱᱤᱥ ᱫᱚ ᱥᱴᱚᱠ ᱨᱮ ᱦᱮᱡ ᱦᱩᱭ ᱮᱱᱟ"; + +/* Title for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This title is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Title.v121" = "ᱡᱤᱱᱤᱥ ᱫᱚ ᱵᱟᱹᱱᱩᱜ ᱠᱟᱫᱟ"; + +/* Description for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Description.v120" = "ᱱᱚᱶᱟ ᱫᱚ 60 ᱴᱤᱡ ᱜᱟᱱ ᱚᱠᱛᱚ ᱤᱫᱤᱭᱟᱭ ᱾"; + +/* Title for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Title.v120" = "ᱧᱮᱞᱯᱚᱨᱚᱠ ᱜᱩᱱ ᱧᱮᱞ ᱦᱩᱭᱩᱜ ᱠᱟᱱᱟ"; + +/* Title for info card when the product is in analysis mode. The placeholder represents the percentage of the analysis progress, ranging between 1 and 100. */ +"Shopping.InfoCard.ProgressAnalysis.Title.v123" = "ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣ ᱜᱩᱱ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣ ᱮᱫᱟ (%@)"; + +/* This description appears beneath the confirmation title on the information card to inform the user that their report regarding the product stock status has been received and is being processed. It serves to set the expectation that the review information will be updated within 24 hours and invites the user to revisit the product page for updates. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Description.v121" = "ᱟᱞᱮ ᱴᱷᱮᱱ ᱫᱚ ᱱᱚᱶᱟ ᱡᱤᱱᱤᱥ ᱵᱚᱵᱛ ᱧᱮᱞ ᱵᱤᱰᱟᱣᱜ 24 ᱴᱟᱲᱟᱝ ᱜᱟᱱ ᱞᱟᱣᱟ ᱞᱮᱭᱟ ᱾ ᱫᱟᱭᱟᱠᱟᱛᱮ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣ ᱢᱮ ᱾"; + +/* This title is displayed on the information card as a confirmation message after a user reports that a previously out-of-stock product is now available. It's meant to acknowledge the user's contribution and encourage community engagement by letting them know their report has been successfully submitted. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Title.v121" = "ᱠᱷᱚᱵᱚᱨ ᱮᱢ ᱞᱟᱹᱜᱤᱫ ᱟᱹᱰᱤ ᱥᱟᱨᱦᱟᱣ!"; + +/* Text for the analyzer button displayed when an analysis can be updated for a product. */ +"Shopping.NoAnalysisCard.AnalyzerButton.Title.v120" = "ᱧᱮᱞᱯᱚᱨᱚᱠ ᱜᱩᱱ ᱧᱮᱞ ᱢᱮ"; + +/* Text for the body label, to check the reliability of a product. */ +"Shopping.NoAnalysisCard.BodyLabel.Title.v120" = "ᱡᱩᱫᱤ ᱟᱢ ᱵᱟᱲᱟᱭ ᱥᱮᱱ ᱢᱮ ᱠᱟᱱᱟ ᱱᱚᱶᱟ ᱡᱤᱱᱤᱥ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱵᱟᱵᱚᱛ, ᱢᱮᱱᱠᱷᱟᱱ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱜᱩᱱ ᱧᱮᱞ ᱢᱮ ᱾ ᱱᱚᱶᱟ ᱫᱚ 60 ᱴᱤᱡ ᱤᱫᱤᱭᱟᱭ ᱾"; + +/* Title for card displayed when a shopping product has not been analysed yet. */ +"Shopping.NoAnalysisCard.HeadlineLabel.Title.v120" = "ᱱᱚᱶᱟ ᱧᱮᱞᱯᱚᱨᱚᱠᱷ ᱞᱟᱹᱜᱤᱫ ᱪᱮᱫ ᱵᱤᱵᱨᱚᱬ ᱵᱟᱹᱱᱩᱜᱼᱟ"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replaced by the Fakespot app name. After the colon, what appears are two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v120" = "ᱵᱟᱪᱷᱟᱣ ᱠᱟᱛᱮ \"ᱦᱮᱸ, ᱱᱚᱣᱟ ᱠᱩᱨᱩᱢᱩᱴᱩᱭ ᱢᱮ\" ᱟᱢ %@ ᱠᱷᱚᱱ ᱮᱢ ᱦᱮᱥᱤᱭᱟᱹᱨ ᱮᱫᱟ :"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). After the colon, there will be two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v123" = "ᱵᱟᱪᱷᱟᱣ ᱠᱟᱛᱮ \"ᱦᱮᱸ, ᱱᱚᱣᱟ ᱠᱩᱨᱩᱢᱩᱴᱩᱭ ᱢᱮ\" ᱟᱢ ᱱᱟᱶᱟ ᱡᱤᱱᱤᱥᱠᱚᱛᱮ ᱦᱮᱥᱤᱭᱟᱹᱨ ᱮᱫᱟ :"; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. This string is almost identical with 'Shopping.OptInCard.FirstParagraph.Description', but without Best Buy and Walmart websites, which are not available in many locales. */ +"Shopping.OptInCard.FirstParagraph.AmazonOnly.Description.v122" = "%1$@ ᱨᱮ ᱠᱤᱨᱤᱧ ᱢᱟᱲᱟᱝ ᱨᱮ ᱧᱮᱞᱢᱮ ᱡᱤᱱᱤᱥ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚᱫᱚ ᱪᱮᱫ ᱞᱮᱠᱷᱟ ᱢᱮᱱᱟᱜ ᱠᱟᱫᱟ ᱾ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱧᱮᱧᱮᱞᱤᱭᱟᱹ, %2$@ ᱠᱷᱚᱱ ᱢᱤᱫᱴᱟᱹᱝ ᱮᱠᱥᱯᱮᱨᱤᱢᱮᱱᱴᱟᱞ ᱯᱷᱤᱪᱚᱨ, ᱱᱚᱶᱟ ᱫᱚ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱪᱮᱛᱟᱱ ᱨᱮ ᱛᱮᱭᱟᱨ ᱠᱟᱱᱟ ᱾"; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "%1$@ ᱨᱮ ᱠᱤᱨᱤᱧ ᱢᱟᱲᱟᱝ ᱨᱮ ᱧᱮᱞᱢᱮ ᱡᱤᱱᱤᱥ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚᱫᱚ ᱪᱮᱫ ᱞᱮᱠᱷᱟ ᱢᱮᱱᱟᱜ ᱠᱟᱫᱟ ᱾ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱧᱮᱧᱮᱞᱤᱭᱟᱹ, %2$@ ᱠᱷᱚᱱ ᱢᱤᱫᱴᱟᱹᱝ ᱮᱠᱥᱯᱮᱨᱤᱢᱮᱱᱴᱟᱞ ᱯᱷᱤᱪᱚᱨ, ᱱᱚᱶᱟ ᱫᱚ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱪᱮᱛᱟᱱ ᱨᱮ ᱛᱮᱭᱟᱨ ᱠᱟᱱᱟ ᱾ ᱱᱚᱶᱟ ᱫᱚ %3$@ ᱟᱨ %4$@ ᱨᱮ ᱦᱚᱸ ᱠᱟᱹᱢᱤᱭᱟᱭ ᱾"; + +/* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.HeaderLabel.Title.v120" = "ᱡᱤᱱᱤᱥ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱨᱮ ᱟᱞᱮᱭᱟᱜ ᱵᱷᱚᱨᱥᱟ ᱜᱚᱲᱚᱠᱚ ᱪᱮᱥᱴᱟ ᱰᱤᱲᱟᱹᱣ ᱢᱮ\n"; + +/* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "ᱰᱷᱮᱨ ᱥᱮᱬᱟᱭ ᱢᱮ"; + +/* Text for the main button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.MainButton.Title.v120" = "ᱦᱚᱭ, ᱪᱮᱥᱴᱟᱭ ᱢᱮ"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v120" = "ᱫᱟᱱᱟᱝ ᱨᱤᱛᱤ"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Firefox app name. */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v123" = "%@ᱦᱟᱜ ᱱᱤᱥᱚᱱ ᱠᱷᱚᱵᱚᱨ"; + +/* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.SecondaryButton.Title.v120" = "ᱱᱤᱛᱚᱜ ᱫᱚ ᱵᱟᱝᱟ"; + +/* Label for the second paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be replaced by the Fakespot app name. The second parameter will be replaced the company name of Mozilla. */ +"Shopping.OptInCard.SecondParagraph.Description.v120" = "%1$@ ᱨᱮᱭᱟᱜ ᱯᱟᱣᱟᱨ ᱵᱮᱵᱷᱟᱨ,‌ ᱟᱞᱮ ᱫᱚ ᱠᱤᱨᱤᱧ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱟᱨ ᱵᱟᱝ ᱴᱷᱟᱹᱣᱠᱟᱹ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱵᱟᱲᱟᱭ ᱨᱮ ᱜᱚᱲᱚ ᱮᱢᱚᱜᱟᱭ ᱾ ᱟᱞᱮᱭᱟᱜ AI ᱢᱳᱰᱮᱞ ᱫᱚ ᱟᱢ ᱡᱟᱦᱟᱱᱟᱜ ᱠᱤᱨᱤᱧ ᱥᱩᱫᱷᱨᱟᱹᱣ ᱨᱮ ᱜᱚᱲᱚ ᱮᱢ ᱮᱫᱟᱭ ᱾%2$@"; + +/* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "ᱵᱮᱵᱷᱟᱨ ᱨᱮᱭᱟᱜ ᱥᱚᱨᱛᱚ"; + +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "%@ ᱵᱮᱵᱷᱟᱨ ᱨᱮᱭᱟᱜ ᱥᱚᱨᱛᱚ"; + +/* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ +"Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "ᱜᱽᱨᱮᱰ %@"; + +/* Title of the reliability card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityCardTitle.v120" = "ᱱᱚᱶᱟ ᱧᱮᱞᱵᱤᱲᱟᱹᱣᱠᱚ ᱛᱤᱱᱟᱹᱜ ᱵᱷᱚᱨᱥᱟ ᱫᱮᱲᱮᱭᱟᱜᱼᱟᱢ ?"; + +/* Description of the reliability ratings for rating 'A' and 'B' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.AB.Description.v120" = "ᱴᱷᱤᱠ ᱵᱟᱛᱟᱣᱟᱜᱠᱚ"; + +/* Description of the reliability rating 'C' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.C.Description.v120" = "ᱴᱷᱤᱠ ᱵᱟᱛᱟᱣᱟᱜ ᱟᱨ ᱵᱤᱱᱥᱨᱚᱛ ᱢᱮᱥᱟᱢᱤᱥᱤ ᱧᱮᱞᱵᱤᱲᱟᱹᱣᱟᱜᱠᱚ"; + +/* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "ᱵᱤᱱᱥᱨᱚᱛ ᱧᱮᱞᱵᱤᱲᱟᱹᱣ"; + +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "*ᱨᱮᱴᱤᱝ ᱥᱟᱹᱛ* ᱫᱚ ᱪᱷᱚᱞ ᱜᱮᱭᱟ ᱡᱟᱦᱟᱸᱫᱚ ᱟᱞᱮ ᱵᱩᱡᱷᱟᱹᱣ ᱨᱮ ᱴᱷᱤᱠ ᱜᱮᱭᱟ ᱾"; + +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "ᱨᱟᱹᱯᱩᱫ ᱢᱮ ᱟᱵᱚ ᱪᱮᱫ ᱞᱮᱠᱟ ᱛᱮ ᱧᱮᱞ ᱜᱩᱱ ᱠᱟᱨᱰ ᱵᱚᱱ ᱴᱷᱟᱹᱣᱠᱟᱹᱭᱟ"; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "ᱟᱞᱮ ᱫᱚ ᱧᱮᱞᱵᱤᱲᱟᱹᱣᱟᱜ ᱜᱩᱱ ᱫᱚ ᱪᱮᱫ ᱞᱮᱠᱷᱟ ᱞᱮ ᱵᱟᱲᱟᱭ ᱮᱫᱟ ᱯᱟᱥᱱᱟᱣ ᱢᱮ"; + +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "%2$@ ᱯᱟᱦᱴᱟ ᱠᱷᱚᱱ %1$@ ᱠᱷᱚᱱ AI ᱴᱮᱠᱱᱚᱞᱚᱡᱤ ᱵᱮᱵᱷᱟᱨ ᱠᱟᱛᱮᱜ ᱟᱞᱮ ᱯᱨᱚᱰᱚᱠᱴ ᱨᱤᱵᱷᱤᱭᱩ ᱨᱮᱱᱟᱜ ᱥᱟᱹᱨᱤᱜᱟᱱᱤ ᱧᱮᱞ ᱵᱤᱰᱟᱹᱣᱜ ᱮᱫᱟ ᱞᱮ ᱾ ᱱᱚᱶᱟ ᱫᱚ ᱧᱮᱞ ᱵᱤᱰᱟᱣᱜ ᱜᱩᱱ ᱵᱟᱲᱟᱭ ᱪᱷᱚ ᱨ ᱜᱚᱲᱚ ᱮᱢ ᱫᱟᱲᱮᱭᱟᱢᱟ, ᱯᱨᱚᱰᱚᱠᱴ ᱨᱮᱭᱟᱜ ᱢᱩᱞ ᱜᱩᱱ ᱵᱟᱵᱚᱛ ᱫᱚ ᱵᱟᱝᱟ ᱾"; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "*ᱪᱤᱱᱦᱟᱹᱠ* ᱫᱚ %1@ ᱧᱮᱞᱯᱚᱨᱚᱠᱷ ᱠᱷᱚᱱ ᱢᱩᱪᱟᱹᱫ 80 ᱢᱟᱦᱟᱸ ᱨᱮᱭᱟᱜ ᱠᱟᱱᱟ ᱡᱟᱦᱟᱸᱭ ᱫᱚ ᱟᱞᱮ ᱦᱤᱥᱟᱹᱵᱽ ᱛᱮ ᱵᱷᱚᱨᱥᱟ ᱜᱮᱭᱟ ᱾"; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "*ᱪᱤᱱᱦᱟᱹᱠ* ᱫᱚ %@ ᱧᱮᱞᱯᱚᱨᱚᱠᱷ ᱠᱷᱚᱱ ᱢᱩᱪᱟᱹᱫ 80 ᱢᱟᱦᱟᱸ ᱨᱮᱭᱟᱜ ᱠᱟᱱᱟ ᱡᱟᱦᱟᱸᱭ ᱫᱚ ᱟᱞᱮ ᱦᱤᱥᱟᱹᱵᱽ ᱛᱮ ᱵᱷᱚᱨᱥᱟ ᱜᱮᱭᱟ ᱾"; + +/* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Label.Title.v120" = "ᱟᱞᱮ ᱫᱚ ᱧᱮᱞᱵᱤᱲᱟᱹᱣᱟᱜ ᱜᱩᱱ ᱫᱚ ᱪᱮᱫ ᱞᱮᱠᱷᱟ ᱞᱮ ᱵᱟᱲᱟᱭ ᱮᱫᱟ"; + +/* The title of the learn more button from How we determine review quality card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the Fakespot app name. */ +"Shopping.ReviewQualityCard.LearnMoreButton.Title.v120" = "ᱵᱟᱹᱲᱛᱤ ᱵᱟᱰᱟᱭ ᱢᱮ ᱪᱮᱫ ᱞᱮᱠᱟ %@ ᱧᱮᱞ ᱫᱚᱦᱲᱟ ᱜᱩᱱ ᱴᱷᱟᱹᱣᱠᱟᱹᱭᱟ"; + +/* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "ᱟᱞᱮ ᱵᱩᱡᱷᱟᱹᱣ ᱨᱮ ᱢᱮᱥᱟ ᱟᱨ ᱵᱟᱝ ᱴᱷᱤᱠ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱢᱮᱱᱟᱜ ᱠᱟᱫᱟ ᱾"; + +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "ᱵᱷᱚᱨᱥᱟ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱾ ᱟᱞᱮ ᱵᱩᱡᱷᱟᱹᱣ ᱨᱮ ᱧᱮᱞᱯᱚᱨᱚᱠ ᱫᱚ ᱥᱟᱹᱨᱤ ᱦᱚᱲᱴᱷᱮᱱᱠᱷᱚᱱ ᱦᱟᱹᱡᱩᱜ ᱠᱟᱱᱟ, ᱟᱨ ᱱᱤᱥᱪᱟᱞ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱠᱟᱱᱟ ᱾"; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "ᱟᱞᱮ ᱫᱚ ᱯᱨᱚᱰᱚᱠᱴ ᱨᱮᱭᱟᱜ ᱧᱮᱞ ᱵᱤᱰᱟᱣᱜ ᱫᱚ *ᱪᱤᱠᱤ ᱜᱽᱨᱮᱰ* A ᱠᱷᱚᱱ F ᱫᱷᱟᱹᱵᱤᱡ ᱞᱮ ᱮᱢ ᱮᱫᱟ ᱾"; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "ᱱᱤᱥᱪᱷᱚᱞ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚ ᱾ ᱟᱞᱮ ᱵᱩᱡᱷᱟᱹᱣ ᱨᱮ ᱧᱮᱞᱯᱚᱨᱚᱠᱠᱚᱫᱚ ᱪᱷᱚᱞ ᱥᱮ ᱠᱤᱨᱤᱧ ᱧᱮᱞᱯᱚᱨᱚᱠᱷᱤᱭᱟᱹ ᱠᱟᱱᱟᱠᱚ ᱾"; + +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "ᱥᱟᱡᱟᱣ ᱠᱟᱰ ᱦᱩᱰᱤᱧ ᱪᱷᱚᱭ ᱢᱮ"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "ᱥᱟᱡᱟᱣ ᱠᱟᱰ ᱯᱟᱥᱱᱟᱣ ᱢᱮ"; + +/* Accessibility hint for the recommended products label and switch, grouped together. When the group is selected in VoiceOver mode, the hint is read to help the user understand what action can be performed. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityHint.v123" = "ᱥᱮᱴᱤᱝ ᱛᱮ ᱵᱚᱫᱚᱞ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱨ ᱫᱷᱟᱣ ᱚᱛᱟᱭ ᱢᱮ ᱾"; + +/* Accessibility label for the recommended products label and switch, grouped together. The first placeholder is for the recommended products label, and the second placeholder is for the state of the switch: On/Off. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityLabel.v123" = "%1$@, ᱥᱣᱤᱪ ᱵᱩᱛᱟᱹᱢ , %2$@ ᱾"; + +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ ᱫᱚ %2$@ ᱫᱟᱨᱟᱭ ᱛᱮ %1$@ ᱜᱚᱲᱚ ᱥᱟᱦᱚᱫ ᱮᱢ ᱠᱟᱜᱼᱟᱭ"; + +/* Title of the settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.Label.Title.v120" = "ᱥᱟᱡᱟᱣ ᱠᱚ"; + +/* Label of the switch from settings card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the app name. */ +"Shopping.SettingsCard.RecommendedProducts.Label.v120" = "%@ ᱦᱚᱛᱮᱛᱮ ᱥᱚᱞᱦᱟ ᱟᱠᱟᱱ ᱡᱤᱱᱤᱥ ᱩᱫᱩᱜ ᱢᱮ"; + +/* Toggled Off accessibility switch value from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOff.AccessibilityLabel.v123" = "ᱵᱚᱸᱫᱚ"; + +/* Toggled On accessibility value, from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOn.AccessibilityLabel.v123" = "ᱪᱟᱹᱞᱩ"; + +/* Label of the button from settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.TurnOffButton.Title.v120" = "ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* Beta label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Beta.Title.v120" = "BETA"; + +/* Accessibility label for close button that dismisses the Shopping Experience (Fakespot) sheet. */ +"Shopping.Sheet.Close.AccessibilityLabel.v121" = "ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* Label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Title.v120" = "ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ"; + +/* Text for body of error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Description.v120" = "ᱟᱢᱟᱜ ᱱᱮᱴᱣᱟᱨᱠ ᱡᱩᱲᱟᱹᱣ ᱧᱮᱞ ᱛᱟᱢ ᱟᱨ ᱥᱟᱦᱴᱟ ᱫᱩᱦᱲᱟᱹ ᱞᱟᱫᱮ ᱞᱟᱹᱜᱤᱫ ᱪᱮᱥᱴᱟᱭ ᱢᱮ ᱾"; + +/* Title for error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Title.v120" = "ᱱᱮᱴᱣᱟᱨᱠ ᱡᱩᱲᱟᱹᱣ ᱫᱚ ᱵᱟᱹᱱᱩᱜᱼᱟ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SnackBar.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SnackBar.strings index 59688353ef148..492e22ce74050 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SnackBar.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SnackBar.strings @@ -4,3 +4,6 @@ /* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets saved successfully */ "CreditCard.SnackBar.SavedCardLabel.v112" = "ᱱᱟᱶᱟ ᱠᱟᱰ ᱥᱟᱧᱪᱟᱣ ᱮᱱᱟ"; +/* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets updated successfully */ +"CreditCard.SnackBar.UpdatedCardLabel.v122" = "ᱠᱟᱰ ᱥᱟᱦᱴᱟ ᱦᱟᱹᱞᱤᱭᱟᱹᱠ ᱮᱱᱟ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SuggestedSites.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SuggestedSites.strings new file mode 100644 index 0000000000000..471f9fc3c84b0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/SuggestedSites.strings @@ -0,0 +1,3 @@ +/* Title for X (formerly Twitter) tile in the suggested sites section of the homepage. */ +"SuggestedSites.X.Title.v131" = "X"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabLocation.strings index 5f4415ff70f07..e8bee9423e5da 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabLocation.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabLocation.strings @@ -1,3 +1,6 @@ +/* Accessibility hint for the reload button */ +"Address.Bar.Reload.A11y.Hint.v124" = "ᱵᱟᱹᱲᱛᱤ ᱚᱯᱥᱚᱱ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱨ ᱫᱷᱟᱣ ᱴᱤᱯᱟᱹᱣ ᱟᱨ ᱥᱟᱵ ᱫᱚᱦᱚᱭ ᱢᱮ"; + /* Accessibility label for the security icon in url bar */ "TabLocation.ETP.Off.NotSecure.A11y.Label.v119" = "ᱡᱩᱲᱟᱹᱣ ᱫᱚ ᱵᱟᱝ ᱨᱩᱠᱷᱤᱭᱟᱹ ᱟ ᱾ ᱵᱟᱲᱦᱟᱣᱟᱠᱟᱱ ᱯᱟᱧᱡᱟ ᱫᱟᱱᱟᱲ ᱪᱤᱱᱦᱟᱹ ᱟᱰ ᱫᱚ ᱵᱚᱸᱫᱚᱭᱮᱱᱟ ᱾"; @@ -10,6 +13,24 @@ /* Accessibility label for the security icon in url bar */ "TabLocation.ETP.On.Secure.A11y.Label.v119" = "ᱡᱟᱯᱛᱤ ᱡᱚᱱᱚᱲᱟᱣ"; +/* Accessibility label for the lock / tracking protection button on the URL bar */ +"TabLocation.LockButton.AccessibilityLabel.v122" = "ᱯᱟᱸᱡᱟ ᱨᱚᱯᱷᱟ"; + +/* Large content title for the lock button. This title is displayed when accessible font sizes are enabled */ +"TabLocation.LockButton.LargeContentTitle.v122" = "ᱯᱟᱸᱡᱟ ᱨᱚᱯᱷᱟ"; + /* Accessibility label for the share button in url bar */ "TabLocation.Share.A11y.Label.v119" = "ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱦᱟᱹᱴᱤᱧ ᱢᱮ"; +/* Large content title for the share button. This title is displayed when using accessible font sizes is enabled */ +"TabLocation.ShareButton.AccessibilityLabel.v122" = "ᱦᱟᱹᱴᱤᱧ ᱢᱮ"; + +/* Accessibility label for the shopping button in url bar */ +"TabLocation.Shopping.A11y.Label.v120" = "ᱫᱩᱦᱲᱟᱹ ᱧᱮᱞ ᱵᱤᱲᱟᱹᱣᱤᱭᱟᱹ"; + +/* Large content title for the tabs button. The argument is the number of open tabs or an infinity symbol. This title is displayed when using accessible font sizes is enabled. */ +"TabsButton.Accessibility.LargeContentTitle.v122" = "ᱴᱮᱵᱽ ᱩᱫᱩᱜ ᱢᱮ: %@"; + +/* Large content title for the button shown in editing mode to remove this site from the top sites panel. */ +"TopSites.RemoveButton.LargeContentTitle.v122" = "ᱥᱟᱦᱴᱟ ᱚᱪᱚᱜᱽ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabToolbar.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabToolbar.strings new file mode 100644 index 0000000000000..b7efd09570c5c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabToolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the tab toolbar fire button in private mode, used to provide users a way to end and delete their private session data. */ +"TabToolbar.Accessibility.DataClearance.v122" = "ᱰᱟᱴᱟ ᱢᱮᱴᱟᱣ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabsTray.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabsTray.strings index 2e81fef2b06c1..dc13a4a169a8c 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabsTray.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/TabsTray.strings @@ -13,3 +13,6 @@ /* Users can disable syncing tabs from other devices. In the Sync Tabs panel of the Tab Tray, we inform the user tab syncing can be switched back on to view those tabs. */ "TabsTray.Sync.SyncTabsDisabled.v116" = "ᱚᱞᱜᱟ ᱥᱟᱫᱷᱚᱱ ᱨᱮ ᱴᱮᱵᱽ ᱨᱮᱭᱟᱜ ᱞᱤᱥᱴᱤ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱫ ᱴᱮᱵᱽ ᱟᱹᱛᱩᱨ ᱟᱬᱜᱚ ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ ᱾"; +/* Button label to sync tabs in your account */ +"TabsTray.SyncTabs.SyncTabsButton.Title.v119" = "ᱴᱮᱵᱽ ᱠᱚ ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Toolbar.strings new file mode 100644 index 0000000000000..f0e9730af0ea1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "ᱱᱟᱶᱟ ᱴᱮᱵᱽ"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "ᱱᱚᱶᱟ ᱴᱮᱵ ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ToolbarLocation.strings index fd86fb878d5d3..e3446ec164480 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ToolbarLocation.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/ToolbarLocation.strings @@ -1,6 +1,9 @@ /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ "ContextualHints.Toolbar.Bottom.Description.v107" = "ᱚᱱᱟ ᱡᱩᱫᱤ ᱟᱢᱟᱜ ᱥᱴᱟᱭᱤᱞ ᱠᱟᱱᱟ ᱢᱮᱱᱠᱷᱟᱱ ᱪᱮᱛᱟᱱ ᱛᱮ ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱵᱟᱨ ᱩᱪᱟᱹᱲ ᱢᱮ ᱾"; +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can press and hold either the back or forward web navigation buttons to quickly navigate their back/forward history */ +"ContextualHints.Toolbar.Navigation.Description.v132" = "ᱱᱚᱶᱟ ᱴᱮᱵᱽ ᱱᱟᱜᱟᱢ ᱨᱮ ᱥᱟᱦᱴᱟ ᱛᱟᱞᱟ ᱨᱮ ᱩᱪᱟᱹᱲᱚᱜ ᱞᱟᱹᱜᱤᱫ ᱥᱟᱨ ᱪᱤᱱᱦᱟᱹ ᱨᱮ ᱴᱤᱯᱟᱹᱣ ᱢᱮ ᱾"; + /* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ "ContextualHints.Toolbar.Top.Description.v107" = "ᱚᱱᱟ ᱡᱩᱫᱤ ᱟᱢᱟᱜ ᱥᱴᱟᱭᱤᱞ ᱠᱟᱱᱟ ᱢᱮᱱᱠᱷᱟᱱ ᱞᱟᱛᱟᱨ ᱛᱮ ᱦᱟᱹᱛᱤᱭᱟᱹᱨ ᱵᱟᱨ ᱩᱪᱟᱹᱲ ᱢᱮ ᱾"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/UpdateCard.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/UpdateCard.strings index 2a18a9d29ce83..d141a4f9c3630 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/UpdateCard.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/UpdateCard.strings @@ -1,9 +1,15 @@ /* This value is used as the toast message for the saving success alert in the remember credit card page */ "CreditCard.RememberCard.SecondaryButtonTitle.v116" = "ᱠᱟᱰ ᱥᱟᱦᱴᱟ ᱦᱟᱹᱞᱤᱭᱟᱹᱠ ᱮᱱᱟ"; +/* This value is used as the title for the update card page */ +"CreditCard.UpdateCard.MainTitle.v122" = "ᱠᱟᱰ ᱟᱹᱯᱰᱮᱴ ᱟ ᱥᱮ ?"; + /* This value is used as the title for the Manage Cards button from the update credit card page */ "CreditCard.UpdateCard.ManageCardsButtonTitle.v115" = "ᱠᱟᱰ ᱢᱮᱱᱮᱡᱽ ᱢᱮ"; /* This value is used as the title for the Not Now button in the update credit card page */ "CreditCard.UpdateCard.NotNowButtonTitle.v115" = "ᱱᱤᱛᱚᱜ ᱫᱚ ᱵᱟᱝᱟ"; +/* This value is used as the title for the button in the update credit card page. It indicates the action to update the details f9 the card. */ +"CreditCard.UpdateCard.YesButtonTitle.v122" = "ᱦᱟᱹᱞᱤᱭᱟᱹᱠ"; + diff --git a/firefox-ios/Shared/Supporting Files/si.lproj/Bookmarks.strings b/firefox-ios/Shared/Supporting Files/si.lproj/Bookmarks.strings new file mode 100644 index 0000000000000..bc3ee8b8a1179 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/si.lproj/Bookmarks.strings @@ -0,0 +1,21 @@ +/* When navigating through the bookmarks menu and bookmark folders, a back button with an `All` (bookmarks) label is present to take the user to the top level bookmarks menu. */ +"Bookmarks.Menu.AllBookmarks.v131" = "සියල්ල"; + +/* Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen */ +"Bookmarks.Menu.BookmarksTopLevelTitle.v131" = "පොත්යොමු"; + +/* When a folder is longpressed in the bookmarks menu, a `Delete Folder` button is present. */ +"Bookmarks.Menu.DeleteFolder.v131" = "බහාලුම මකන්න"; + +/* When a bookmark is longpressed in the bookmarks menu, an `Edit Bookmark` button is present. */ +"Bookmarks.Menu.EditBookmark.v131" = "පොත්යොමුව සංස්කරණය"; + +/* Label on the top of the `Edit Bookmarks` screen. */ +"Bookmarks.Menu.EditBookmarkTitle.v131" = "පොත්යොමුව සංස්කරණය"; + +/* When a folder is longpressed in the bookmarks menu, an `Edit Folder` button is present. */ +"Bookmarks.Menu.EditFolder.v131" = "බහාලුම සංස්කරණය"; + +/* The title for the Edit context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.Edit.v131" = "සංස්කරණය"; + diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..d1509caccb2ac --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Posledná hodina"; + diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/MainMenu.strings index 3b89d20d41e1f..5e2513543783a 100644 --- a/firefox-ios/Shared/Supporting Files/sk.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Uložiť"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Podponuka Uložiť"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Prepnúť na plnú verziu webu"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Nástroje"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podponuka Nástroje"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Hľadať na stránke…"; diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings index b53845a4ce946..3be6f678c1128 100644 --- a/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Predvolený vyhľadávací modul"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ďalšie informácie o Návrhoch Firefoxu"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ďalšie informácie o Návrhoch Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatívne vyhľadávače"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Zobrazovať návrhy vyhľadávania"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Panel s adresou – Návrhy Firefoxu"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Panel s adresou – Návrhy Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ďalšie informácie o Návrhoch Firefoxu"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ďalšie informácie o Návrhoch Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Zobrazovať Návrhy Firefoxu v súkromných reláciách"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Zobrazovať Návrhy Ecosia v súkromných reláciách"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Navrhovať z histórie prehliadania"; diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..fb9c9c71c892a --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Zadnja ura"; + diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/MainMenu.strings index dbdfacdbe7e94..eff28c89a50b6 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Shrani"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Podmeni Shrani"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Preklopi na stran za računalnike"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Orodja"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Podmeni Orodja"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Najdi na strani …"; diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings index 812d48e3af7e9..9e54cc60791f2 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Privzeti iskalnik"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Več o Firefoxovih predlogih"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Več o Ecosiaovih predlogih"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Pomožni iskalniki"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Prikaži predloge iskanja"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Naslovna vrstica – Firefoxovi predlogi"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Naslovna vrstica – Ecosiaovi predlogi"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Več o Firefoxovih predlogih"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Več o Ecosiaovih predlogih"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikazuj Firefoxove predloge v zasebnih sejah"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikazuj Ecosiaove predloge v zasebnih sejah"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Iskanje po zgodovini iskanja"; diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/AddressToolbar.strings index f32c0b3689029..2e05521aff184 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Rregullime Privatësie & Sigurie"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "Hap përzgjedhje motori kërkimesh"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "Motor Kërkimesh: %@"; diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings index ac94f8f8df218..4da07b165d06d 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings @@ -85,6 +85,9 @@ /* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ "Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Komunë"; + /* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ "Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Fshat ose Lokalitet"; diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/FxANotification.strings new file mode 100644 index 0000000000000..fb2188d530fb7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "Skeda %1$@ të mbyllura: %2$@"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "Shihni skeda të mbyllura së fundi"; + diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..43eb9acacc9e6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Orën e Fundit"; + diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/MainMenu.strings index 0c1f272129368..56e1ba0a61822 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Ruaje"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Ruaje nënmenunë"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Kaloni te sajti për desktop"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Mjete"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Nënmenu mjetesh"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Gjeni në Faqe…"; diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..68f863a736b2c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "Mbylle"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "Bën hapjen e këtij kërkimi"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "Rregullime mbi kërkimin"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "Këtë herë kërko me:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "Rregullime Mbi Kërkimin"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "Këtë herë kërko me:"; + diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/Settings.strings index f115c1a6db2a3..4ddac5b44f430 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Motor Parazgjedhje Kërkimesh"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Mësoni më tepër rreth “Firefox-i Sugjeron”"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Mësoni më tepër rreth “Ecosia-i Sugjeron”"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Motorë Alternativë Kërkimesh"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Shfaq Sugjerime Kërkimi"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Shtyllë adresash - Firefox Sugjeron"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Shtyllë adresash - Ecosia Sugjeron"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Mësoni më tepër rreth “Firefox-i Sugjeron”"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Mësoni më tepër rreth “Ecosia-i Sugjeron”"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Shfaq në sesione private sugjerime nga Firefox-i Sugjeron"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Shfaq në sesione private sugjerime nga Ecosia-i Sugjeron"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Kërko në Historik Shfletimi"; diff --git a/firefox-ios/Shared/Supporting Files/su.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/su.lproj/Settings.strings index 8ca9bed2b5376..4d8a30b136a97 100644 --- a/firefox-ios/Shared/Supporting Files/su.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/su.lproj/Settings.strings @@ -89,7 +89,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Mesin Pamaluruh Baku"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Leuwih teleb ngeunaan Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Leuwih teleb ngeunaan Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Mesin Pamaluruhan Séjénna"; @@ -125,10 +125,10 @@ "Settings.Search.ShowSuggestions.v121" = "Témbongkeun Saran Maluruh"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Palang alamat - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Palang alamat - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Leuwih teleb ngeunaan Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Leuwih teleb ngeunaan Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ "Settings.Search.Suggest.PrivateSession.Description.v125" = "Témbongkeun saran ti mesin pamaluruh dina rintakan nyamuni"; diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..d323c47eb201c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Senaste timmen"; + diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/MainMenu.strings index fe10a3b600097..57953dbf63dc3 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Spara"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Undermeny spara"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Växla till datorwebbplats"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Verktyg"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Undermeny verktyg"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Hitta på sidan…"; diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings index cc1ead1f3be6e..0bc2c57ca0370 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardsökmotor"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Läs mer om Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Läs mer om Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternativa sökmotorer"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Visa sökförslag"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adressfält - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adressfält - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Läs mer om Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Läs mer om Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Visa förslag från Firefox Suggest i privata sessioner"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Visa förslag från Ecosia Suggest i privata sessioner"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Sök i webbläsarhistorik"; diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings index 7fa41a851787e..d9f02f3816cb4 100644 --- a/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings +++ b/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings @@ -7,6 +7,9 @@ /* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ "AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "การตั้งค่าความเป็นส่วนตัวและความปลอดภัย"; +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint describing what tapping the search engine icon does. */ +"AddressToolbar.SearchEngine.A11y.Hint.v133" = "เปิดการเลือกเครื่องมือค้นหา"; + /* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ "AddressToolbar.SearchEngine.A11y.Label.v128" = "เครื่องมือค้นหา: %@"; diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/th.lproj/FxANotification.strings new file mode 100644 index 0000000000000..2496439e3249c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/FxANotification.strings @@ -0,0 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "ปิดแท็บ %1$@ แล้ว %2$@ แท็บ"; + +/* Label for an action used to view recently closed tabs. */ +"CloseTab.ViewAction.title.v133" = "ดูแท็บที่ปิดล่าสุด"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/th.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..ae3e80f23cb49 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "ชั่วโมงที่แล้ว"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/th.lproj/MainMenu.strings index 4f3fd2a59451a..6bbf4a4297002 100644 --- a/firefox-ios/Shared/Supporting Files/th.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/th.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "บันทึก"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "บันทึกเมนูย่อย"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "สลับเป็นไซต์เดสก์ท็อป"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "เครื่องมือ"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "เมนูย่อยของเครื่องมือ"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "ค้นหาในหน้า…"; diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/SearchEngineSelection.strings b/firefox-ios/Shared/Supporting Files/th.lproj/SearchEngineSelection.strings new file mode 100644 index 0000000000000..b8ddcb45b4c3f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/SearchEngineSelection.strings @@ -0,0 +1,18 @@ +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the sheet's close button. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.CloseButton.Label.v133" = "ปิด"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility hint for tapping the search settings row at the bottom of the list, which opens the app's search settings screen. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Hint.v133" = "เปิดการตั้งค่าการค้นหา"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the row at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.SearchSettings.Label.v133" = "ตั้งค่าการค้นหา"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the accessibility label for the title of that sheet. */ +"UnifiedSearch.SearchEngineSelection.AccessibilityLabels.TopTitle.Label.v133" = "ครั้งนี้ค้นหาใน:"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This string is the label for the button at the bottom of the list. When this row is tapped, the app's search settings screen appears. */ +"UnifiedSearch.SearchEngineSelection.SearchSettings.Title.v133" = "การตั้งค่าการค้นหา"; + +/* When the user taps the search engine icon in the toolbar, a sheet with a list of alternative search engines appears. This is the title for the sheet. */ +"UnifiedSearch.SearchEngineSelection.TopTitle.Title.v133" = "ครั้งนี้ค้นหาใน:"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/th.lproj/Settings.strings index 7f0248f07338a..716c54be53eb3 100644 --- a/firefox-ios/Shared/Supporting Files/th.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/th.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "เครื่องมือค้นหาเริ่มต้น"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "เรียนรู้เพิ่มเติมเกี่ยวกับ Firefox Suggest"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "เรียนรู้เพิ่มเติมเกี่ยวกับ Ecosia Suggest"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "เครื่องมือค้นหาทดแทน"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "แสดงข้อเสนอแนะการค้นหา"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "แถบที่อยู่ - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "แถบที่อยู่ - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "เรียนรู้เพิ่มเติมเกี่ยวกับ Firefox Suggest"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "เรียนรู้เพิ่มเติมเกี่ยวกับ Ecosia Suggest"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "แสดงคำแนะนำจาก Firefox Suggest ในวาระส่วนตัว"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "แสดงคำแนะนำจาก Ecosia Suggest ในวาระส่วนตัว"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "ค้นหาประวัติการเรียกดู"; diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..8e5bd0918b2a1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Son bir saat"; + diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/MainMenu.strings index 093201277b992..93e031376462e 100644 --- a/firefox-ios/Shared/Supporting Files/tr.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Kaydet"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Alt menüyü kaydet"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Masaüstü sitesine geç"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Araçlar"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Araçlar alt menüsü"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Sayfada bul…"; diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings index 118bb9141d386..62b4d68788f27 100644 --- a/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Varsayılan arama motoru"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox Önerileri hakkında bilgi alın"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia Önerileri hakkında bilgi alın"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternatif arama motorları"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Arama önerilerini göster"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adres çubuğu - Firefox Önerileri"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adres çubuğu - Ecosia Önerileri"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox Önerileri hakkında bilgi alın"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia Önerileri hakkında bilgi alın"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Gizli oturumlarda Firefox Önerileri’nin önerilerini göster"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Gizli oturumlarda Ecosia Önerileri’nin önerilerini göster"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Gezinti geçmişinde ara"; diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..972a1b259c97a --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "ئاخىرقى سائەت"; + diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/MainMenu.strings index 3c0146a9a6df6..c98cfcd55b4da 100644 --- a/firefox-ios/Shared/Supporting Files/ug.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "ساقلا"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "تارماق تىزىملىكنى ساقلا"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "ئۈستەل يۈزى تور بېكىتىگە ئالماشتۇر"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "قورال"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "قورال تارماق تىزىملىك"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "بەتتىن ئىزدە…"; diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings index 615ad26289d38..5c19b9d2cb7c2 100644 --- a/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "كۆڭۈلدىكى ئىزدەش موتورى"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Firefox تەكلىپى ھەققىدىكى تەپسىلاتلار"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Ecosia تەكلىپى ھەققىدىكى تەپسىلاتلار"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "زاپاس ئىزدەش موتورى"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "ئىزدەش تەكلىپلىرىنى كۆرسىتىدۇ"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "ئادرېس بالدىقى - Firefox تەكلىپى"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "ئادرېس بالدىقى - Ecosia تەكلىپى"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Firefox تەكلىپى ھەققىدىكى تەپسىلاتلار"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Ecosia تەكلىپى ھەققىدىكى تەپسىلاتلار"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "شەخسىي سۆزلەشكۈدىكى Firefox تەۋسىيەسىنى كۆرسىتىدۇ"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "شەخسىي سۆزلەشكۈدىكى Ecosia تەۋسىيەسىنى كۆرسىتىدۇ"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "زىيارەت تارىخىنى ئىزدە"; diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..a0f0413f2f2a9 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Остання година"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/MainMenu.strings index f302c699842a5..8fbc220ce456a 100644 --- a/firefox-ios/Shared/Supporting Files/uk.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Зберегти"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Підменю Зберегти"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Повна версія сайту"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Інструменти"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Підменю Інструменти"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Знайти на сторінці…"; diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/Settings.strings index e9250072af2b0..5bcbba61661b5 100644 --- a/firefox-ios/Shared/Supporting Files/uk.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Типова пошукова система"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Докладніше про Пропозиції Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Докладніше про Пропозиції Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Альтернативні пошукові системи"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Пошукові пропозиції"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Панель адреси - Пропозиції Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Панель адреси - Пропозиції Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Докладніше про Пропозиції Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Докладніше про Пропозиції Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Показувати пропозиції Firefox у приватних сеансах"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Показувати пропозиції Ecosia у приватних сеансах"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Шукати в історії перегляду"; diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..d998236bc92cc --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "Một tiếng trước"; + diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/MainMenu.strings index 42761af0452b0..352c2604a4bd7 100644 --- a/firefox-ios/Shared/Supporting Files/vi.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "Lưu"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "Lưu menu phụ"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "Giao diện máy tính"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "Công cụ"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "Công cụ menu phụ"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "Tìm trong trang…"; diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings index 22cc1388f1ffc..2a026b865ce5a 100644 --- a/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Công cụ tìm kiếm mặc định"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Tìm hiểu thêm về đề xuất của Firefox"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Tìm hiểu thêm về đề xuất của Ecosia"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Công cụ tìm kiếm thay thế"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "Hiển thị đề xuất tìm kiếm"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Thanh địa chỉ - Đề xuất của Firefox"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Thanh địa chỉ - Đề xuất của Ecosia"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Tìm hiểu thêm về đề xuất của Firefox"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Tìm hiểu thêm về đề xuất của Ecosia"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "Hiển thị đề xuất ​​Firefox trong phiên riêng tư"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Hiển thị đề xuất ​​Ecosia trong phiên riêng tư"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Tìm kiếm lịch sử duyệt web"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/FxANotification.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/FxANotification.strings index 2ba64b1fcbb46..51919d71d61eb 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/FxANotification.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/FxANotification.strings @@ -1,3 +1,6 @@ +/* Title of notification shown when a remote device has requested to close a number of tabs. First parameter is the app name and the second parameter is the number of tabs. */ +"CloseTab.ArrivingNotification.title.v133" = "%1$@ 已关闭 %2$@ 个标签页"; + /* Label for an action used to view recently closed tabs. */ "CloseTab.ViewAction.title.v133" = "查看最近关闭的标签页"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..f32e0c7647cd8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "上一小时"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/MainMenu.strings index 4deb69af718ee..4b4c6a5e23103 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "保存"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "保存子菜单"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "切换到桌面版网站"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "工具"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "工具子菜单"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "页内查找…"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings index 436c2a04e0f59..ca331dd8eba42 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "默认搜索引擎"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "详细了解 Firefox 建议"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "详细了解 Ecosia 建议"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "备用搜索引擎"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "显示搜索建议"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "地址栏 - Firefox 建议"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "地址栏 - Ecosia 建议"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "详细了解 Firefox 建议"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "详细了解 Ecosia 建议"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "在隐私浏览中显示来自 Firefox 建议的结果"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "在隐私浏览中显示来自 Ecosia 建议的结果"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "搜索浏览历史"; diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/LibraryPanel.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/LibraryPanel.strings new file mode 100644 index 0000000000000..0b1a0e12ca1b5 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/LibraryPanel.strings @@ -0,0 +1,3 @@ +/* This label is meant to signify the section containing a group of items from the past hour. This is primarily used in the history library panel when grouping sites that have been visited in the last hour. */ +"LibraryPanel.Sections.LastHour.v134" = "一小時內"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/MainMenu.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/MainMenu.strings index 59107549c3380..8b5f3710076a1 100644 --- a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/MainMenu.strings +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/MainMenu.strings @@ -256,6 +256,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ "MainMenu.ToolsSection.AccessibilityLabels.Save.v132" = "儲存"; +/* On the main menu, the accessibility label for the action that will take the user to the Save submenu in the menu. In the main menu, there is an option called Save that is taking the user to the Save submenu where user can share, bookmark the page and so on. */ +"MainMenu.ToolsSection.AccessibilityLabels.Save.v133" = "儲存子選單"; + /* On the main menu, the accessibility label for the action that will switch a site from mobile version to the desktop version, if available. */ "MainMenu.ToolsSection.AccessibilityLabels.SwitchToDesktopSite.v132" = "切換為桌面版"; @@ -265,6 +268,9 @@ /* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ "MainMenu.ToolsSection.AccessibilityLabels.Tools.v132" = "工具"; +/* On the main menu, the accessibility label for the action that will take the user to the Tools submenu in the menu. */ +"MainMenu.ToolsSection.AccessibilityLabels.Tools.v133" = "工具子選單"; + /* On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page. */ "MainMenu.ToolsSection.FindInPage.Title.v131" = "在頁面中搜尋…"; diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings index 71f08b346e6d8..14c2518c4ac94 100644 --- a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings @@ -101,7 +101,7 @@ "Settings.Search.Accessibility.DefaultSearchEngine.v121" = "預設搜尋引擎"; /* Accessibility label for Learn more about Firefox Suggest. */ -"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "了解 Firefox Suggest 的相關資訊"; +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "了解 Ecosia Suggest 的相關資訊"; /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "替代搜尋引擎"; @@ -137,13 +137,13 @@ "Settings.Search.ShowSuggestions.v121" = "顯示搜尋建議"; /* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ -"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "網址列 - Firefox Suggest"; +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "網址列 - Ecosia Suggest"; /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ -"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "了解 Firefox Suggest 的相關資訊"; +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "了解 Ecosia Suggest 的相關資訊"; /* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ -"Settings.Search.Suggest.PrivateSession.Description.v125" = "於隱私瀏覽階段中顯示 Firefox Suggest 的建議內容"; +"Settings.Search.Suggest.PrivateSession.Description.v125" = "於隱私瀏覽階段中顯示 Ecosia Suggest 的建議內容"; /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "顯示瀏覽紀錄"; diff --git a/firefox-ios/Shared/TimeConstants.swift b/firefox-ios/Shared/TimeConstants.swift index f2086e1c78252..5b2d854fef82e 100644 --- a/firefox-ios/Shared/TimeConstants.swift +++ b/firefox-ios/Shared/TimeConstants.swift @@ -182,6 +182,9 @@ extension Date { extension Date { public static var yesterday: Date { return Date().dayBefore } public static var tomorrow: Date { return Date().dayAfter } + public var lastHour: Date { + return Calendar.current.date(byAdding: .hour, value: -1, to: self) ?? Date() + } public var lastTwoWeek: Date { return Calendar.current.date(byAdding: .day, value: -14, to: noon) ?? Date() } @@ -212,12 +215,19 @@ extension Date { return Calendar.current.isDateInYesterday(self) } - public func isWithinLast7Days() -> Bool { - return (Date().lastWeek ... Date()).contains(self) + /// Comparison date is used to control unit tests outcome. + public func isWithinLast7Days(comparisonDate: Date = Date()) -> Bool { + return (comparisonDate.lastWeek ... comparisonDate).contains(self) + } + + /// Comparison date is used to control unit tests outcome. + public func isWithinLast14Days(comparisonDate: Date = Date()) -> Bool { + return (comparisonDate.lastTwoWeek ... comparisonDate).contains(self) } - public func isWithinLast14Days() -> Bool { - return (Date().lastTwoWeek ... Date()).contains(self) + /// Comparison date is used to control unit tests outcome. + public func isWithinLastHour(comparisonDate: Date = Date()) -> Bool { + return (comparisonDate.lastHour ... comparisonDate).contains(self) } } diff --git a/firefox-ios/Shared/UserAgent.swift b/firefox-ios/Shared/UserAgent.swift index 3955b25b3dd40..cad6c451fb041 100644 --- a/firefox-ios/Shared/UserAgent.swift +++ b/firefox-ios/Shared/UserAgent.swift @@ -5,11 +5,20 @@ import Common import WebKit import UIKit +import Ecosia open class UserAgent { public static let uaBitSafari = "Safari/605.1.15" public static let uaBitMobile = "Mobile/15E148" public static let uaBitFx = "FxiOS/\(AppInfo.appVersion)" + // Ecosia: Add Ecosia UserAgent info + public static let uaBitVersion = "Version/\(UIDevice.current.systemVersion)" + public static var uaBitEcosia: String { + let ecosiaAppVersion = AppInfo.applicationBundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String + return "(Ecosia ios@\(ecosiaAppVersion).\(AppInfo.buildNumber))" + } + public static let ecosiaDesktopUA = "\(UserAgent.desktopUserAgent()) \(UserAgent.uaBitEcosia)" + public static let product = "Mozilla/5.0" public static let platform = "AppleWebKit/605.1.15" public static let platformDetails = "(KHTML, like Gecko)" @@ -30,19 +39,27 @@ open class UserAgent { } public static var syncUserAgent: String { - return clientUserAgent(prefix: "Firefox-iOS-Sync") + // Ecosia: update UA prefix + // return clientUserAgent(prefix: "Firefox-iOS-Sync") + return clientUserAgent(prefix: "Ecosia-iOS-Sync") } public static var tokenServerClientUserAgent: String { - return clientUserAgent(prefix: "Firefox-iOS-Token") + // Ecosia: update UA prefix + // return clientUserAgent(prefix: "Firefox-iOS-Token") + return clientUserAgent(prefix: "Ecosia-iOS-Token") } public static var fxaUserAgent: String { - return clientUserAgent(prefix: "Firefox-iOS-FxA") + // Ecosia: update UA prefix + // return clientUserAgent(prefix: "Firefox-iOS-FxA") + return clientUserAgent(prefix: "Ecosia-iOS-EcosiaA") } public static var defaultClientUserAgent: String { - return clientUserAgent(prefix: "Firefox-iOS") + // Ecosia: update UA prefix + // return clientUserAgent(prefix: "Firefox-iOS") + return clientUserAgent(prefix: "Ecosia-iOS") } public static func isDesktop(ua: String) -> Bool { @@ -56,7 +73,7 @@ open class UserAgent { } public static func mobileUserAgent() -> String { - return UserAgentBuilder.defaultMobileUserAgent().userAgent() + return UserAgentBuilder.defaultMobileUserAgent().userAgent() } public static func oppositeUserAgent(domain: String) -> String { @@ -69,11 +86,28 @@ open class UserAgent { } public static func getUserAgent(domain: String, platform: UserAgentPlatform) -> String { + /* Ecosia: review User Aget retrieval + switch platform { + case .Desktop: + guard let customUA = CustomUserAgentConstant.customDesktopUAForDomain[domain] else { + return desktopUserAgent() + } + return customUA + case .Mobile: + guard let customUA = CustomUserAgentConstant.customMobileUAForDomain[domain] else { + return mobileUserAgent() + } + return customUA + } + */ switch platform { case .Desktop: + if let customUA = CustomUserAgentConstant.customDesktopUAForDomain[domain] { + return customUA + } return desktopUserAgent() case .Mobile: - if let customUA = CustomUserAgentConstant.customUAFor[domain] { + if let customUA = CustomUserAgentConstant.customMobileUAForDomain[domain] { return customUA } else { return mobileUserAgent() @@ -97,14 +131,26 @@ public enum UserAgentPlatform { case Mobile } -public struct CustomUserAgentConstant { +struct CustomUserAgentConstant { private static let defaultMobileUA = UserAgentBuilder.defaultMobileUserAgent().userAgent() private static let customDesktopUA = UserAgentBuilder.defaultDesktopUserAgent().clone(extensions: "Version/\(AppInfo.appVersion) \(UserAgent.uaBitSafari)") - public static let customUAFor = [ + static let customMobileUAForDomain = [ + // Ecosia: Re-introduce paypal UA so it uses the Firefox one + "paypal.com": UserAgentBuilder.defaultFirefoxMobileUserAgent().userAgent(), + "disneyplus.com": customDesktopUA + ] + + static let customDesktopUAForDomain = [ + /* Ecosia: Update paypal UA so it uses the Firefox one "paypal.com": defaultMobileUA, - "yahoo.com": defaultMobileUA, - "disneyplus.com": customDesktopUA] + */ + "paypal.com": UserAgentBuilder.defaultFirefoxMobileUserAgent().userAgent(), + "firefox.com": defaultMobileUA, + // Ecosia: Add Ecosia URLs + Ecosia.URLProvider.production.domain: UserAgent.ecosiaDesktopUA, + Ecosia.URLProvider.staging.domain: UserAgent.ecosiaDesktopUA + ] } public struct UserAgentBuilder { @@ -158,12 +204,15 @@ public struct UserAgentBuilder { } public static func defaultMobileUserAgent() -> UserAgentBuilder { + /* Ecosia: Always returns the Ecosia's UA as default one return UserAgentBuilder( product: UserAgent.product, systemInfo: "(\(UIDevice.current.model); CPU iPhone OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) like Mac OS X)", platform: UserAgent.platform, platformDetails: UserAgent.platformDetails, extensions: "FxiOS/\(AppInfo.appVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)") + */ + return UserAgentBuilder.ecosiaMobileUserAgent() } public static func defaultDesktopUserAgent() -> UserAgentBuilder { @@ -172,6 +221,30 @@ public struct UserAgentBuilder { systemInfo: "(Macintosh; Intel Mac OS X 10.15)", platform: UserAgent.platform, platformDetails: UserAgent.platformDetails, + /* Ecosia: Update extension extensions: "FxiOS/\(AppInfo.appVersion) \(UserAgent.uaBitSafari)") + */ + extensions: "\(UserAgent.uaBitVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)") + } +} + +// Ecosia: Ecosia Mobile UA + Firefox +extension UserAgentBuilder { + + public static func ecosiaMobileUserAgent() -> UserAgentBuilder { + UserAgentBuilder(product: UserAgent.product, + systemInfo: "(\(UIDevice.current.model); CPU iPhone OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) like Mac OS X)", + platform: UserAgent.platform, + platformDetails: UserAgent.platformDetails, + extensions: "\(UserAgent.uaBitVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari) \(UserAgent.uaBitEcosia)") + } + + // This is what Firefox uses on production + public static func defaultFirefoxMobileUserAgent() -> UserAgentBuilder { + UserAgentBuilder(product: UserAgent.product, + systemInfo: "(\(UIDevice.current.model); CPU iPhone OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) like Mac OS X)", + platform: UserAgent.platform, + platformDetails: UserAgent.platformDetails, + extensions: "FxiOS/\(AppInfo.appVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)") } } diff --git a/firefox-ios/Shared/ab.lproj/3DTouchActions.strings b/firefox-ios/Shared/ab.lproj/3DTouchActions.strings new file mode 100644 index 0000000000000..52bf38d89e046 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/3DTouchActions.strings @@ -0,0 +1,21 @@ +/* Label for preview action on Tab Tray Tab to add current tab to Bookmarks */ +"Add to Bookmarks" = "Адыргақәа рахь иацҵатәуп"; + +/* Label for preview action on Tab Tray Tab to close the current tab */ +"Close Tab" = "Абларҭа арктәуп"; + +/* Label for preview action on Tab Tray Tab to copy the URL of the current tab to clipboard */ +"Copy URL" = "Ақәҭыхра URL"; + +/* Label for preview action on Tab Tray Tab to send the current link to another device */ +"Menu.SendLinkToDevice" = "Аиқәыршәага ахь азхьарԥш ашьҭра"; + +/* String describing the action of opening the last added bookmark from the home screen Quick Actions via 3D Touch */ +"Open Last Bookmark" = "Иаарттәуп аҵыхәтәантәи адырга"; + +/* Accessibility label, associated to the 3D Touch action on the current tab in the tab tray, used to display a larger preview of the tab. */ +"Preview of %@" = "Аԥхьахәаԥшра %@"; + +/* Label for preview action on Tab Tray Tab to send the current tab to another device */ +"Send to Device" = "Аиқәыршәага ахь ашьҭра"; + diff --git a/firefox-ios/Shared/ab.lproj/AuthenticationManager.strings b/firefox-ios/Shared/ab.lproj/AuthenticationManager.strings new file mode 100644 index 0000000000000..99c15ea761c7f --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/AuthenticationManager.strings @@ -0,0 +1,6 @@ +/* Text displayed above the input field when changing the existing passcode */ +"Enter passcode" = "Иҭажәгал амашәыртә код"; + +/* Touch ID prompt subtitle when accessing logins */ +"Use your fingerprint to access Logins now." = "Аҭаларақәа рахь анеиразы, уажәы шәнацәашьҭа шәхы иашәырхәа."; + diff --git a/firefox-ios/Shared/ab.lproj/ClearHistoryConfirm.strings b/firefox-ios/Shared/ab.lproj/ClearHistoryConfirm.strings new file mode 100644 index 0000000000000..22294842ac1cb --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/ClearHistoryConfirm.strings @@ -0,0 +1,9 @@ +/* The cancel button when confirming clear history. */ +"Cancel" = "Аԥыхра"; + +/* The confirmation button that clears history even when Sync is connected. */ +"OK" = "ИБЗИОУП"; + +/* Description of the confirmation dialog shown when a user tries to clear history that's synced to another device. */ +"This action will clear all of your private data, including history from your synced devices." = "Ари аус ухатә даннақәа зегьы аныхуеит, аҭоурыхгьы уахь иналаҵаны, асинхронизациа зызу ахархәагақәа зегьы рҟынтә."; + diff --git a/firefox-ios/Shared/ab.lproj/ClearPrivateData.strings b/firefox-ios/Shared/ab.lproj/ClearPrivateData.strings new file mode 100644 index 0000000000000..bb34d7d79618b --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/ClearPrivateData.strings @@ -0,0 +1,21 @@ +/* Settings item for clearing browsing history */ +"Browsing History" = "Аҭаара ажурнал"; + +/* Settings item for clearing the cache */ +"Cache" = "Акеш"; + +/* Settings item for clearing cookies */ +"Cookies" = "Акукиқәа"; + +/* Settings item for deleting downloaded files */ +"Downloaded Files" = "Иҭагалоу афаилқәа"; + +/* Settings item for clearing website data */ +"Offline Website Data" = "Аоффлаин Асаит адыррақәа"; + +/* Settings item for clearing passwords and login data */ +"Saved Logins" = "Иеиқәырхоу аҭаларақәа"; + +/* A settings item that allows a user to use Apple's \"Spotlight Search\" in Data Management's Website Data option to search for and select an item to delete. */ +"Spotlight Index" = "Аиндекс азҿлымҳара"; + diff --git a/firefox-ios/Shared/ab.lproj/ClearPrivateDataConfirm.strings b/firefox-ios/Shared/ab.lproj/ClearPrivateDataConfirm.strings new file mode 100644 index 0000000000000..66bfa96522f98 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/ClearPrivateDataConfirm.strings @@ -0,0 +1,9 @@ +/* The cancel button when confirming clear private data. */ +"Cancel" = "Аԥыхра"; + +/* The button that clears private data. */ +"OK" = "ИБЗИОУП"; + +/* Description of the confirmation dialog shown when a user tries to clear their private data. */ +"This action will clear all of your private data. It cannot be undone." = "Ари аҟаҵара ухатә данноқәа зегьы аныхуеит. Уи ԥсахшьа амам."; + diff --git a/firefox-ios/Shared/ab.lproj/Default Browser.strings b/firefox-ios/Shared/ab.lproj/Default Browser.strings new file mode 100644 index 0000000000000..e20e95efbae84 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Default Browser.strings @@ -0,0 +1,45 @@ +/* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia убраузер еиԥш алхра иаарту, насгьы зегьы ирзымариоу Интернет азы абжьыҭира ауп."; + +/* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.BetterInternet.Title.v108" = "Еиӷьу аинтернет ахь аиасра"; + +/* Button string to learn how to set your default browser. */ +"DefaultBrowserCard.Button.v2" = "Еилышәкаа ишыҟало"; + +/* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ +"DefaultBrowserCard.Description" = "Ecosia аҟны асаитқәа, аимеилқәа, Ацҳарақәа рҟынтәи автоматла иаартуа азхьарԥшқәа шьақәыргылатәуп."; + +/* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.NextLevel.Description.v108" = "Иалышәх Ecosia шәбраузер еиԥш аццакыра, ашәарҭадара, амаӡара рзы."; + +/* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.NextLevel.Title.v108" = "Есыҽнытәи уԥшаара еиӷьтәтәуп"; + +/* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia есымза ахархәаҩ ибжьаратәла 3,000 тракерк аанкылоит. Умаӡара иазкны угәы ҭынчхарц азы убраузер ҳзыҟаҵа."; + +/* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia амаӡара еиқәнаршәоит"; + +/* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ +"DefaultBrowserCard.Title" = "Иԥсахтәуп ишыҟоу еиԥш иҟоу абраузер"; + +/* Button string to open settings that allows user to switch their default browser to Firefox. */ +"DefaultBrowserOnboarding.Button" = "Аиқәыршәарақәа рахь ацара"; + +/* Description for default browser onboarding card. */ +"DefaultBrowserOnboarding.Description1" = "1. Аиқәыршәарақәа рахь ацара"; + +/* Description for default browser onboarding card. */ +"DefaultBrowserOnboarding.Description2" = "2. Иалшәх \"Абраузер апликациа стандарт\""; + +/* Description for default browser onboarding card. */ +"DefaultBrowserOnboarding.Description3" = "3. Иалшәх Ecosia"; + +/* Text for the screenshot of the iOS system settings page for Firefox. */ +"DefaultBrowserOnboarding.Screenshot" = "Абраузер апликациа ишыҟоу еиԥш"; + +/* Menu option for setting Firefox as default browser. */ +"Settings.DefaultBrowserMenuItem" = "Абраузер ишыҟоу еиԥш ишьақәыргылатәуп"; + diff --git a/firefox-ios/Shared/ab.lproj/ErrorPages.strings b/firefox-ios/Shared/ab.lproj/ErrorPages.strings new file mode 100644 index 0000000000000..9c24827c96dec --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/ErrorPages.strings @@ -0,0 +1,6 @@ +/* Shown in error pages for files that can't be shown and need to be downloaded. */ +"Open in Safari" = "Иаартуп Сафари аҟны"; + +/* Shown in error pages on a button that will try to load the page again */ +"Try again" = "Даҽазнык шәҽазышәшәа"; + diff --git a/firefox-ios/Shared/ab.lproj/FindInPage.strings b/firefox-ios/Shared/ab.lproj/FindInPage.strings new file mode 100644 index 0000000000000..54eb78f5802a6 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/FindInPage.strings @@ -0,0 +1,12 @@ +/* Done button in Find in Page Toolbar. */ +"Done" = "Амазеи"; + +/* Text selection menu item */ +"Find in Page" = "Адаҟьаҿы аԥшаара"; + +/* Accessibility label for next result button in Find in Page Toolbar. */ +"Next in-page result" = "Анаҩстәи аҭак адаҟьаҿы"; + +/* Accessibility label for previous result button in Find in Page Toolbar. */ +"Previous in-page result" = "Уаанӡатәи алҵшәа адаҟьаҿы"; + diff --git a/firefox-ios/Shared/ab.lproj/HistoryPanel.strings b/firefox-ios/Shared/ab.lproj/HistoryPanel.strings new file mode 100644 index 0000000000000..9145f28010e20 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/HistoryPanel.strings @@ -0,0 +1,3 @@ +/* Action button for deleting history entries in the history panel. */ +"Delete" = "Аныхра"; + diff --git a/firefox-ios/Shared/ab.lproj/Intro.strings b/firefox-ios/Shared/ab.lproj/Intro.strings new file mode 100644 index 0000000000000..2824070521ef0 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Intro.strings @@ -0,0 +1,9 @@ +/* Sign in to Firefox account button on second intro screen. */ +"Intro.Slides.Button.SignIn" = "Аҭалара"; + +/* Title for the first panel 'Welcome' in the First Run tour. */ +"Intro.Slides.Welcome.Title.v2" = "Бзиала шәаабеит Ecosia ахь"; + +/* See http://mzl.la/1T8gxwo */ +"Start Browsing" = "Аинтернет аҭаара алагара"; + diff --git a/firefox-ios/Shared/ab.lproj/Localizable.strings b/firefox-ios/Shared/ab.lproj/Localizable.strings new file mode 100644 index 0000000000000..e6455eb115b60 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Localizable.strings @@ -0,0 +1,1803 @@ +/* Button for smaller reader font size. Keep this extremely short! This is shown in the reader mode toolbar. */ +"-" = "-"; + +/* Button for larger reader font size. Keep this extremely short! This is shown in the reader mode toolbar. */ +"+" = "+"; + +/* Authentication prompt message with no realm. Parameter is the hostname of the site */ +"A username and password are being requested by %@." = "%@ ахархәаҩ ихьӡи аԥсыӡажәеи рзы иаҳәоит."; + +/* Authentication prompt message with a realm. First parameter is the hostname. Second is the realm string */ +"A username and password are being requested by %@. The site says: %@" = "%1$@ ахархәаҩ ихьӡи аԥсыӡажәеи ирыҳәоит. Асаит иаҳәоит: %2$@"; + +/* Button for reader mode font size. Keep this extremely short! This is shown in the reader mode toolbar. */ +"Aa" = "Аа"; + +/* About settings section title */ +"About" = "Апрограмма иазкны"; + +/* The title for the pinning a shortcut action */ +"ActivityStream.ContextMenu.AddToShortcuts" = "Аҿацәқәа рахь иацҵатәуп"; + +/* The title for the pinning a topsite action */ +"ActivityStream.ContextMenu.PinTopsite2" = "Аидҳәалара"; + +/* The title for the unpinning a topsite action */ +"ActivityStream.ContextMenu.UnpinTopsite" = "Аԥырҵра"; + +/* Title for the Jump Back In section. This section allows users to jump back in to a recently viewed tab */ +"ActivityStream.JumpBackIn.SectionTitle" = "Ухынҳәы"; + +/* On the Firefox homepage in the Jump Back In section, if a Tab group item - a collection of grouped tabs from a related search - exists underneath the search term for the tab group, there will be a subtitle with a number for how many tabs are in that group. The placeholder is for a number. It will read 'Tabs: 5' or similar. */ +"ActivityStream.JumpBackIn.TabGroup.SiteCount" = "Аҭаӡҩырақәа: %d"; + +/* A string used to signify the start of the Recently Saved section in Home Screen. */ +"ActivityStream.Library.Title" = "Ааигәа иацҵоу"; + +/* Section title label for recently visited websites */ +"ActivityStream.RecentHistory.Title" = "Ааигәа дахьнеиз"; + +/* Section title label for Shortcuts */ +"ActivityStream.Shortcuts.SectionTitle" = "Аҿацәқәа"; + +/* The title for the setting page which lets you select the number of top site rows */ +"ActivityStream.TopSites.RowSettingFooter" = "Ацәаҳәақәа рхыԥхьаӡара"; + +/* Accessibility label for the Add Tab button in the Tab Tray. */ +"Add Tab" = "Агәылаҵа ацҵатәуп"; + +/* Name for button adding current article to reading list in reader mode */ +"Add to Reading List" = "Аԥхьара ахьӡынҵа иацҵатәуп"; + +/* Accessibility message e.g. spoken by VoiceOver after the current page gets added to the Reading List using the Reader View button, e.g. by long-pressing it or by its accessibility custom action. */ +"Added page to Reading List" = "Адаҟьа аԥхьара ахьӡынҵа иацҵоуп"; + +/* Button to dismiss the 'Add Pass Failed' alert. See https://support.apple.com/HT204003 for context on Wallet. */ +"AddPass.Error.Dismiss" = "ИБЗИОУП"; + +/* Text of the 'Add Pass Failed' alert. See https://support.apple.com/HT204003 for context on Wallet. */ +"AddPass.Error.Message" = "Агха ҟалеит Аԥараҭра амаӡажәа ацҵараан. Ҳаҳәоит даҽазнык шәҽазышәшәа."; + +/* Title of the 'Add Pass Failed' alert. See https://support.apple.com/HT204003 for context on Wallet. */ +"AddPass.Error.Title" = "Амаӡажәа ацҵара алымшеит"; + +/* Accessibility label for address and search field, both words (Address, Search) are therefore nouns. */ +"Address and Search" = "Адреси аԥшаареи"; + +/* Accessibility label for action adding current page to reading list. */ +"Address.Bar.ReadingList.v106" = "Аԥхьара ахьӡынҵа иацҵатәуп"; + +/* Accessibility label for the Address Bar, where a user can enter the search they wish to make */ +"Address.Bar.v99" = "Адресқәа рпанель"; + +/* Used as a button label for crash dialog prompt */ +"Always Send" = "Еснагь иаашәышьҭла"; + +/* Tile title for Amazon */ +"Amazon" = "Амазон"; + +/* Authentication prompt title */ +"Authentication required" = "Аутентификациа аҭахуп"; + +/* Accessibility label for the Back button in the tab toolbar. */ +"Back" = "Шьҭахьҟа аара"; + +/* Block pop-up windows setting */ +"Block Pop-up Windows" = "Ицәырҵуа аблокатор"; + +/* The label for the Title field when editing a bookmark */ +"Bookmark.DetailFieldTitle.Label" = "Ахы"; + +/* The label for the URL field when editing a bookmark */ +"Bookmark.DetailFieldURL.Label" = "URL"; + +/* Toggle bookmarks syncing setting */ +"Bookmarks" = "Адыргақәа"; + +/* The button on the snackbar to edit a bookmark after adding it. */ +"Bookmarks.Edit.Button" = "Аԥсахра"; + +/* The button to edit a bookmark */ +"Bookmarks.EditBookmark.Label" = "Аҭыӡдырга аредакциа азутәуп"; + +/* The button to edit a folder */ +"Bookmarks.EditFolder.Label" = "Аҭаӡ ԥсахтәуп"; + +/* The label to show the location of the folder where the bookmark is located */ +"Bookmarks.Folder.Label" = "Апапка"; + +/* A label indicating all bookmarks grouped under the category 'Desktop Bookmarks'. */ +"Bookmarks.Menu.DesktopBookmarks" = "Шәкомпиутер аҿы аҭыӡдыргақәа"; + +/* The button to create a new bookmark */ +"Bookmarks.NewBookmark.Label" = "Адырга ҿыц"; + +/* The button to create a new folder */ +"Bookmarks.NewFolder.Label" = "Аҭаӡ ҿыц"; + +/* The button to create a new separator */ +"Bookmarks.NewSeparator.Label" = "Аиҟәыҭхага ҿыц"; + +/* The label for the title of a bookmark */ +"Bookmarks.Title.Label" = "Ахы"; + +/* Describes the date on which the breach occurred */ +"BreachAlerts.BreachDate" = "Ари ацәцара ҟалеит"; + +/* Description of what a breach is */ +"BreachAlerts.Description" = "Аҵыхәтәантәи аамҭазы уԥсыӡажәа ануԥсах нахыс, уԥсыӡажәақәа рцәыӡит ма иӷьычит. Ари ахыԥхьаӡара ахьчаразы, асаит шәҭаланы шәпароль шәыԥсах."; + +/* Link to monitor.firefox.com to learn more about breached passwords */ +"BreachAlerts.LearnMoreButton" = "Еиҳаны аинформациа"; + +/* Leads to a link to the breached website */ +"BreachAlerts.Link" = "Ииастәуп"; + +/* Title for the Breached Login Detail View. */ +"BreachAlerts.Title" = "Асаит адыррақәа рцәцара"; + +/* Accessibility label for brightness adjustment slider in Reader Mode display settings */ +"Brightness" = "Алашара"; + +/* Label for Cancel button */ +"Cancel" = "Аԥыхра"; + +/* Accessibility hint for the color theme setting buttons in reader mode display settings */ +"Changes color theme." = "Иԥсахуеит аԥштәы атема."; + +/* Accessibility hint for the font type buttons in reader mode display settings */ +"Changes font type." = "Иԥсахуеит ашрифт ахкы."; + +/* The button to open a new tab with the copied link */ +"ClipboardToast.GoToCopiedLink.Button" = "Ацара"; + +/* Message displayed when the user has a copied link on the clipboard */ +"ClipboardToast.GoToCopiedLink.Title" = "Иқәҭыху азхьарԥш шәықәныҟәа?"; + +/* Accessibility label for action denoting closing a tab in tab list (tray) */ +"Close" = "Аркра"; + +/* Accessibility label (used by assistive technology) notifying the user that the tab is being closed. */ +"Closing tab" = "Агәылаҵа аркра"; + +/* Context menu item for bookmarking a link URL */ +"ContextMenu.BookmarkLinkButtonTitle" = "Адыргақәа рахь иацҵатәуп"; + +/* The label text in the Button Toast for switching to a fresh New Private Tab. */ +"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText" = "Ихатәу абларҭа ҿыц аартуп"; + +/* The label text in the Button Toast for switching to a fresh New Private Tab. */ +"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText.v113" = "Ихатәу абларҭа ҿыц аартуп"; + +/* The button text in the Button Toast for switching to a fresh New Tab. */ +"ContextMenu.ButtonToast.NewTabOpened.ButtonText" = "Ацара"; + +/* The label text in the Button Toast for switching to a fresh New Tab. */ +"ContextMenu.ButtonToast.NewTabOpened.LabelText" = "Агәылаҵа ҿыц аартуп"; + +/* The label text in the Button Toast for switching to a fresh New Tab. */ +"ContextMenu.ButtonToast.NewTabOpened.LabelText.v114" = "Агәылаҵа ҿыц аартуп"; + +/* Context menu item for copying an image to the clipboard */ +"ContextMenu.CopyImageButtonTitle" = "Асахьа ақәҭыхра"; + +/* Context menu item for copying an image URL to the clipboard */ +"ContextMenu.CopyImageLinkButtonTitle" = "Асахьа азхьарԥш ақәҭыхра"; + +/* Context menu item for copying a link URL to the clipboard */ +"ContextMenu.CopyLinkButtonTitle" = "Азхьарԥш ақәҭыхра"; + +/* Context menu item for downloading a link URL */ +"ContextMenu.DownloadLinkButtonTitle" = "Аҵаҩра аҟынтәи аҭагалара"; + +/* Context menu item for opening a link in a new tab */ +"ContextMenu.OpenInNewTabButtonTitle" = "Агәылаҵа ҿыц аҿы иаарттәуп"; + +/* Context menu item for saving an image */ +"ContextMenu.SaveImageButtonTitle" = "Еиқәырхатәуп асахьа"; + +/* Context menu item for sharing a link URL */ +"ContextMenu.ShareLinkButtonTitle" = "Аиҩшара азхьарԥш"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing search bar placement. It indicates a user can navigate to the settings page that allows them to customize the placement of the search bar. */ +"ContextualHints.SearchBarPlacement.CallToAction" = "Амаругақәа рпанель архиарақәа"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one talks about the inactive tabs feature. */ +"ContextualHints.TabTray.InactiveTabs" = "Ҩымчыбжь рыла узҭамлаз анҵамҭақәа арахь ииагахоит."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is the call to action for the inactive tabs contextual popup. */ +"ContextualHints.TabTray.InactiveTabs.CallToAction" = "Аиқәыршәарақәа рҿы аҿыхтәуп"; + +/* Accessibility label for action denoting closing contextual hint. */ +"ContextualHintsCloseButtonAccessibility.v105" = "Аркра"; + +/* Accessibility message e.g. spoken by VoiceOver after adding current webpage to the Reading List failed. */ +"Could not add page to Reading list" = "Адаҟьа аԥхьара ахьӡынҵахь ацҵара алымшеит"; + +/* Accessibility message e.g. spoken by VoiceOver after the user wanted to add current page to the Reading List and this was not done, likely because it already was in the Reading List, but perhaps also because of real failures. */ +"Could not add page to Reading List. Maybe it’s already there?" = "Адаҟьа аԥхьара ахьӡынҵахь ацҵара алымшеит. Иҟалап лара уа дыҟазар?"; + +/* Error message that is shown in settings when there was a problem loading */ +"Could not load page." = "Адаҟьа аҭагалара алымшеит."; + +/* Description for the new ETP mode i.e. standard vs strict */ +"CoverSheet.v24.ETP.Description" = "Иагәылоу иаԥхьагылоу ашьҭаԥшратә хьчара аӡыргақәа ушьҭаҵара аԥырҟәҟәаара иацхраауеит. Иаарттәуп Ахьчара ӷәӷәа еиҳаны ашьҭаҵаҩцәеи, аӡыргареи, ицәырҵуа аҿаԥшыгақәеи раанкыларазы."; + +/* Text for the new ETP settings button */ +"CoverSheet.v24.ETP.Settings.Button" = "Аиқәыршәарақәа рахь ацара"; + +/* Title for the new ETP mode i.e. standard vs strict */ +"CoverSheet.v24.ETP.Title" = "Аӡырга ашьҭаҵара ахьчара"; + +/* See http://mzl.la/1Qtkf0j */ +"Create an account" = "Ахыԥхьаӡара аԥҵара"; + +/* Dark theme setting in Reading View settings */ +"Dark" = "Аиқәаҵәа"; + +/* Accessibility label for button decreasing font size in display settings of reader mode */ +"Decrease text size" = "Ашрифт ашәагаа армаҷтәуп"; + +/* Title for default search engine picker. */ +"Default Search Engine" = "Аԥшаага машьына ишыҟоу еиԥш"; + +/* Accessibility label for action denoting closing default browser home tab banner. */ +"DefaultBrowserCloseButtonAccessibility.v102" = "Аркра"; + +/* Name for display settings button in reader mode. Display in the meaning of presentation, not monitor. */ +"Display Settings" = "Аарԥшра архиарақәа"; + +/* Used as a button label for crash dialog prompt */ +"Don’t Send" = "Иумышьҭын"; + +/* Done button on left side of the Settings view controller title bar */ +"Done" = "Амазеи"; + +/* Panel accessibility label */ +"Downloads" = "Аҭагаларақәа"; + +/* The label of the button the user will press to start downloading a file */ +"Downloads.Alert.DownloadNow" = "Иҭагалатәуп уажәы"; + +/* Button confirming the cancellation of the download. */ +"Downloads.CancelDialog.Cancel" = "Аԥыхра"; + +/* Alert dialog body when the user taps the cancel download icon. */ +"Downloads.CancelDialog.Message" = "Аиҭагара мап ацәкра уҭахума?"; + +/* Button declining the cancellation of the download. */ +"Downloads.CancelDialog.Resume" = "Анаркьаҿ"; + +/* Alert dialog title when the user taps the cancel download icon. */ +"Downloads.CancelDialog.Title" = "Аҭагалара аԥыхтәуп"; + +/* The label text in the Download Cancelled toast for showing confirmation that the download was cancelled. */ +"Downloads.Toast.Cancelled.LabelText" = "Аҭагалара мап ацәкуп"; + +/* The label text in the Download Failed toast for showing confirmation that the download has failed. */ +"Downloads.Toast.Failed.LabelText" = "Аҭагалара алымшеит"; + +/* The button to open a new tab with the Downloads home panel */ +"Downloads.Toast.GoToDownloads.Button" = "Аҭагаларақәа"; + +/* The description text in the Download progress toast for showing the number of files when multiple files are downloading. */ +"Downloads.Toast.MultipleFiles.DescriptionText" = "1 %d афаилқәа рҟынтә"; + +/* The description text in the Download progress toast for showing the number of files (1$) and download progress (2$). This string only consists of two placeholders for purposes of displaying two other strings side-by-side where 1$ is Downloads.Toast.MultipleFiles.DescriptionText and 2$ is Downloads.Toast.Progress.DescriptionText. This string should only consist of the two placeholders side-by-side separated by a single space and 1$ should come before 2$ everywhere except for right-to-left locales. */ +"Downloads.Toast.MultipleFilesAndProgress.DescriptionText" = "%1$@ %2$@"; + +/* The description text in the Download progress toast for showing the downloaded file size (1$) out of the total expected file size (2$). */ +"Downloads.Toast.Progress.DescriptionText" = "%1$@/%2$@"; + +/* Action button for deleting downloaded files in the Downloads panel. */ +"DownloadsPanel.Delete.Title" = "Аныхра"; + +/* Title for the Downloads Panel empty state. */ +"DownloadsPanel.EmptyState.Title" = "Иҭагалоу афаилқәа абра иааԥшуеит."; + +/* Action button for sharing downloaded files in the Downloads panel. */ +"DownloadsPanel.Share.Title" = "Ашара"; + +/* Text message in the settings table view */ +"Enter your password to connect" = "Аҽаԥшьгаразы иҭажәгал шәмаӡажәа"; + +/* Label for button to perform advanced actions on the error page */ +"ErrorPages.Advanced.Button" = "Уи адагьы"; + +/* Warning text when clicking the Advanced button on error pages */ +"ErrorPages.AdvancedWarning1.Text" = "Агәҽанҵара: Ари асаит ахь шәеимадара шшәарҭам ҳшьақәҳарӷәӷәар ҳалшом."; + +/* Additional warning text when clicking the Advanced button on error pages */ +"ErrorPages.AdvancedWarning2.Text" = "Ари зыхҟьо ииашам аконфигурациа ма ажәылаҩ иҟаиҵаз ажәылара акәзар ҟалоит. Уара уцалар улшоит, иҟалар зылшо ашәарҭара уақәшаҳаҭхар."; + +/* Warning text on the certificate error page */ +"ErrorPages.CertWarning.Description" = "%@ зтәу исаит ииашаны ирхиам. Удыррақәа аӷьычра иацәыхьчарц азы, Ecosia ари асаит иаҿамгылеит."; + +/* Title on the certificate error page */ +"ErrorPages.CertWarning.Title" = "Шәеимадара шәарҭам"; + +/* Label for button to go back from the error page */ +"ErrorPages.GoBack.Button" = "Шьҭахьҟа аара"; + +/* Button label to temporarily continue to the site from the certificate error page */ +"ErrorPages.VisitOnce.Button" = "Ишыҟазаалакгьы асаит аарттәуп"; + +/* Question shown to user when tapping a link that opens the App Store app */ +"ExternalLink.AppStore.ConfirmationTitle" = "Ари азхьарԥш App Store апрограммаҿы иаарттәума?"; + +/* Question shown to user when tapping an SMS or MailTo link that opens the external app for those. */ +"ExternalLink.AppStore.GenericConfirmationTitle" = "Ари азхьарԥш даҽа апликациак аҿы иаарттәума?"; + +/* Tile title for Facebook */ +"Facebook" = "Феисбук"; + +/* Title for firefox about:home page in tab history list */ +"Firefox.HomePage.Title" = "Ecosia ахатә даҟьа"; + +/* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ +"FirefoxHome.CustomizeHomeButton.Title" = "Ухатә даҟьа рхиала"; + +/* At the end of the Pocket section on the Firefox Homepage, this button appears and indicates tapping it will navigate the user to more Pocket Stories. */ +"FirefoxHome.Pocket.DiscoverMore" = "Еиҳаны аилкаара"; + +/* On each Pocket Stories on the Firefox Homepage, this label appears and indicates the number of minutes to read an article. Minutes should be abbreviated due to space constraints. %d represents the number of minutes */ +"FirefoxHome.Pocket.Minutes.v99" = "%d амин"; + +/* This is the title of the Pocket section on Firefox Homepage. */ +"FirefoxHome.Pocket.SectionTitle" = "Узырхәыцша ажәабжьқәа"; + +/* When a user taps and holds on an item from the Recently Visited section, this label will appear indicating the option to remove that item. */ +"FirefoxHome.RecentHistory.Remove" = "Аныхра"; + +/* Label showing how many pages there is in a search group. %d represents a number */ +"FirefoxHomepage.Common.PagesCount.v112" = "Адаҟьақәа: %d"; + +/* The title for the Settings context menu action for sponsored tiles in the Firefox home page shortcuts section. Clicking this brings the users to the Shortcuts Settings. */ +"FirefoxHomepage.ContextualMenu.Settings.v101" = "Архиарақәа"; + +/* The title for the Sponsored Content context menu action for sponsored tiles in the Firefox home page shortcuts section. Clicking this brings the users to a support page where users can learn more about Sponsored content and how it works. */ +"FirefoxHomepage.ContextualMenu.SponsoredContent.v101" = "Ҳара ҳспонсорцәеи шәмаӡареи"; + +/* Accessibility action title to open the synced tab for tab pickup on the Firefox homepage in the Jump Back In section. */ +"FirefoxHomepage.JumpBackIn.TabPickup.OpenTab.A11y.v106" = "Даҽа хархәагак аҟынтәи абларҭа аарттәуп"; + +/* Button title shown for tab pickup on the Firefox homepage in the Jump Back In section. */ +"FirefoxHomepage.JumpBackIn.TabPickup.ShowAll.ButtonTitle.v104" = "Егьырҭ аиқәыршәагақәа рҟынтә абларҭақәа зегьы"; + +/* If a user is signed in, and a sync has been performed to collect their recent tabs from other signed in devices, their most recent tab from another device can now appear in the Jump Back In section. This label specifically points out which cell inside the Jump Back In section shows that synced tab. */ +"FirefoxHomepage.JumpBackIn.TabPickup.v104" = "Абларҭа алхра"; + +/* This string will show under the description on pocket story, indicating that the story is sponsored. */ +"FirefoxHomepage.Pocket.Sponsored.v103" = "Аспонсортә"; + +/* This string will show under a shortcuts tile on the firefox home page, indicating that the tile is a sponsored tile. Space is limited, please keep as short as possible. */ +"FirefoxHomepage.Shortcuts.Sponsored.v100" = "Аспонсортә"; + +/* Accessibility Label for the tab toolbar Forward button */ +"Forward" = "Аԥхьаҟа"; + +/* Settings section title for Firefox Account */ +"FxA.FirefoxAccount" = "Ecosia ахыԥхьаӡара"; + +/* Button label to go to Firefox Account settings */ +"FxA.ManageAccount" = "Ахыԥхьаӡареи ахархәагақәеи рнапхгара"; + +/* Label when no internet is present */ +"FxA.NoInternetConnection" = "Аинтернет аимадара ыҟам"; + +/* FxA sign in view subtitle */ +"fxa.signin.camera-signin" = "Укамера ухы иархәаны аҭалара"; + +/* FxA sign in view qr code instructions */ +"fxa.signin.qr-link-instruction" = "Укомпиутер аҿы Ecosia аус аруны, Ecosia.com/pair ахь уца"; + +/* FxA sign in view qr code scan button */ +"fxa.signin.ready-to-scan" = "Асканер QR акод"; + +/* FxA sign in view email login button */ +"fxa.signin.use-email-instead" = "Ае-маил ахархәара"; + +/* Button label to Sync your Firefox Account */ +"FxA.SyncNow" = "Асинхронизациа"; + +/* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ +"FxAPush_DeviceConnected_body" = "Ecosia Асинхронизациа амаҵзура %@ иаҿакуп"; + +/* Title of a notification displayed when another device has connected to FxA. */ +"FxAPush_DeviceConnected_title" = "Асинхронизациа еидҳәалоуп"; + +/* Body of a notification displayed when named device has been disconnected from FxA. %@ refers to the name of the disconnected device. */ +"FxAPush_DeviceDisconnected_body" = "%@ гәаӷьрала иалхын."; + +/* Body of a notification displayed when this device has been disconnected from FxA by another device. */ +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ари аиқәыршәага Ecosia Sync амаҵзура иацрыхуп."; + +/* Title of a notification displayed when this device has been disconnected by another device. */ +"FxAPush_DeviceDisconnected_ThisDevice_title" = "Асинхронизациа еиҟәгоуп"; + +/* Title of a notification displayed when named device has been disconnected from FxA. */ +"FxAPush_DeviceDisconnected_title" = "Асинхронизациа еиҟәгоуп"; + +/* Body of a notification displayed when unnamed device has been disconnected from FxA. */ +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Аиқәыршәага Ecosia Sync амаҵзура иаҟәыҵит"; + +/* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia логотип, аҭӡамц аԥсахра."; + +/* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ +"Help" = "Азхьарԥш"; + +/* Toggle history syncing setting */ +"History" = "Ажурнал"; + +/* Title for button in the history panel to clear recent history */ +"HistoryPanel.ClearHistoryButtonTitle" = "Иаԥыхтәуп аҵыхәтәантәи аҭоурых…"; + +/* Option title to clear all browsing history. */ +"HistoryPanel.ClearHistoryMenuOptionEverything" = "Зегьы"; + +/* Button to perform action to clear history for the last hour */ +"HistoryPanel.ClearHistoryMenuOptionTheLastHour" = "Аҵыхәтәантәи асааҭ азы"; + +/* Button to perform action to clear history for today only */ +"HistoryPanel.ClearHistoryMenuOptionToday" = "Иахьазы"; + +/* Button to perform action to clear history for yesterday and today */ +"HistoryPanel.ClearHistoryMenuOptionTodayAndYesterday" = "Иахьеи иацтәи амши рзы"; + +/* Title for the History Panel empty state. */ +"HistoryPanel.EmptyState.Title" = "Ааигәа шәызҭааз асаитқәа абра иааԥшуеит."; + +/* Description for the empty synced tabs null state in the History Panel */ +"HistoryPanel.EmptySyncedTabsNullState.Description" = "Абра даҽа ҳәагақәак рҟынтә шәҭаӡҩырақәа ааԥшуеит."; + +/* Description for the empty synced tabs 'not signed in' state in the History Panel */ +"HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Аҭалара даҽа ҳәагақәак рҟынтәи аҭаӡқәа рсиа абаразы."; + +/* Title for the empty synced tabs state in the History Panel */ +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia асинхронизациа"; + +/* Title for the Back to History button in the History Panel */ +"HistoryPanel.HistoryBackButton.Title" = "Ахҭыс"; + +/* Title for the Recently Closed button in the History Panel */ +"HistoryPanel.RecentlyClosedTabsButton.Title" = "Ааигәа иаркыз"; + +/* Accessibility label for the tab toolbar indicating the Home button. */ +"Home" = "Аҩны"; + +/* The title for the Bookmark context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.Bookmark" = "Адыргақәа"; + +/* The title for the Delete from History context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.DeleteFromHistory" = "Аҭоурых аҟынтә ианыхтәуп"; + +/* The title for the Open in New Private Tab context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.OpenInNewPrivateTab.v101" = "Ихатәу абларҭаҿы иаарттәуп"; + +/* The title for the Open in New Tab context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.OpenInNewTab" = "Агәылаҵа ҿыц аҿы иаарттәуп"; + +/* The title for the Remove context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.Remove" = "Аныхра"; + +/* The title for the Remove Bookmark context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.RemoveBookmark" = "Ианыхтәуп анҵамҭа"; + +/* The title for the Share context menu action for sites in Home Panels */ +"HomePanel.ContextMenu.Share" = "Ашара"; + +/* A label indicating the keyboard shortcut to navigate backwards, through session history, inside the current tab. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.Back.DiscoveryTitle" = "Шьҭахьҟа аара"; + +/* A label indicating the keyboard shortcut of closing the current tab a user is in. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.CloseTab.DiscoveryTitle" = "Абларҭа арктәуп"; + +/* A label indicating the keyboard shortcut of finding text a user desires within a page. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.Find.DiscoveryTitle" = "Аԥшаара"; + +/* A label indicating the keyboard shortcut of switching to a subsequent tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.Forward.DiscoveryTitle" = "Аԥхьаҟа"; + +/* A label indicating the keyboard shortcut of creating a new private tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.NewPrivateTab.DiscoveryTitle" = "Ахатә абӷьыц ҿыц"; + +/* A label indicating the keyboard shortcut of creating a new tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.NewTab.DiscoveryTitle" = "Абларҭа ҿыц"; + +/* A label indicating the keyboard shortcut of switching from Private Browsing to Normal Browsing Mode. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.NormalMode.DiscoveryTitle" = "Анормалтә хәаԥшышьа арежим"; + +/* A label indicating the keyboard shortcut of switching from Normal Browsing mode to Private Browsing Mode. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.PrivateMode.DiscoveryTitle" = "Ахатә алаԥшхгара арежим"; + +/* A label indicating the keyboard shortcut of reloading the current page. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.Reload.DiscoveryTitle" = "Адаҟьа ҿыц аҭагалара"; + +/* A label indicating the keyboard shortcut of directly accessing the URL, location, bar. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.SelectLocationBar.DiscoveryTitle" = "Иалхтәуп адресқәа рцәаҳәа"; + +/* A label indicating the keyboard shortcut of switching to a subsequent tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.ShowNextTab.DiscoveryTitle" = "Анаҩстәи абларҭа аарԥштәуп"; + +/* A label indicating the keyboard shortcut of switching to a tab immediately preceding to the currently selected tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Hotkeys.ShowPreviousTab.DiscoveryTitle" = "Иаԥхьанеиуа абларҭа аарԥштәуп"; + +/* In the Tabs Tray, in the Inactive Tabs section, this is the button the user must tap in order to close all inactive tabs. */ +"InactiveTabs.TabTray.CloseButtonTitle" = "Иактивым абларҭақәа зегьы арктәуп"; + +/* Accessibility label for button increasing font size in display settings of reader mode */ +"Increase text size" = "Ашрифт ашәагаа иацҵатәуп"; + +/* Relative time for a tab that was visited within the last few moments. */ +"just now" = "абыржәыҵәҟьа"; + +/* A label indicating the keyboard shortcut of resetting a web page's view to the standard viewing size. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ActualSize" = "Аоригиналтә шәагаа"; + +/* A label indicating the keyboard shortcut of adding the currently viewing web page as a bookmark. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.AddBookmark" = "Адырга ацҵатәуп"; + +/* A label indicating the keyboard shortcut of clearing recent history. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ClearRecentHistory" = "Аҵыхәтәантәи аҭоурых аԥыхра"; + +/* A label indicating the keyboard shortcut of finding text a user desires within a page again. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.FindAgain" = "Анаҩстәи аԥшаара"; + +/* A label indicating the keyboard shortcut to reload a tab without it's cache. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. */ +"Keyboard.Shortcuts.RefreshWithoutCache.v108" = "Акәаш ҳасаб амҭакәа ҿыц аҭагалара"; + +/* A label indicating a grouping of related keyboard shortcuts describing actions a user can do with Bookmarks. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.Section.Bookmark" = "Адыргақәа"; + +/* A label indicating a grouping of related keyboard shortcuts describing actions a user can do with History. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.Section.History" = "Ахҭыс"; + +/* A label indicating a grouping of related keyboard shortcuts describing actions a user can do with locally saved items. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.Section.Tools" = "Аинструментқәа"; + +/* A label indicating a grouping of related keyboard shortcuts describing actions a user can take when navigating between their availale set of tabs. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.Section.Window" = "Аԥенџьыр"; + +/* A label indicating the keyboard shortcut of opening the application's settings menu. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.Settings" = "Архиарақәа"; + +/* A label indicating the keyboard shortcut of showing all bookmarks. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ShowBookmarks" = "Адыргақәа зегьы аарԥштәуп"; + +/* A label indcating the keyboard shortcut of showing all downloads. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ShowDownloads" = "Иҭагалаз зегьы аарԥштәуп"; + +/* A label indicating the keyboard shortcut to switch from the current tab to the first tab. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ShowFirstTab" = "Актәи абларҭа аарԥштәуп"; + +/* A label indicating the keyboard shortcut of showing all history. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ShowHistory" = "Аҭоурых арбатәуп"; + +/* A label indicating the keyboard shortcut switch from your current tab to the last tab. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ShowLastTab" = "Аҵыхәтәантәи абларҭа аарԥштәуп"; + +/* A label indicating the keyboard shortcut of enlarging the view of the current web page. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ZoomIn" = "Ацҵара"; + +/* A label indicating the keyboard shortcut of shrinking the view of the current web page. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Keyboard.Shortcuts.ZoomOut" = "Агырхара"; + +/* This label is meant to signify the section containing a group of items from the past thirty days. */ +"Last month" = "Иҳаҩсыз амзазы"; + +/* This label is meant to signify the section containing a group of items from the past seven days. */ +"Last week" = "Иаҳхысыз амчыбжь"; + +/* Panel accessibility label */ +"LibraryPanel.Accessibility.Bookmarks.v106" = "Адыргақәа"; + +/* Panel accessibility label */ +"LibraryPanel.Accessibility.History.v106" = "Ахҭыс"; + +/* Within the History Panel, users can delete search group sites history. They can cancel this action by pressing a cancel button. */ +"LibraryPanel.History.ClearGroupedTabsCancel.v100" = "Аԥыхра"; + +/* Within the History Panel, users can delete search group sites history. They need to confirm the action by pressing the delete button. */ +"LibraryPanel.History.ClearGroupedTabsDelete.v100" = "Аныхра"; + +/* Within the History Panel, users can delete search group sites history. %@ represents the search group name. */ +"LibraryPanel.History.ClearGroupedTabsTitle.v100" = "%@ аҟны иҟоу асаитқәа зегьы ԥыхтәума?"; + +/* Within the History Panel, users can open an action menu to clear recent history. */ +"LibraryPanel.History.ClearHistoryMenuTitle.v100" = "Ирыцқьоит аҭоурых (егьырҭ ахархәагақәа рҟынтәи аҭоурыхгьы уахь иналаҵаны), акукиқәеи егьырҭ аԥшааратә данноқәеи."; + +/* Within the history panel, a user can navigate into a screen with only grouped history items. Within that screen, a user can now swipe to delete a single item in the list. This label informs the user of a deletion action on the item. */ +"LibraryPanel.History.DeleteGroupedItem.v104" = "Аныхра"; + +/* In the history panel, users will be able to search terms in their browsing history. This label is shown when there is no results after querying the search terms in the user's history. */ +"LibraryPanel.History.NoHistoryFound.v99" = "Аҭоурых ԥшаам"; + +/* In the history panel, this is the title on the button that navigates the user to a screen showing their recently closed tabs. */ +"LibraryPanel.History.RecentlyClosedTabs.v99" = "Ааигәа иаркыз абларҭақәа"; + +/* In the history panel, users will be able to search terms in their browsing history. This placeholder text inside the search component will indicate that a user can search through their browsing history. */ +"LibraryPanel.History.SearchHistoryPlaceholder.v99" = "Иҭажәгал шәыԥшааратә термин"; + +/* Within the History Panel, users can see the option of viewing their history from synced tabs. */ +"LibraryPanel.History.SyncedHistory.v100" = "Егьырҭ ахархәагақәа рҟынтәи аҭоурых"; + +/* This label is meant to signify the section containing a group of items that are older than thirty days. */ +"LibraryPanel.Section.Older" = "Еиҳа ижәытәу"; + +/* Settings item that opens a tab containing the licenses. See http://mzl.la/1NSAWCG */ +"Licenses" = "Алицензиақәа"; + +/* Light theme setting in Reading View settings */ +"Light" = "Алашара"; + +/* Link for going to the non-reader page when the reader view could not be loaded. This message will appear only when sharing to Firefox reader mode from another app. */ +"Load original page" = "Ахыҵхырҭа адаҟьа аҭагалара"; + +/* Message displayed when the reader mode page is loading. This message will appear only when sharing to Firefox reader mode from another app. */ +"Loading content…" = "Аҵакы аҭагалара…"; + +/* Authentication prompt log in button */ +"Log in" = "Аҭалара"; + +/* Toggle logins syncing setting */ +"Logins" = "Аҭаларақәа"; + +/* Title of the Learn More button that links to a support page about device passcode requirements. */ +"Logins.DevicePasscodeRequired.LearnMoreButtonTitle" = "Еиҳаны аинформациа"; + +/* Title of the Continue button. */ +"Logins.Onboarding.ContinueButtonTitle" = "Ацҵара"; + +/* Title of the Learn More button that links to a support page about device passcode requirements. */ +"Logins.Onboarding.LearnMoreButtonTitle" = "Еиҳаны аинформациа"; + +/* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ +"Logins.PasscodeRequirement.Warning" = "Ecosia азы Автоҭарҭәара аҟазшьа ахархәаразы, шәхыхәҭа аԥсыӡкод аус аузар ауп."; + +/* Label displaying welcome view tagline under the title */ +"Logins.WelcomeView.Tagline" = "Шәпарольқәа шәыманы шәца"; + +/* Label displaying welcome view title */ +"Logins.WelcomeView.Title2" = "Ecosia амаӡажәақәа автоматла рхарҭәаара"; + +/* Title of the big blue button to enable AutoFill */ +"Logins.WelcomeView.TurnOnAutoFill" = "Автохарҭәаара аус арутәуп"; + +/* Button to not save the user's password */ +"LoginsHelper.DontSave.Button" = "Еиқәырхатәым"; + +/* Button to not update the user's password */ +"LoginsHelper.DontUpdate.Button" = "Арҿыцра ҟашәымҵан"; + +/* Prompt for saving a login. The first parameter is the username being saved. The second parameter is the hostname of the site. */ +"LoginsHelper.PromptSaveLogin.Title" = "Аҭалара еиқәырхатәуп %1$@ %2$@ азы?"; + +/* Prompt for saving a password with no username. The parameter is the hostname of the site. */ +"LoginsHelper.PromptSavePassword.Title" = "Аԥсыӡажәа %@ азы еиқәырхатәума?"; + +/* Prompt for updating a login. The first parameter is the hostname for which the password will be updated for. */ +"LoginsHelper.PromptUpdateLogin.Title.OneArg" = "Алогин арҿыцра %@ азы?"; + +/* Prompt for updating a login. The first parameter is the username for which the password will be updated for. The second parameter is the hostname of the site. */ +"LoginsHelper.PromptUpdateLogin.Title.TwoArg" = "%1$@ аҭалара арҿыцра %2$@ азы?"; + +/* Button to save the user's password */ +"LoginsHelper.SaveLogin.Button" = "Аҭалара еиқәырхатәуп"; + +/* Button to update the user's password */ +"LoginsHelper.Update.Button" = "Арҿыцра"; + +/* Placeholder test for search box in logins list view. */ +"LoginsList.LoginsListSearchPlaceholder" = "Араӡага"; + +/* Label that appears after the search if there are no logins matching the search */ +"LoginsList.NoMatchingResult.Subtitle" = "Узҵаара иақәшәо ​​аҭак ҳәа акгьы ҳԥыхьамшәеит"; + +/* Title for cancel button for user to stop searching for a particular login */ +"LoginsList.Search.Cancel" = "Аԥыхра"; + +/* Label displaying select a password to fill instruction */ +"LoginsList.SelectPassword.Title" = "Иалышәх ашәҟәыҭагаларазы ажәамаӡа"; + +/* Title for the list of logins */ +"LoginsList.Title" = "ИХЫРХЫЗ АЛОГИНҚӘА"; + +/* Restore Tabs Prompt Description */ +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia уаанӡа иԥхасҭахазшәоуп ишыҟоу. Уара уҭаӡҩырақәа еиҭашьақәургыларц уҭахума?"; + +/* Title for the button that marks a reading list item as read */ +"Mark as Read" = "Иаԥхьахьоушәа азгәаҭа"; + +/* Title for the button that marks a reading list item as unread */ +"Mark as Unread" = "Иаԥхьам ҳәа азгәаҭа"; + +/* Toast displayed to the user after a bookmark has been added. */ +"Menu.AddBookmark.Confirm" = "Аҭыӡдырга ацҵоуп"; + +/* Label for the add bookmark button in the menu. Pressing this button bookmarks the current page. Please keep the text as short as possible for this label. */ +"Menu.AddBookmark.Label.v99" = "Ацҵара"; + +/* Toast displayed to the user after adding the item to the Shortcuts. */ +"Menu.AddPin.Confirm2" = "Аҿацәқәа ирыцҵоуп"; + +/* Label for the add to reading list button in the menu. Pressing this button adds the current page to the reading list. Please keep the text as short as possible for this label. */ +"Menu.AddReadingList.Label.v99" = "Ацҵара"; + +/* Toast displayed to the user after adding the item to their reading list. */ +"Menu.AddToReadingList.Confirm" = "Аԥхьара ахьӡынҵа иацҵоуп"; + +/* Label for the add to shortcuts button in the menu. Pressing this button pins the current website as a shortcut on the home page. */ +"Menu.AddToShortcuts.v99" = "Аҿацәқәа рахь иацҵатәуп"; + +/* Label for the button, displayed in the menu, takes you to bookmarks screen when pressed. */ +"Menu.Bookmarks.Label" = "Адыргақәа"; + +/* The title for the button that lets you copy the url from the location bar. */ +"Menu.Copy.Title" = "Адрес ақәҭыхра"; + +/* Toast displayed to user after copy url pressed. */ +"Menu.CopyURL.Confirm" = "URL ақәҭыхга таблицахь иқәҭыхуп"; + +/* Label for the customize homepage button in the menu page. Pressing this button takes users to the settings options, where they can customize the Firefox Home page */ +"Menu.CustomizeHomePage.v99" = "Ухатә даҟьа рхиала"; + +/* Label for the button, displayed in the menu, takes you to Downloads screen when pressed. */ +"Menu.Downloads.Label" = "Аҭагаларақәа"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtectionOff.Title" = "Ари асаит ашәарҭадара аҿыхуп"; + +/* A switch to enable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtectionOn.Title" = "Ари асаит азы ашәарҭадара аус арууп"; + +/* Label for the help button in the menu page. Pressing this button opens the support page https://support.mozilla.org/en-US/products/ios */ +"Menu.Help.v99" = "Ацхыраара"; + +/* Label for the button, displayed in the menu, takes you to History screen when pressed. */ +"Menu.History.Label" = "Ахҭыс"; + +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "Ахатә абӷьыц ҿыц"; + +/* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ +"Menu.NewTab.v99" = "Абларҭа ҿыц"; + +/* Label for the button, displayed in the menu, turns off night mode. */ +"Menu.NightModeTurnOff.Label2" = "Уахынлатәи арежим аҿыхтәуп"; + +/* Label for the button, displayed in the menu, turns on night mode. */ +"Menu.NightModeTurnOn.Label2" = "Уахынлатәи арежим аус арутәуп"; + +/* Label for the button, displayed in the menu, takes you to passwords screen when pressed. */ +"Menu.Passwords.Label" = "Амаӡажәақәа"; + +/* The title for the button that lets you paste into the location bar */ +"Menu.Paste.Title" = "Алаҵара"; + +/* The title for the button that lets you paste and go to a URL */ +"Menu.PasteAndGo.Title" = "Иҷабланы уца"; + +/* Label for the button, displayed in the menu, takes you to Reading List screen when pressed. */ +"Menu.ReadingList.Label" = "Аԥхьара ахьӡынҵа"; + +/* Label for the button, displayed in the menu, used to reload the current website without Tracking Protection */ +"Menu.ReloadWithoutTrackingProtection.Title" = "Ашьҭаҵара ахьчарада ҿыц аҭагалара"; + +/* Label for the button, displayed in the menu, used to reload the current website with Tracking Protection enabled */ +"Menu.ReloadWithTrackingProtection.Title" = "Ашьҭаҵара ахьчарала ҿыц аҭагалара"; + +/* Toast displayed to the user after a bookmark has been removed. */ +"Menu.RemoveBookmark.Confirm" = "Аҭыжьымҭақәа аныхуп"; + +/* Label for the remove bookmark button in the menu. Pressing this button remove the current page from the bookmarks. Please keep the text as short as possible for this label. */ +"Menu.RemoveBookmark.Label.v99" = "Аныхра"; + +/* Label for the remove from shortcuts button in the menu. Pressing this button removes the current website from the shortcut pins on the home page. */ +"Menu.RemovedFromShortcuts.v99" = "Аҿацәқәа рҟынтә иаԥыхтәуп"; + +/* Toast displayed to the user after removing the item to the Shortcuts. */ +"Menu.RemovePin.Confirm2.v99" = "Аҿацәқәа рҟынтә иаԥыхуп"; + +/* Toast displayed to confirm to the user that his reading list item was correctly removed. */ +"Menu.RemoveReadingList.Confirm.v99" = "Аԥхьара ахьӡынҵа аҟынтәи иаԥыхуп"; + +/* Label for the remove from reading list button in the menu. Pressing this button removes the current page from the reading list. Please keep the text as short as possible for this label. */ +"Menu.RemoveReadingList.Label.v99" = "Аныхра"; + +/* Label for the share button in the menu page. Pressing this button open the share menu to share the current website. */ +"Menu.Share.v99" = "Ашара"; + +/* Label for the Firefox Sync button in the menu page. Pressing this button open the sign in to Firefox page service to sync and save data. */ +"Menu.SyncAndSaveData.v103" = "Адыррақәа рсинхронизациеи рыхьчареи"; + +/* Toast displayed to the user after a tab has been sent successfully. */ +"Menu.TabSent.Confirm" = "Абларҭа ашьҭуп"; + +/* Accessibility label for the Bookmark button on the toolbar. Pressing this button opens the bookmarks menu */ +"Menu.Toolbar.Bookmarks.AccessibilityLabel.v99" = "Адыргақәа"; + +/* Accessibility label for the Home button on the toolbar. Pressing this button brings the user to the home page. */ +"Menu.Toolbar.Home.AccessibilityLabel.v99" = "Аҩны"; + +/* String to let users know the site verifier, where the placeholder represents the SSL certificate signer. */ +"Menu.TrackingProtection.Details.Verifier" = "Ишьақәырӷәӷәоуп: %@"; + +/* The title for tracking protection settings */ +"Menu.TrackingProtection.ProtectionSettings.Title" = "Ашәарҭадара архиарақәа"; + +/* The title that shows the number of content cookies blocked */ +"Menu.TrackingProtectionBlockedContent.Title" = "Аҵакы ашьҭаҵара"; + +/* The title that shows the number of social URLs blocked */ +"Menu.TrackingProtectionBlockedSocial.Title" = "Асоциалтә медиа ашьҭаҵаҩцәа"; + +/* The title that shows the number of cross-site URLs blocked */ +"Menu.TrackingProtectionCrossSiteTrackers.Title" = "Асаитқәа рыбжьаратәи ашьҭаҵагақәа"; + +/* The title that shows the number of cryptomining scripts blocked */ +"Menu.TrackingProtectionCryptominersBlocked.Title" = "Акриптоминерцәа"; + +/* Description of content trackers. */ +"Menu.TrackingProtectionDescription.ContentTrackers" = "Асаитқәа рҿы адәныҟатәи аӡыргақәа, авидео, насгьы иҵәаху ашьҭаҵагақәа зҵоу егьырҭ аконтентқәа рҭагалара ауеит. Абас еиԥш иҟоу аҵакы аблокада асаитқәа еиҳа ирласны рҭагалара иацхраауеит, аха џьоукы агәыҵәҟьақәа, аформақәа, насгьы алогин аҭыԥқәа аус руамзар ауеит."; + +/* Description of cross-site trackers. */ +"Menu.TrackingProtectionDescription.CrossSiteNew" = "Арҭ акукиқәа шәара ишәышьҭоуп еиуеиԥшым асаитқәа рҿы, интернетла иҟашәҵо иазку адыррақәа реизгаразы. Урҭ шьақәдыргылоит ахԥатәи аганқәа, иаҳҳәап, аӡыргаҩцәеи аналитикатә компаниақәеи."; + +/* Description of cryptominers. */ +"Menu.TrackingProtectionDescription.CryptominersNew" = "Акриптомаинерцәа маӡала рхы иадырхәоит шәсистема аҳасабратә мчра адиджиталтә ԥарақәа рыԥшааразы. Арҭ аскриптқәа убатареиа рцәырыргоит, ухы иаурхәо аус армаҷуеит, насгьы амчхаразы аԥара узҭо еиҳар алшоит."; + +/* Description of fingerprinters. */ +"Menu.TrackingProtectionDescription.Fingerprinters" = "Убраузери укомпиутери рырхиарақәа зеиԥшыҟам. Анацәашьҭақәа рхы иадырхәоит абарҭ иҷыдоу апараметрқәа реизга, уара упрофиль аԥҵаразы, уи ахархәара алшоит ауеб аҿы уахьнеиуа ушьҭаҵаразы."; + +/* Description of social network trackers. */ +"Menu.TrackingProtectionDescription.SocialNetworksNew" = "Асоциалтә еимадарақәа егьырҭ асаитқәа рҿы ашьҭаҵагақәа шьақәдыргылоит, еиҳа инарҭбаау, хықәкылатәи упрофиль аԥҵаразы. Абарҭ ашьҭаҵагақәа раанкылара онлаинтәи уусқәа ирызкны еизырго адыррақәа рхыԥхьаӡара армаҷуеит."; + +/* The title that shows the number of fingerprinting scripts blocked */ +"Menu.TrackingProtectionFingerprintersBlocked.Title" = "Адиджиталтә нацәашьҭақәа реизгагақәа"; + +/* The title for the option to view the What's new page. */ +"Menu.WhatsNew.Title" = "Иҿыцу"; + +/* Label for the zoom page button in the menu, used to show the Zoom Page bar. The placeholder shows the current zoom level in percent. */ +"Menu.ZoomPage.Title.v113" = "Амҽхакрбага (%@)"; + +/* Accessibility label for Mobile Device image in remote tabs list */ +"mobile device" = "Еиҭаҵуа ахархәага"; + +/* Relative date for dates older than a month and less than two months. */ +"more than a month ago" = "Мызкы инеиҳаны уажәаԥхьа"; + +/* Description for a date more than a week ago, but less than a month ago. */ +"more than a week ago" = "Мчыбжьык инеиҳаны ҵуеит"; + +/* Accessibility label for the navigation toolbar displayed at the bottom of the screen. */ +"Navigation Toolbar" = "Анавигациа апанель"; + +/* Accessibility label for the New Tab button in the tab toolbar. */ +"New Tab" = "Абларҭа ҿыц"; + +/* Restore Tabs Negative Action */ +"No" = "Мамоу"; + +/* Message spoken by VoiceOver to indicate that there are no tabs in the Tabs Tray */ +"No tabs" = "Агәылаҵақәа ыҟам"; + +/* OK button */ +"OK" = "ИБЗИОУП"; + +/* Restore Tabs Affirmative Action */ +"Okay" = "Ибзиаӡоуп"; + +/* On the onboarding card letting users know what's new in this version of Firefox, this is the title for the button, on the bottom of the card, used to get back to browsing on Firefox by dismissing the onboarding card */ +"Onboarding.WhatsNew.Button.Title" = "Аинтернет аҭаара алагара"; + +/* Title for prompt displayed to user after the app crashes */ +"Oops! Firefox crashed" = "Оҳ! Ecosia еилабгеит"; + +/* See http://mzl.la/1LXbDOL */ +"Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Астатиақәа аԥхьаҩцәа рҭеиҭыԥш аҿы иаарттәуп, адресқәа рбар аҿы ашәҟәы аҵакы ақәыӷәӷәарала."; + +/* See http://mzl.la/1G7uHo7 */ +"Open Settings" = "Аиқәыршәарақәа аарттәуп"; + +/* Toggle tabs syncing setting */ +"Open Tabs" = "Иаарттәуп абларҭақәа"; + +/* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ +"OpenURL.Error.Message" = "Ecosia адаҟьа аартра ауам, избан акәзар уи адрес иашам."; + +/* Title of the message shown when the user attempts to navigate to an invalid link. */ +"OpenURL.Error.Title" = "Адаҟьа аартра алымшеит"; + +/* Password textbox in Authentication prompt */ +"Password" = "Апароль"; + +/* See http://mzl.la/1G7uHo7 */ +"PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Уи асахьа укамератә роль аҿы иҵәахуеит."; + +/* See http://mzl.la/1G7uHo7 */ +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia иаҭахуп упатреҭқәа рахь анеира"; + +/* Button for closing the menu action sheet */ +"PhotonMenu.close" = "Аркра"; + +/* Privacy section title */ +"Privacy" = "Амаӡара"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the settings. See https://www.mozilla.org/privacy/firefox/ */ +"Privacy Policy" = "Амаӡаратә политика"; + +/* This is the value for a label that indicates if a user is on an unencrypted website. */ +"ProtectionStatus.NotSecure" = "Аимадара шәарҭам"; + +/* This is the value for a label that indicates if a user is on a secure https connection. */ +"ProtectionStatus.Secure" = "Аимадара шәарҭам"; + +/* Title for quick-search engines settings section. */ +"Quick-Search Engines" = "Ирццаку аԥшааратә системақәа"; + +/* A label indicating the action that a user can rate the Firefox app in the App store. */ +"Ratings.Settings.RateOnAppStore" = "Ахәшьара App Store аҟны"; + +/* Accessibility label for read article in reading list. It's a past participle - functions as an adjective. */ +"read" = "аԥхьара"; + +/* Accessibility label for the Reader View button */ +"Reader View" = "Аԥхьара аԥшра"; + +/* Accessibility message e.g. spoken by VoiceOver when Reader Mode becomes available. */ +"ReaderMode.Available.VoiceOverAnnouncement" = "Аԥхьара арежим ыҟоуп"; + +/* Name for Mark as read button in reader mode */ +"ReaderModeBar.MarkAsRead.v106" = "Иаԥхьазшәа азгәаҭа"; + +/* Name for Mark as unread button in reader mode */ +"ReaderModeBar.MarkAsUnread.v106" = "Иаԥхьам ҳәа азгәаҭа"; + +/* Panel accessibility label */ +"Reading list" = "Аԥхьара ахьӡынҵа"; + +/* Title for the Recently Closed Tabs Panel */ +"RecentlyClosedTabsPanel.Title" = "Ааигәа иаркыз"; + +/* More button text for Recently Saved items at the home page. */ +"RecentlySaved.Actions.More" = "Зегьы аарԥштәуп"; + +/* Accessibility Label for the tab toolbar Reload button */ +"Reload" = "Арҿыцра"; + +/* Accessibility label for the reload button */ +"Reload page" = "Адаҟьа ҿыц аҭагалара"; + +/* Title for the button that removes a reading list item */ +"Remove" = "Аныхра"; + +/* Name for button removing current article from reading list in reader mode */ +"Remove from Reading List" = "Аԥхьара ахьӡынҵа аҟынтәи ианыхтәуп"; + +/* Cancel button text shown in reopen-alert at home page. */ +"ReopenAlert.Actions.Cancel" = "Аԥыхра"; + +/* Reopen button text shown in reopen-alert at home page. */ +"ReopenAlert.Actions.Reopen" = "Арҿыцра"; + +/* Reopen alert title shown at home page. */ +"ReopenAlert.Title" = "Аҵыхәтәантәи иарку абларҭа еиҭашьақәыргылатәуп"; + +/* Accessibility label for button resetting font size in display settings of reader mode */ +"Reset text size" = "Ашрифт ашәагаа еиҭашьақәыргылатәуп"; + +/* Font type setting in the reading view settings */ +"Sans-serif" = "Асерифда"; + +/* See http://mzl.la/1LXbDOL */ +"Save pages to your Reading List by tapping the book plus icon in the Reader View controls." = "Аԥхьаратә рсиаҿы адаҟьақәа еиқәырхатәуп, аԥхьаратә ҭагылазаашьа аконтроль аҟны ашәҟәы ацҵаны аҵакы ақәыӷәӷәарала."; + +/* OK button to dismiss the error prompt. */ +"ScanQRCode.Error.OK.Button" = "ИБЗИОУП"; + +/* Text for the instructions label, displayed in the QR scanner view */ +"ScanQRCode.Instructions.Label" = "Асканертә фреим аҩныҵҟа QR-код ҭажәгал"; + +/* Text of the prompt that is shown to the user when the data is invalid */ +"ScanQRCode.InvalidDataError.Message" = "Ииашам адыррақәа"; + +/* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ +"ScanQRCode.PermissionError.Message.v100" = "Шәхыхәҭа Аиқәыршәарақәа рахь шәца > Фаерфокс. Азин аҭ Ecosia укамера анеиразы."; + +/* Title for the QR code scanner view. */ +"ScanQRCode.View.Title" = "Асканер QR акод"; + +/* Open search section of settings */ +"Search" = "Аԥшаара"; + +/* The text shown in the URL bar on about:home */ +"Search or enter address" = "Иҭажәгал шәзыҳәара ма шәҭыӡҭыԥ"; + +/* Search suggestion cell label that allows user to switch to tab which they searched for in url bar */ +"Search.Awesomebar.SwitchToTab" = "Абларҭахь ииастәуп"; + +/* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ +"Search.SuggestSectionTitle.v102" = "Ecosia Адгалара"; + +/* The message that asks the user to Add the search provider explaining where the search engine will appear */ +"Search.ThirdPartyEngines.AddMessage" = "Аԥшаага ҿыц ааԥшуеит Ирццаку аԥшаара абар аҟны."; + +/* The success message that appears after a user sucessfully adds a new search engine */ +"Search.ThirdPartyEngines.AddSuccess" = "Аԥшаага машьына ацҵоуп!"; + +/* The title that asks the user to Add the search provider */ +"Search.ThirdPartyEngines.AddTitle" = "Аԥшаага ацҵара?"; + +/* The cancel button if you do not want to add a search engine. */ +"Search.ThirdPartyEngines.Cancel" = "Аԥыхра"; + +/* A message explaining fault in custom search engine form. */ +"Search.ThirdPartyEngines.DuplicateErrorMessage" = "Аԥшаага машьына ари ахы мамзаргьы URL ацҵоуп."; + +/* A title stating that we failed to add custom search engine. */ +"Search.ThirdPartyEngines.DuplicateErrorTitle" = "Аҭабгара"; + +/* A title explaining that we failed to add a search engine */ +"Search.ThirdPartyEngines.FailedMessage" = "Аԥшаага ацҵара алымшеит."; + +/* A title explaining that we failed to add a search engine */ +"Search.ThirdPartyEngines.FailedTitle" = "Аҭабгара"; + +/* A message explaining fault in custom search engine form. */ +"Search.ThirdPartyEngines.FormErrorMessage" = "Аҭыԥқәа зегьы ииашаны иҭәызароуп."; + +/* A title stating that we failed to add custom search engine. */ +"Search.ThirdPartyEngines.FormErrorTitle" = "Аҭабгара"; + +/* The confirmation button */ +"Search.ThirdPartyEngines.OK" = "ИБЗИОУП"; + +/* Message displayed in the crash dialog above the buttons used to select when sending reports */ +"Send a crash report so Mozilla can fix the problem?" = "Аԥхасҭа аҳасабырба ашьҭра, Мозилла апроблема аӡбара алнаршарц азы?"; + +/* Menu item in settings used to open input.mozilla.org where people can submit feedback */ +"Send Feedback" = "Агәаанагара ашьҭра"; + +/* Used as a button label for crash dialog prompt */ +"Send Report" = "Ацҳара ашьҭра"; + +/* Button title for cancelling share screen */ +"SendTo.Cancel.Button" = "Аԥыхра"; + +/* Close button in top navigation bar */ +"SendTo.Close.Button" = "Аркра"; + +/* Header for the list of devices table */ +"SendTo.DeviceList.Text" = "Иҟоу ахархәагақәа:"; + +/* Message in error prompt explaining why the URL is invalid. */ +"SendTo.Error.Message" = "HTTP-еи HTTPS-еи рызхьарԥшқәа мацара роуп еицаҳзеиԥшу."; + +/* OK button to dismiss the error prompt. */ +"SendTo.Error.OK.Button" = "ИБЗИОУП"; + +/* Title of error prompt displayed when an invalid URL is shared. */ +"SendTo.Error.Title" = "Ишәзеиҭашәҳәарц шәызҿу азхьарԥш еицаҳзеиҭашәҳәар ауам."; + +/* Title of the dialog that allows you to send a tab to a different device */ +"SendTo.NavBar.Title" = "Агәылаҵа ашьҭра"; + +/* Error message shown in the remote tabs panel */ +"SendTo.NoDevicesFound.Message" = "Ари Ecosia Ахыԥхьаӡара иацҵоу, насгьы асинхронизациазы иҟоу ахархәагақәа шәымам."; + +/* See http://mzl.la/1ISlXnU */ +"SendTo.NotSignedIn.Message" = "Ҳаҳәоит, Ecosia аартны, Аиқәыршәарақәа рахь шәцаны шәҭала ацҵаразы."; + +/* See http://mzl.la/1ISlXnU */ +"SendTo.NotSignedIn.Title" = "Уара Ecosia Ахыԥхьаӡара уҭамлаӡеит."; + +/* Navigation bar button to Send the current page to a device */ +"SendTo.SendAction.Text" = "Ашьҭра"; + +/* Body of notification shown when the device is sent one or more tabs from an unnamed device. */ +"SentTab_TabArrivingNotification_NoDevice_body" = "Даҽа ҳәагак аҟынтәи аҭаӡҩыра ҿыц ааит."; + +/* Title of notification shown when the device is sent one or more tabs from an unnamed device. */ +"SentTab_TabArrivingNotification_NoDevice_title" = "Иҳауз абларҭа"; + +/* Body of notification shown when the device is sent one or more tabs from the named device. %@ is the placeholder for the app name. */ +"SentTab_TabArrivingNotification_WithDevice_body" = "%@ абӷьыц ҿыц амоуп"; + +/* Title of notification shown when the device is sent one or more tabs from the named device. %@ is the placeholder for the device name. This device name will be localized by that device. */ +"SentTab_TabArrivingNotification_WithDevice_title" = "Аҭаӡҩыра аагоуп %@"; + +/* Body of notification received after a spurious message from FxA has been received. */ +"SentTab.NoTabArrivingNotification.body" = "Алагара азы шәақәыӷәӷәа"; + +/* Title of notification received after a spurious message from FxA has been received. */ +"SentTab.NoTabArrivingNotification.title" = "Ecosia асинхронизациа"; + +/* Label for an action used to view one or more tabs from a notification. */ +"SentTab.ViewAction.title" = "Асахьа"; + +/* Sepia theme setting in Reading View settings */ +"Sepia" = "Асепиа"; + +/* Font type setting in the reading view settings */ +"Serif" = "Асериф"; + +/* Title in the settings view controller title bar */ +"Settings" = "Архиарақәа"; + +/* The button text in Search Settings that opens the Custom Search Engine view. */ +"Settings.AddCustomEngine" = "Аԥшаага ацҵатәуп"; + +/* The text on the Save button when saving a custom search engine */ +"Settings.AddCustomEngine.SaveButtonText" = "Ахьчара"; + +/* The title of the Custom Search Engine view. */ +"Settings.AddCustomEngine.Title" = "Аԥшаага ацҵара"; + +/* The title for the field which sets the title for a custom search engine. */ +"Settings.AddCustomEngine.TitleLabel" = "Ахьӡ"; + +/* The placeholder for Title Field when saving a custom search engine. */ +"Settings.AddCustomEngine.TitlePlaceholder" = "Аԥшаагатә машьына"; + +/* The title for URL Field */ +"Settings.AddCustomEngine.URLLabel" = "URL"; + +/* The placeholder for URL Field when saving a custom search engine */ +"Settings.AddCustomEngine.URLPlaceholder" = "URL (аҳәара %s ала иԥсахтәуп)"; + +/* Title of setting to block opening external apps when pressing links. */ +"Settings.BlockOpeningExternalApps.Title" = "Адәныҟатәи апрограммақәа раартра аблок"; + +/* Button in Data Management that clears all items. */ +"Settings.ClearAllWebsiteData.Clear.Button" = "Асаит адыррақәа зегьы ԥыхтәуп"; + +/* Button in settings that clears private data for the selected items. */ +"Settings.ClearPrivateData.Clear.Button" = "Иаԥыхтәуп ахатә дыррақәа"; + +/* Label used as an item in Settings. When touched it will open a dialog prompting the user to make sure they want to clear all of their private data. */ +"Settings.ClearPrivateData.SectionName" = "Ахатә дыррақәа раныхра"; + +/* Button in Data Management that clears private data for the selected items. Parameter is the number of items to be cleared */ +"Settings.ClearSelectedWebsiteData.ClearSelected.Button" = "Ахәҭақәа аԥыхтәуп: %1$@"; + +/* Copy app version alert shown in settings. */ +"Settings.CopyAppVersion.Title" = "Аҭаӡҩыраҿы иқәҭыхуп"; + +/* Default text in search bar for Data Management */ +"Settings.DataManagement.SearchLabel" = "Асаит афильтр"; + +/* Label used as an item in Settings. When touched it will open a dialog prompting the user to make sure they want to clear all of their private data. */ +"Settings.DataManagement.SectionName" = "Адыррақәа рнапхгара"; + +/* Title displayed in header of the setting panel. */ +"Settings.DataManagement.Title" = "Адыррақәа рнапхгара"; + +/* Body of the alert when prompting the user asking to disconnect. */ +"Settings.Disconnect.Body" = "Ecosia ухыԥхьаӡара асинхронизациа ааннакылоит, аха уи аиқәыршәагаҿы увеб-ԥшааратә данноқәа зегьы аныхӡом."; + +/* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ +"Settings.Disconnect.Button" = "Аиҿыхра"; + +/* Cancel action button in alert when user is prompted for disconnect */ +"Settings.Disconnect.CancelButton" = "Аԥыхра"; + +/* Destructive action button in alert when user is prompted for disconnect */ +"Settings.Disconnect.DestructiveButton" = "Аиҿыхра"; + +/* Title of the alert when prompting the user asking to disconnect. */ +"Settings.Disconnect.Title" = "Аиҿыхра?"; + +/* Section header for brightness slider. */ +"Settings.DisplayTheme.BrightnessThreshold.SectionHeader" = "Аҩаӡара"; + +/* Display (theme) settings label to show if manually switch theme is enabled. */ +"Settings.DisplayTheme.Manual.StatusLabel" = "Напыла"; + +/* Display (theme) settings switch subtitle, explaining the title 'Manually'. */ +"Settings.DisplayTheme.Manual.SwitchSubtitle" = "Иалышәх шәтема"; + +/* Display (theme) setting to choose the theme manually. */ +"Settings.DisplayTheme.Manual.SwitchTitle" = "Напыла"; + +/* Option choice in display theme settings for dark theme */ +"Settings.DisplayTheme.OptionDark" = "Аиқәаҵәа"; + +/* Option choice in display theme settings for light theme */ +"Settings.DisplayTheme.OptionLight" = "Алашара"; + +/* Display (theme) settings footer describing how the brightness slider works. */ +"Settings.DisplayTheme.SectionFooter" = "Атема автоматла аҽаԥсахуеит аекран аҵыхәтәантәи алашара ашәахәа иабзоураны. Атема аҽанаԥсахуа аҳәаа шьақәургылар улшоит. Агьежьтә индикатор уажәтәи ашәахәа аҩаӡара унарбоит."; + +/* Display (theme) settings label to show if automatically switch theme is enabled. */ +"Settings.DisplayTheme.StatusTitle" = "Автоматла"; + +/* Switch mode settings section title */ +"Settings.DisplayTheme.SwitchMode.SectionHeader" = "Аиҭагаратә режим"; + +/* Display (theme) settings switch subtitle, explaining the title 'Automatically'. */ +"Settings.DisplayTheme.SwitchSubtitle" = "Аекран аҵыхәтәантәи алашара иахәаԥшны автоматла аҽеиҭакра"; + +/* Display (theme) settings switch to choose whether to set the dark mode manually, or automatically based on the brightness slider. */ +"Settings.DisplayTheme.SwitchTitle" = "Автоматла"; + +/* System theme settings section title */ +"Settings.DisplayTheme.SystemTheme.SectionHeader" = "Асистема атема"; + +/* System theme settings switch to choose whether to use the same theme as the system */ +"Settings.DisplayTheme.SystemTheme.SwitchTitle" = "Асистема лашьца/лашара атема ахархәара"; + +/* Theme picker settings section title */ +"Settings.DisplayTheme.ThemePicker.SectionHeader" = "Атема алхра"; + +/* Title in main app settings for Theme settings */ +"Settings.DisplayTheme.Title.v2" = "Амаҭәар"; + +/* Label used for the device name settings section. */ +"Settings.FxA.DeviceName" = "Аиқәыршәага ахьӡ"; + +/* Label used as a section title in the Firefox Accounts Settings screen. */ +"Settings.FxA.Sync.SectionName" = "Асинхронизациа архиарақәа"; + +/* Title displayed in header of the FxA settings panel. */ +"Settings.FxA.Title" = "Ecosia ахыԥхьаӡара"; + +/* General settings section title */ +"Settings.General.SectionName" = "Ихадоу"; + +/* This is the description below the settings section located in the menu under customize current homepage. It describes what the options in the section are for. */ +"Settings.Home.Current.Description.v101" = "Иалышәх шәхатә даҟьаны иаарԥшхо."; + +/* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ +"Settings.Home.Option.Description.v101" = "Иалышәх Ecosia ахатә даҟьаҿы иагәылаҵатәу."; + +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ +"Settings.Home.Option.JumpBackIn" = "Ухынҳәы"; + +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off */ +"Settings.Home.Option.Pocket" = "Иреиӷьуп Pocket"; + +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Recently Saved section on the Firefox homepage on or off */ +"Settings.Home.Option.RecentlySaved" = "Ааигәа иацҵоу"; + +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Recently Visited section on the Firfox homepage on or off */ +"Settings.Home.Option.RecentlyVisited" = "Ааигәа дахьнеиз"; + +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Shortcuts section on the Firefox homepage on or off */ +"Settings.Home.Option.Shortcuts" = "Аҿацәқәа"; + +/* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the Homepage after four hours of inactivity. */ +"Settings.Home.Option.StartAtHome.AfterFourHours" = "Ԥшь-сааҭк аусурадара ашьҭахь ахатә даҟьа"; + +/* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the Homepage every time they open up Firefox */ +"Settings.Home.Option.StartAtHome.Always" = "Ахатә даҟьа"; + +/* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ +"Settings.Home.Option.StartAtHome.Description" = "Иалышәх Ecosia ахь шәаныхынҳәуа ижәбо."; + +/* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ +"Settings.Home.Option.StartAtHome.Never" = "Аҵыхәтәантәи абларҭа"; + +/* Title for the section in the settings menu where users can configure the behaviour of the Start at Home feature on the Firefox Homepage. */ +"Settings.Home.Option.StartAtHome.Title" = "Ахатә екран"; + +/* In the settings menu, this is the title of the Firefox Homepage customization settings section */ +"Settings.Home.Option.Title.v101" = "Ахатә даҟьахь иацҵатәуп"; + +/* In the settings menu, on the Firefox homepage customization section, this is the title for the option that allows users to access the wallpaper settings for the application. */ +"Settings.Home.Option.Wallpaper" = "Аҭӡамц"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia аҭӡамц, аметисттә хсаала."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia ауолпепер, амшын аԥшаҳәа ахәқәа рпатреҭ."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia аҭӡамц, азуртәи ахсаала."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Имариоу аҭӡамц стандарт."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia ауолпепер, амраҭашәара ахсаала."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ +"Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Иаԥсахтәуп ахатә даҟьа аҭӡамц ацикл"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia ауолпепер, ахәылбыҽха ақәцәақәа рцәаҩа."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ +"Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Аклассика %@"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the section that allows users to change the wallpaper settings for the application. */ +"Settings.Home.Option.Wallpaper.CollectionTitle" = "АХАТӘИ АЕКРАН"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the button title of the group of wallpapers that are seasonally available to the user. */ +"Settings.Home.Option.Wallpaper.LearnMore.v106" = "Еиҳаны аинформациа"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the default description of the group of wallpapers that are seasonally available to the user. */ +"Settings.Home.Option.Wallpaper.LimitedEdition.Default.Description.v106" = "Алхра ҿыц ԥышәшәа."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the description of the group of wallpapers that are seasonally available to the user. */ +"Settings.Home.Option.Wallpaper.LimitedEdition.IndependentVoices.Description.v106" = "Алхра ҿыц “Зхы иақәиҭу абжьқәа”."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are seasonally available to the user. */ +"Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Аҭыжьымҭа ԥку"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Уҭӡамц ԥсахтәуп Ecosia ахатә даҟьа алого ақәыӷәӷәарала"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ +"Settings.Home.Option.Wallpaper.Title" = "Аҭӡамц"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of toast that comes up when the user changes wallpaper, which lets them know that the wallpaper has been updated. */ +"Settings.Home.Option.Wallpaper.UpdatedToast" = "Аҭӡамц ҿыцуп!"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the button found on the toast that comes up once the user changes wallpaper, and allows users to dismiss the settings page. In this case, consider View as a verb - the action of dismissing settings and seeing the wallpaper. */ +"Settings.Home.Option.Wallpaper.UpdatedToastButton" = "Асахьа"; + +/* Label used as an item in Settings. When touched it will open a dialog to configure the home page and its uses. */ +"Settings.HomePage.SectionName" = "Ахатә даҟьа"; + +/* This string is the title of the setting button which can be clicked to open a page to customize the number of rows in the shortcuts section */ +"Settings.Homepage.Shortcuts.Rows.v100" = "Ацәаҳәақәа"; + +/* This string is the title of the page to customize the number of rows in the shortcuts section */ +"Settings.Homepage.Shortcuts.RowsPageTitle.v100" = "Ацәаҳәақәа"; + +/* Users can disable or enable shortcuts related settings. This string is the title of the page to change your shortcuts settings. */ +"Settings.Homepage.Shortcuts.ShortcutsPageTitle.v100" = "Аҿацәқәа"; + +/* This string is the title of the toggle to disable the shortcuts section in the settings page. */ +"Settings.Homepage.Shortcuts.ShortcutsToggle.v100" = "Аҿацәқәа"; + +/* This string is the title of the toggle to disable the sponsored shortcuts functionnality which can be enabled in the shortcut sections. This toggle is in the settings page. */ +"Settings.Homepage.Shortcuts.SponsoredShortcutsToggle.v100" = "Аспонсортә дыргақәа"; + +/* Toggled OFF to hide the shortcuts section */ +"Settings.Homepage.Shortcuts.ToggleOff.v100" = "Ирцәоу"; + +/* Toggled ON to show the shortcuts section */ +"Settings.Homepage.Shortcuts.ToggleOn.v100" = "Аҿк."; + +/* Title of the setting section containing the URL of the current home page. */ +"Settings.HomePage.URL.Title" = "Уажәтәи ахатә даҟьа"; + +/* Button in settings to use the current page as home page. */ +"Settings.HomePage.UseCurrent.Button" = "Уажәтәи адаҟьа ахархәара"; + +/* Title for the logins and passwords screen. Translation could just use 'Logins' if the title is too long */ +"Settings.LoginsAndPasswordsTitle" = "Алогинқәеи амаӡажәақәеи"; + +/* Label used to set a custom url as the new tab option (homepage). */ +"Settings.NewTab.CustomURL" = "Иҷыдоу URL"; + +/* Option in settings to show a blank page when you open a new tab */ +"Settings.NewTab.Option.BlankPage" = "Иҭацәу адаҟьа"; + +/* Option in settings to show your homepage when you open a new tab */ +"Settings.NewTab.Option.Custom" = "Егьы"; + +/* Option in settings to show Firefox Home when you open a new tab */ +"Settings.NewTab.Option.FirefoxHome" = "Ecosia ахатә даҟьа"; + +/* Option in settings to show your homepage when you open a new tab */ +"Settings.NewTab.Option.HomePage" = "Ахатә даҟьа"; + +/* Label used as an item in Settings. When touched it will open a dialog to configure the new tab behavior. */ +"Settings.NewTab.SectionName" = "Абларҭа ҿыц"; + +/* Title displayed in header of the setting panel. */ +"Settings.NewTab.Title" = "Абларҭа ҿыц"; + +/* Label at the top of the New Tab screen after entering New Tab in settings */ +"Settings.NewTab.TopSectionName" = "Арбара"; + +/* Footer at the bottom of the New Tab screen after entering New Tab in settings */ +"Settings.NewTab.TopSectionNameFooter" = "Агәылаҵа ҿыц анаартуа иҭагалатәу алышәх"; + +/* Label for the block images toggle displayed in the settings menu. Enabling this toggle will hide images on any webpage the user visits. */ +"Settings.NoImageModeBlockImages.Label.v99" = "Асахьақәа рыблок"; + +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ +"Settings.OfferClipboardBar.Status" = "Ecosia анаартуа"; + +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ +"Settings.OfferClipboardBar.Status.v128" = "%@ анаартуа"; + +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ +"Settings.OfferClipboardBar.StatusV2" = "Ecosia анаартуа"; + +/* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ +"Settings.OfferClipboardBar.Title" = "Иқәҭыху азхьарԥшқәа раартразы аҳәара"; + +/* Title for Open With Settings */ +"Settings.OpenWith.PageTitle" = "Иаарттәуп аимеил азхьарԥшқәа"; + +/* Label used as an item in Settings. When touched it will open a dialog to configure the open with (mail links) behavior. */ +"Settings.OpenWith.SectionName" = "Апоштатә арзаҳал"; + +/* Touch ID prompt subtitle when accessing logins and passwords */ +"Settings.Passwords.FingerPrintReason.v103" = "Аԥсыӡажәақәа рахь анеиразы уажәы шәнацәаҭыԥ шәхы иашәырхәа."; + +/* Message shown when you enter Passwords screen for the first time. It explains how password are protected in the Firefox for iOS application. */ +"Settings.Passwords.OnboardingMessage.v103" = "Уажәшьҭа уԥсыӡажәақәа Face ID, Touch ID, ма ухыхәҭа аԥсыӡкод ала ихьчоуп."; + +/* Setting that appears in the Passwords screen to enable the built-in password manager so users can save their passwords. */ +"Settings.Passwords.SavePasswords.v103" = "Амаӡажәақәа еиқәырхатәуп"; + +/* Title for the passwords screen. */ +"Settings.Passwords.Title.v103" = "Амаӡажәақәа"; + +/* Setting to enable the built-in password manager */ +"Settings.SaveLogins.Title" = "Аҭаларақәа еиқәырхатәуп"; + +/* Button displayed at the top of the search settings. */ +"Settings.Search.Done.Button" = "Амазеи"; + +/* Button displayed at the top of the search settings. */ +"Settings.Search.Edit.Button" = "Аԥсахра"; + +/* title for a link that explains how mozilla collects telemetry */ +"Settings.SendUsage.Link" = "Еиҳаны уаԥхьа."; + +/* A short description that explains why mozilla collects usage data. */ +"Settings.SendUsage.Message" = "Мозилла ахаҭарнакра ауеит Ecosia аус аруразы иаҭаху адыррақәа мацара реизгара, насгьы ԥхьаҟа уи аиӷьтәра."; + +/* The title for the setting to send usage data. */ +"Settings.SendUsage.Title" = "Аусуратә дыррақәа рышьҭра"; + +/* Description displayed under the ”Show Link Previews” option */ +"Settings.ShowLinkPreviews.Status" = "Азхьарԥш акыраамҭа ақәыӷәӷәара"; + +/* Description displayed under the ”Show Link Previews” option */ +"Settings.ShowLinkPreviews.StatusV2" = "Азхьарԥш акыраамҭа ақәыӷәӷәара"; + +/* Title of setting to enable link previews when long-pressing links. */ +"Settings.ShowLinkPreviews.Title" = "Азхьарԥш аԥхьахәаԥшра аарԥштәуп"; + +/* Setting to show Logins & Passwords quick access in the application menu */ +"Settings.ShowLoginsInAppMenu.Title" = "Апрограмма амениу аҟны иаарԥштәуп"; + +/* The description of the open new tab siri shortcut */ +"Settings.Siri.OpenTabShortcut" = "Агәылаҵа ҿыц аарттәуп"; + +/* The description that describes what siri shortcuts are */ +"Settings.Siri.SectionDescription" = "Siri ала Ecosia ирласны аартразы Siri адҵақәа шәхы иашәырхәа"; + +/* The option that takes you to the siri shortcuts settings page */ +"Settings.Siri.SectionName" = "Сири адҵақәа"; + +/* Title for a link that explains what Mozilla means by Studies */ +"Settings.Studies.Toggle.Link" = "Еиҳаны еилышәкаа."; + +/* A short description that explains that Mozilla is running studies */ +"Settings.Studies.Toggle.Message" = "Ecosia зны-зынла ишьақәнаргылоит, насгьы аҭҵаара мҩаԥнагоит."; + +/* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ +"Settings.Studies.Toggle.Title" = "Аҭҵаара"; + +/* Ddescription that appears in the settings screen to explain what Firefox Sync is useful for. */ +"Settings.Sync.ButtonDescription.v103" = "Аҭаҩра асинхронизациа аҭаӡҩырақәа, анҵамҭақәа, аԥсыӡажәақәа, уҳәа убас иҵегьы."; + +/* Button label that appears in the settings to prompt the user to sign in to Firefox for iOS sync service to sync and save data. */ +"Settings.Sync.ButtonTitle.v103" = "Адыррақәа рсинхронизациеи рыхьчареи"; + +/* Title for the page where the user sign in to their Firefox Sync account. */ +"Settings.Sync.SignInView.Title.v103" = "Адыррақәа рсинхронизациеи рыхьчареи"; + +/* This is the description for the setting that toggles the Inactive Tabs feature in the settings menu under the Tabs customization section. Inactive tabs are a separate section of tabs that appears in the Tab Tray, which can be enabled or not */ +"Settings.Tabs.CustomizeTabsSection.InactiveTabs" = "Иактивым абларҭақәа"; + +/* This is the description for the setting that toggles the Inactive Tabs feature in the settings menu under the Tabs customization section. Inactive tabs are a separate section of tabs that appears in the Tab Tray, which can be enabled or not */ +"Settings.Tabs.CustomizeTabsSection.InactiveTabsDescription.v101" = "Ҩымчыбжь раахыс узҭамлаз анҵамҭақәа активтәым ахь ииасуеит."; + +/* In the settings menu, in the Tabs customization section, this is the title for the setting that toggles the Tab Groups feature - where tabs from related searches are grouped - on or off */ +"Settings.Tabs.CustomizeTabsSection.TabGroups" = "Агәылаҵа Агәыԥқәа"; + +/* In the settings menu, in the Tabs customization section, this is the title for the Tabs Tray customization section. The tabs tray is accessed from firefox hompage */ +"Settings.Tabs.CustomizeTabsSection.Title" = "Агәылаҵа аҭаӡ рхиатәуп"; + +/* In the settings menu, this is the title for the Tabs customization section option */ +"Settings.Tabs.Title" = "Агәылаҵақәа"; + +/* In the settings menu, in the Toolbar customization section, this label indicates that selecting this will make the toolbar appear at the bottom of the screen. */ +"Settings.Toolbar.Bottom" = "Ҵаҟа"; + +/* In the settings menu, this label indicates that there is an option of customizing the Toolbar appearance. */ +"Settings.Toolbar.SettingsTitle" = "Амыругақәа рпанель"; + +/* In the settings menu, in the Toolbar customization section, this label indicates that selecting this will make the toolbar appear at the top of the screen. */ +"Settings.Toolbar.Top" = "Хыхь"; + +/* Dismiss button for the tracker protection alert. */ +"Settings.TrackingProtection.Alert.Button" = "Ибзиоуп, избоит"; + +/* Decription for the tracker protection alert. */ +"Settings.TrackingProtection.Alert.Description" = "Асаит ииашаны аус амуазар, адресқәа рбар аҿы иҟоу ашәақь ақәыӷәӷәаны, уи адаҟьазы Ирӷәӷәоу ашьҭаԥшра ахьчара арцәа."; + +/* Title for the tracker protection alert. */ +"Settings.TrackingProtection.Alert.Title" = "Азҿлымҳара!"; + +/* The Title on info view which shows a list of all blocked websites */ +"Settings.TrackingProtection.Info.BlocksTitle" = "ИАБЛОКИРУП"; + +/* 'Learn more' info link on the Tracking Protection settings screen. */ +"Settings.TrackingProtection.LearnMore" = "Еиҳаны аинформациа"; + +/* Additional information about your Enhanced Tracking Protection */ +"Settings.TrackingProtection.ProtectionCellFooter" = "Иазырхоу аӡыргара армаҷуеит, насгьы аӡыргаҩцәа шәвеб-сафинг ашьҭаҵара аанкылара иацхраауеит."; + +/* Footer information for tracker protection level. */ +"Settings.TrackingProtection.ProtectionLevel.Footer" = "Асаит ииашаны аус амуазар, адресқәа рбар аҿы иҟоу ашәақь ақәыӷәӷәаны, уи адаҟьазы Ирӷәӷәоу ашьҭаԥшра ахьчара арцәа."; + +/* Footer information for tracker protection level. */ +"Settings.TrackingProtection.ProtectionLevel.Footer.Lock" = "Асаит ииашаны аус амуазар, адресқәа рбар аҿы иҟоу ашәақь ақәыӷәӷәаны, уи адаҟьазы Ирӷәӷәоу ашьҭаԥшра ахьчара арцәа."; + +/* Description for standard level tracker protection */ +"Settings.TrackingProtection.ProtectionLevelStandard.Description" = "Асаитқәа рзы аӡыргаратә тракерқәа ииашаны аус рулартә алнаршоит."; + +/* Description for strict level tracker protection */ +"Settings.TrackingProtection.ProtectionLevelStrict.Description" = "Еиҳаны ашьҭаԥшыгақәеи, аӡыргақәеи, ицәырҵуа аҿаԥшыгақәеи ааннакылоит. Адаҟьақәа еиҳа ирласны иҭагалахоит, аха ҟазшьақәак аус руамзар ҟалоит."; + +/* Title for tracking protection options section where level can be selected. */ +"Settings.TrackingProtection.ProtectionLevelTitle" = "Ахьчара аҩаӡара"; + +/* Row in top-level of settings that gets tapped to show the tracking protection settings detail view. */ +"Settings.TrackingProtection.SectionName" = "Ашьҭаҵара ахьчара"; + +/* Tracking protection settings option for using the basic blocklist. */ +"Settings.TrackingProtectionOption.BasicBlockList" = "Астандарт (ишыҟоу)"; + +/* Tracking protection settings status showing the current option selected. */ +"Settings.TrackingProtectionOption.BasicBlockList.Status" = "Астандарт"; + +/* Tracking protection settings option for using the strict blocklist. */ +"Settings.TrackingProtectionOption.BlockListStrict" = "Иӷәӷәаны ахьчара"; + +/* Settings option to specify that Tracking Protection is on */ +"Settings.TrackingProtectionOption.NormalBrowsingLabelOn" = "Еиӷьтәу ашьҭаԥшра ахьчара"; + +/* Title of link to help page to find out how to solve Sync issues */ +"Settings.TroubleShootSync.Title" = "Ауадаҩрақәа рыӡбара"; + +/* Button shows all websites on website data tableview */ +"Settings.WebsiteData.ButtonShowMore" = "Еиҳаны аарԥшра"; + +/* Description of the confirmation dialog shown when a user tries to clear their private data. */ +"Settings.WebsiteData.ConfirmPrompt" = "Ари аус шәсаит адыррақәа зегьы аԥнахуеит. Уи ԥсахшьа амам."; + +/* Description of the confirmation dialog shown when a user tries to clear some of their private data. */ +"Settings.WebsiteData.SelectedConfirmPrompt" = "Ари аҟаҵара иалху ахәҭақәа аԥнахуеит. Уи ԥсахшьа амам."; + +/* Title displayed in header of the Data Management panel. */ +"Settings.WebsiteData.Title" = "Асаит адыррақәа"; + +/* Action label on share extension to add page to the Firefox reading list. */ +"ShareExtension.AddToReadingListAction.Title" = "Аԥхьара ахьӡынҵа иацҵатәуп"; + +/* Share extension label shown after user has performed 'Add to Reading List' action. */ +"ShareExtension.AddToReadingListActionDone.Title" = "Аԥхьара ахьӡынҵа иацҵоуп"; + +/* Action label on share extension to bookmark the page in Firefox. */ +"ShareExtension.BookmarkThisPageAction.Title" = "Ари адаҟьа азгәаҭа"; + +/* Share extension label shown after user has performed 'Bookmark this Page' action. */ +"ShareExtension.BookmarkThisPageActionDone.Title" = "Иарбоуп"; + +/* Action label on share extension to load the page in Firefox when user switches apps to bring it to foreground. */ +"ShareExtension.LoadInBackgroundAction.Title" = "Афон аҿы аҭагалара"; + +/* Share extension label shown after user has performed 'Load in Background' action. */ +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia ахь ашьҭыхра"; + +/* Action label on share extension to immediately open page in Firefox. */ +"ShareExtension.OpenInFirefoxAction.Title" = "Иаарттәуп Ecosia аҟны"; + +/* Action label on share extension to search for the selected text in Firefox. */ +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia аҟны аԥшаара"; + +/* Button in share sheet to copy the url of the current tab. */ +"ShareSheet.Copy.Title.v108" = "Ақәҭыхра"; + +/* Button in the share sheet to send the current link to another device. */ +"ShareSheet.SendToDevice.Title.v108" = "Аиқәыршәага ахь азхьарԥш ашьҭра"; + +/* Label for show search suggestions setting. */ +"Show Search Suggestions" = "Аԥшааратә ажәалагалақәа"; + +/* Accessibility label for the tabs button in the (top) tab toolbar */ +"Show Tabs" = "Агәылаҵақәа аарԥштәуп"; + +/* Show the on-boarding screen again from the settings */ +"Show Tour" = "Аҭаара амҩаԥгара"; + +/* Accessibility Label for the tab toolbar Stop button */ +"Stop" = "Аанкылара"; + +/* Support section title */ +"Support" = "Адгылара"; + +/* Accessibility hint for tab tray's displayed tab. */ +"Swipe right or left with three fingers to close the tab." = "Аҭаӡҩыра аркразы, х-нацәак рыла арӷьарахь ма арӷьарахь еиҭагатәуп."; + +/* Message displayed when the user's account is syncing with ellipsis at the end */ +"Sync.SyncingEllipsis.Label" = "Асинхронизациа…"; + +/* Title for Sync status message when Sync failed due to being offline */ +"SyncState.Offline.Title" = "Аоффлаин асинхронизациа"; + +/* Message spoken by VoiceOver saying the position of the single currently visible tab in Tabs Tray, along with the total number of tabs. E.g. \"Tab 2 of 5\" says that tab 2 is visible (and is the only visible tab), out of 5 tabs total. */ +"Tab %@ of %@" = "%1$@ %2$@ анҵамҭақәа рҟынтә"; + +/* A label indicating the keyboard shortcut of showing the tab tray. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"Tab.ShowTabTray.KeyCodeTitle" = "Агәылаҵақәа зегьы аарԥштәуп"; + +/* Message spoken by VoiceOver saying the range of tabs that are currently visible in Tabs Tray, along with the total number of tabs. E.g. \"Tabs 8 to 10 of 15\" says tabs 8, 9 and 10 are visible, out of 15 tabs total. */ +"Tabs %@ to %@ of %@" = "%1$@ инаркны %2$@ рҟынӡа %3$@ анҵамҭақәа рҟынтә"; + +/* Accessibility label for the Tabs Tray view. */ +"Tabs Tray" = "Аҭаӡҩыра"; + +/* The button to undo the delete all tabs */ +"Tabs.DeleteAllUndo.Button" = "Аԥыхра"; + +/* The label indicating that all the tabs were closed */ +"Tabs.DeleteAllUndo.Title" = "%d анҵамҭа(қәа) иаркуп"; + +/* Accessibility title for the inactive tabs section button when section is closed. This section groups all tabs that haven't been used in a while. */ +"TabsTray.InactiveTabs.SectionTitle.Closed.Accessibility.v103" = "Иактивым абларҭақәа рбара"; + +/* Accessibility title for the inactive tabs section button when section is open. This section groups all tabs that haven't been used in a while. */ +"TabsTray.InactiveTabs.SectionTitle.Opened.Accessibility.v103" = "Иактивым абларҭақәа ҵәахтәуп"; + +/* Accessibility Label for the tab toolbar Search button */ +"TabToolbar.Accessibility.Search.v106" = "Аԥшаара"; + +/* Accessibility label for the Add Tab button in the Tab Tray. */ +"TabTray.AddTab.Button" = "Агәылаҵа ацҵатәуп"; + +/* A label indicating the keyboard shortcut of closing all tabs from the tab tray. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"TabTray.CloseAllTabs.KeyCodeTitle" = "Агәылаҵақәа зегьы арктәуп"; + +/* Accessibility label for the currently selected tab. */ +"TabTray.CurrentSelectedTab.A11Y" = "Уажәы иалху абӷьыц."; + +/* In the tab tray, when tab groups appear and there exist tabs that don't belong to any group, those tabs are listed under this header as \"Others\" */ +"TabTray.Header.FilteredTabs.SectionHeader" = "Егьы"; + +/* Title for the inactive tabs section. This section groups all tabs that haven't been used in a while. */ +"TabTray.InactiveTabs.SectionTitle" = "Иактивым абларҭақәа"; + +/* A label indicating the keyboard shortcut of opening a new tab in the tab tray. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ +"TabTray.OpenNewTab.KeyCodeTitle" = "Агәылаҵа ҿыц аарттәуп"; + +/* The title on the button to look at private tabs. */ +"TabTray.SegmentedControlTitles.PrivateTabs" = "Хаҭалатәи"; + +/* The title on the button to look at synced tabs. */ +"TabTray.SegmentedControlTitles.SyncedTabs" = "Асинхронизациа"; + +/* The title on the button to look at regular tabs. */ +"TabTray.SegmentedControlTitles.Tabs" = "Агәылаҵақәа"; + +/* The title for the tab tray */ +"TabTray.Title" = "Иаарттәуп абларҭақәа"; + +/* Accessibility Label for the tabs button in the tab toolbar */ +"TabTrayButtons.Accessibility.ShowTabs.v106" = "Агәылаҵақәа аарԥштәуп"; + +/* Message displayed when the reader mode page could not be loaded. This message will appear only when sharing to Firefox reader mode from another app. */ +"The page could not be displayed in Reader View." = "Ари адаҟьа аԥхьаратә хәаԥшышьала аарԥшра ауам."; + +/* Error message in the remote tabs panel */ +"There was a problem accessing tabs from your other devices. Try again in a few moments." = "Даҽа ҳәагақәак рҟынтәи аҭаӡҩырақәа рахь анеираҿы апроблема ыҟан. Ԥыҭрак уааԥшы, нас даҽазнык уҽазышәа."; + +/* Error displayed when reader mode cannot be enabled */ +"There was an error converting the page" = "Адаҟьа аиҭакраан агха ҟалеит"; + +/* Relative date for date in past week. */ +"this week" = "Ари амчыбжь азы"; + +/* Relative date for yesterday. */ +"TimeConstants.Yesterday.v106" = "иацы"; + +/* label for Not Now button */ +"Toasts.NotNow" = "Уажәазы ​​акәым"; + +/* Open App Store button */ +"Toasts.OpenAppStore" = "Апрограммақәа рыҭирҭа аарттәуп"; + +/* Label for button to undo the action just performed */ +"Toasts.Undo" = "Аԥыхра"; + +/* This label is meant to signify the section containing a group of items from the current day. */ +"Today" = "Иахьа"; + +/* Relative date for date older than a minute. */ +"today at %@" = "иахьа %@"; + +/* Accessibility label for the Menu button. */ +"Toolbar.Menu.AccessibilityLabel" = "Амениу"; + +/* Accessibility label for the Close All Tabs menu button. */ +"Toolbar.Menu.CloseAllTabs" = "Агәылаҵақәа зегьы арктәуп"; + +/* Button shown in editing mode to remove this site from the top sites panel. */ +"TopSites.RemovePage.Button" = "Адаҟьа аԥыхра - %@"; + +/* Button to disallow the page to be translated to the user locale language */ +"TranslationToastHandler.PromptTranslate.Cancel" = "Мамоу"; + +/* Button to allow the page to be translated to the user locale language */ +"TranslationToastHandler.PromptTranslate.OK" = "Ааи"; + +/* Prompt for translation. The first parameter is the language the page is in. The second parameter is the name of our local language. The third is the name of the service. */ +"TranslationToastHandler.PromptTranslate.Title" = "Ари адаҟьа абызшәа %1$@ ауп. %2$@ ахь аиагара %3$@ ахархәарала?"; + +/* Tile title for Twitter */ +"Twitter" = "Твиттер"; + +/* The menu item that pastes the current contents of the clipboard into the URL bar and navigates to the page */ +"UIMenuItem.PasteGo" = "Иҷабланы уца"; + +/* Search in New Tab Text selection menu item */ +"UIMenuItem.SearchWithFirefox" = "Ecosia ала аԥшаара"; + +/* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ +"unread" = "Иаԥхьам"; + +/* Debug option */ +"Use stage servers" = "Аԥышәаратә серверқәа рхархәара"; + +/* Username textbox in Authentication prompt */ +"Username" = "Ахархәаҩ ихьӡ"; + +/* The message of the error displayed to a user when they trying to change a wallpaper failed. */ +"Wallpaper.Change.Error.Body.v106" = "Аҭӡамц акы еиқәымшәеит."; + +/* The title of the error displayed when changing wallpaper fails. */ +"Wallpaper.Change.Error.Title.v106" = "Аҭӡамц аԥсахра алымшеит"; + +/* The message of the error displayed to a user when they try change a wallpaper that failed downloading. */ +"Wallpaper.Download.Error.Body.v106" = "Аҭагалараан акы еиқәымшәеит."; + +/* The title of the error displayed if download fails when changing a wallpaper. */ +"Wallpaper.Download.Error.Title.v106" = "Аҭӡамц аҭагалара алымшеит"; + +/* An action for the error displayed to a user when they trying to change a wallpaper failed. */ +"Wallpaper.Error.Dismiss.v106" = "Аԥыхра"; + +/* Action displayed when changing wallpaper fails. */ +"Wallpaper.Error.TryAgain.v106" = "Даҽазнык шәҽазышәшәа"; + +/* Accessibility label for the main web content view */ +"Web content" = "Ауеб аҵакы"; + +/* See http://mzl.la/1LXbDOL */ +"Welcome to your Reading List" = "Бзиала шәаабеит шәзыԥхьо рсиахь"; + +/* Restore Tabs Prompt Title */ +"Well, this is embarrassing." = "Ыы, уи насыԥдароуп"; + +/* Tile title for Wikipedia */ +"Wikipedia" = "Авикипедиа"; + +/* Relative date for yesterday. */ +"yesterday" = "иацы"; + +/* This label is meant to signify the section containing a group of items from the past 24 hours. */ +"Yesterday" = "Иацы"; + +/* Error message in the remote tabs panel */ +"You don’t have any tabs open in Firefox on your other devices." = "Егьырҭ шәхыхәҭақәа рҿы Ecosia аҿы анҵамҭақәа аартуп."; + +/* Your Rights settings section title */ +"Your Rights" = "Шәзинқәа"; + +/* Tile title for YouTube */ +"YouTube" = "Иоутиуб"; + diff --git a/firefox-ios/Shared/ab.lproj/LoginManager.strings b/firefox-ios/Shared/ab.lproj/LoginManager.strings new file mode 100644 index 0000000000000..348411ce9c25a --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/LoginManager.strings @@ -0,0 +1,84 @@ +/* Prompt title when deleting logins */ +"Are you sure?" = "Агәра ганы уҟоума?"; + +/* Prompt option for cancelling out of deletion */ +"Cancel" = "Аԥыхра"; + +/* Accessibility message e.g. spoken by VoiceOver after the user taps the close button in the search field to clear the search and exit search mode */ +"Clear Search" = "Ирыцқьа аԥшаара"; + +/* Copy password text selection menu item */ +"Copy" = "Ақәҭыхра"; + +/* Label describing when the current login was created with the timestamp as the parameter. */ +"Created %@" = "Иаԥҵоуп %@"; + +/* Label for the button used to delete the current login. */ +"Delete" = "Аныхра"; + +/* Prompt option for cancelling out of deletion */ +"DeleteLoginAlert.DeleteButton.Cancel.v122" = "Аԥыхра"; + +/* Label for the button used to delete the current login. */ +"DeleteLoginAlert.DeleteButton.Title.v122" = "Аныхра"; + +/* Prompt message warning the user that deleting non-synced logins will permanently remove them, when they attempt to do so */ +"DeleteLoginAlert.Message.Local.v122" = "Уара иузыҟаҵом ари аус ԥыхра."; + +/* Prompt message warning the user that deleted logins will remove logins from all connected devices */ +"DeleteLoginAlert.Message.Synced.v122" = "Уи асинхронизациа зызу шәхыхәҭақәа зегьы рҟынтәи аԥсыӡажәа аԥнахуеит."; + +/* Title for the prompt that appears when the user deletes a login. */ +"DeleteLoginsAlert.Title.v122" = "Амаӡажәа аԥыхтәума?"; + +/* Label for the button used to deselect all logins. */ +"Deselect All" = "Иалкаатәуп зегьы"; + +/* Accessibility label for entering search mode for logins */ +"Enter Search Mode" = "Иҭажәгал аԥшааратә режим"; + +/* Hide password text selection menu item */ +"Hide" = "Аҵәахра"; + +/* Prompt message warning the user that deleting non-synced logins will permanently remove them */ +"Logins will be permanently removed." = "Аҭаларақәа наӡаӡа иԥыххоит."; + +/* Prompt message warning the user that deleted logins will remove logins from all connected devices */ +"Logins will be removed from all connected devices." = "Алогинқәа ԥыххоит иаԥшьу ахархәагақәа зегьы рҟынтә."; + +/* Label describing when the current login was last modified with the timestamp as the parameter. */ +"Modified %@" = "Иԥсахуп %@ ала"; + +/* Label displayed when no logins are found after searching. */ +"No logins found" = "Аҭаларақәа ԥшаам"; + +/* Label displayed when no logins are found after searching. */ +"NoLoginsFound.Title.v122" = "Иԥшаам ажәамаӡақәа"; + +/* Open and Fill website text selection menu item */ +"Open & Fill" = "Иаартны иҭәтәуп"; + +/* Label displayed above the password row in Login Detail View. */ +"Password" = "Апароль"; + +/* Reveal password text selection menu item */ +"Reveal" = "Арбара"; + +/* Title for the search field at the top of the Logins list screen */ +"Search" = "Аԥшаара"; + +/* Accessibility label for the search input field in the Logins list */ +"Search Input Field" = "Аҭагалара аҭакыра аԥшаара"; + +/* Title for the search field at the top of the Logins list screen */ +"SearchInput.Title.Search.v106" = "Аԥшаара"; + +/* Label for the button used to select all logins. */ +"Select All" = "Иалкаатәуп зегьы"; + +/* Label displayed above the username row in Login Detail View. */ +"Username" = "Ахархәаҩ ихьӡ"; + +/* Label displayed above the website row in Login Detail View. */ +"Website" = "Асаит"; + diff --git a/firefox-ios/Shared/ab.lproj/Menu.strings b/firefox-ios/Shared/ab.lproj/Menu.strings new file mode 100644 index 0000000000000..7dc044074ebf2 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Menu.strings @@ -0,0 +1,57 @@ +/* Label for the button, displayed in the menu, used to close all tabs currently open. */ +"Menu.CloseAllTabsAction.Title" = "Агәылаҵақәа зегьы арктәуп"; + +/* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ +"Menu.CopyLink.Title" = "Азхьарԥш ақәҭыхра"; + +/* Toast displayed to user after downlaod pdf was pressed. */ +"Menu.DownloadPDF.Confirm.v129" = "PDF афаил қәҿиарала иҭагалоуп"; + +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "Аҭагалара PDF"; + +/* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ +"Menu.FindInPageAction.Title" = "Иԥшаатәуп адаҟьаҿы"; + +/* Label for the button, displayed in the menu, used to open a new private tab. */ +"Menu.NewPrivateTabAction.Title" = "Ахатә абӷьыц ҿыц"; + +/* Label for the button, displayed in the menu, used to open a new tab */ +"Menu.NewTabAction.Title" = "Абларҭа ҿыц"; + +/* Accessibility label for the button, displayed in the menu, used to open the Bookmarks home panel. Please keep as short as possible, <15 chars of space available. */ +"Menu.OpenBookmarksAction.AccessibilityLabel.v2" = "Адыргақәа"; + +/* Accessibility label for the button, displayed in the menu, used to open the Downloads home panel. Please keep as short as possible, <15 chars of space available. */ +"Menu.OpenDownloadsAction.AccessibilityLabel.v2" = "Аҭагаларақәа"; + +/* Accessibility label for the button, displayed in the menu, used to open the History home panel. Please keep as short as possible, <15 chars of space available. */ +"Menu.OpenHistoryAction.AccessibilityLabel.v2" = "Ахҭыс"; + +/* Accessibility label for the button, displayed in the menu, used to open the Reading list home panel. Please keep as short as possible, <15 chars of space available. */ +"Menu.OpenReadingListAction.AccessibilityLabel.v2" = "Аԥхьара ахьӡынҵа"; + +/* Label for the button, displayed in the menu, used to open the Settings menu. */ +"Menu.OpenSettingsAction.Title" = "Авариантқәа"; + +/* Accessibility label for the button, displayed in the menu, used to open the Synced Tabs home panel. Please keep as short as possible, <15 chars of space available. */ +"Menu.OpenSyncedTabsAction.AccessibilityLabel.v2" = "Аԥсҭҳәа абларҭақәа"; + +/* Label for the button, displayed in the menu, used to report a compatibility issue with the current page. */ +"Menu.ReportSiteIssueAction.Title" = "Асаит аҿы иҟоу апроблема азы адырраҭара"; + +/* Label for the button, displayed in Firefox Home, used to see all Library panels. */ +"Menu.SeeAllAction.Title" = "Зегьы аарԥштәуп"; + +/* Label for the button, displayed in the menu, used to open the share dialog. */ +"Menu.SharePageAction.Title" = "Ацеиҩшара ала…"; + +/* Label for the button, displayed in the menu, used to request the desktop version of the current website. */ +"Menu.ViewDekstopSiteAction.Title" = "Асаит аверсиа зегьы азыҳәара"; + +/* Label for the button, displayed in the menu, used to request the mobile version of the current website. */ +"Menu.ViewMobileSiteAction.Title" = "Асаит амобильтә версиа азыҳәара"; + +/* Label for the button, displayed in the menu, used to navigate to the home page. */ +"SettingsMenu.OpenHomePageAction.Title" = "Ахатә даҟьа"; + diff --git a/firefox-ios/Shared/ab.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ab.lproj/PrivateBrowsing.strings new file mode 100644 index 0000000000000..051c27a08477d --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/PrivateBrowsing.strings @@ -0,0 +1,33 @@ +/* Setting for closing private tabs */ +"Close Private Tabs" = "Ахатә абларҭақәа арктәуп"; + +/* Context menu option for opening a link in a new private tab */ +"ContextMenu.OpenInNewPrivateTabButtonTitle" = "Иаарттәуп ахатә абларҭа ҿыц аҿы"; + +/* Description text displayed when there are no open tabs while in private mode */ +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia уҭоурых ма укукиқәа рҟынтә акгьы игәалашәом, аха аҭыжьымҭа ҿыцқәа еиқәырхахоит."; + +/* Text button displayed when there are no tabs open while in private mode */ +"Learn More" = "Еиҳаны аинформациа"; + +/* Toggled OFF accessibility value */ +"Off" = "Аусура злымшо"; + +/* Toggled ON accessibility value */ +"On" = "Аус арутәуп"; + +/* Title displayed for when there are no open tabs while in private mode */ +"Private Browsing" = "Ахатә ԥшаара"; + +/* Accessibility label for toggling on/off private mode */ +"Private Mode" = "Ахатә режим"; + +/* Accessibility label for toggling on/off private mode */ +"PrivateBrowsing.Toggle.A11y.Label.v132" = "Ахатә ԥшаара"; + +/* Accessiblity hint for toggling on/off private mode */ +"Turns private mode on or off" = "Ахатә режим аус арутәуп ма иаҿыхтәуп"; + +/* Will be displayed in Settings under 'Close Private Tabs' */ +"When Leaving Private Browsing" = "Ахатә алаԥшхырԥара ааныжьраан"; + diff --git a/firefox-ios/Shared/ab.lproj/Search.strings b/firefox-ios/Shared/ab.lproj/Search.strings new file mode 100644 index 0000000000000..d503601c3702b --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Search.strings @@ -0,0 +1,9 @@ +/* Label for search engine buttons. The argument corresponds to the name of the search engine. */ +"%@ search" = "Аԥшаара %@ аҟны"; + +/* Label for search settings button. */ +"Search Settings" = "Аԥшаара авариантқәа"; + +/* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a label for sponsored Firefox suggestions. */ +"Search.SponsoredSuggestionDescription.v119" = "Аспонсортә"; + diff --git a/firefox-ios/Shared/ab.lproj/Shared.strings b/firefox-ios/Shared/ab.lproj/Shared.strings new file mode 100644 index 0000000000000..36871aa8fb3ce --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Shared.strings @@ -0,0 +1,3 @@ +/* A brief descriptive name for this app on this device, used for Send Tab and Synced Tabs. The first argument is the app name. The second argument is the device name. */ +"%@ on %@" = "%1$@ инаркны %2$@ рҟынӡа"; + diff --git a/firefox-ios/Shared/ab.lproj/Storage.strings b/firefox-ios/Shared/ab.lproj/Storage.strings new file mode 100644 index 0000000000000..cc6ae2807322d --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Storage.strings @@ -0,0 +1,12 @@ +/* The name of the folder that contains desktop bookmarks in the menu. This should match bookmarks.folder.menu.label on Android. */ +"Bookmarks Menu" = "Амениу анҵамҭақәа"; + +/* The name of the folder that contains desktop bookmarks in the toolbar. This should match bookmarks.folder.toolbar.label on Android. */ +"Bookmarks Toolbar" = "Адыргақәа рпанель"; + +/* The title of the folder that contains mobile bookmarks. This should match bookmarks.folder.mobile.label on Android. */ +"Mobile Bookmarks" = "Амобильтә дыргақәа"; + +/* The name of the folder that contains unsorted desktop bookmarks. This should match bookmarks.folder.unfiled.label on Android. */ +"Unsorted Bookmarks" = "Иҭамҵаз аҭыӡдыргақәа"; + diff --git a/firefox-ios/Shared/ab.lproj/Today.strings b/firefox-ios/Shared/ab.lproj/Today.strings new file mode 100644 index 0000000000000..d16327a384ec7 --- /dev/null +++ b/firefox-ios/Shared/ab.lproj/Today.strings @@ -0,0 +1,60 @@ +/* Close Private Tabs button label */ +"TodayWidget.ClosePrivateTabsButton" = "Ахатә абларҭақәа арктәуп"; + +/* Close Private Tabs */ +"TodayWidget.ClosePrivateTabsLabelV2" = "Ахатә абларҭақәа арктәуп"; + +/* Description for medium size widget to add Firefox Shortcut to home screen */ +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ахатә екран ахь Ecosia аҿацәқәа ацҵа."; + +/* Go to link pasted on the clipboard */ +"TodayWidget.GoToCopiedLinkLabelV1" = "Иқәҭыху азхьарԥш шәацныҟәа"; + +/* Go to copied link */ +"TodayWidget.GoToCopiedLinkLabelV2" = "Иқәҭыху азхьарԥш шәацныҟәа"; + +/* %d represents number and it becomes something like +5 more where 5 is the number of open tabs in tab tray beyond what is displayed in the widget */ +"TodayWidget.MoreTabsLabel" = "+ еиҳаны %d…"; + +/* Open New Tab button label */ +"TodayWidget.NewTabButtonLabelV1" = "Аԥшаара ҿыц"; + +/* Label that is shown when there are no tabs opened in tab tray i.e. Empty State */ +"TodayWidget.NoOpenTabsLabel" = "Аҭаӡҩырақәа аартым."; + +/* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ +"TodayWidget.OpenFirefoxLabel" = "Иаарттәуп Ecosia"; + +/* Open New Private Tab button label */ +"TodayWidget.PrivateTabButtonLabelV1" = "Хаҭалатәи аԥшаара"; + +/* Description for small size widget to add it to home screen */ +"TodayWidget.QuickActionGalleryDescription" = "Ахатә екран ахь Ecosia аҿацә ацҵа. Авиджет ацҵара ашьҭахь, уи аредакциа азутәуп, насгьы даҽа ҿацәк алхтәуп."; + +/* Quick Actions title when widget enters edit mode */ +"TodayWidget.QuickActionsGalleryTitle" = "Ирццаку аусқәа"; + +/* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia аҿацәқәа"; + +/* Description for Quick View widget in Gallery View where user can add it to home screen */ +"TodayWidget.QuickViewGalleryDescriptionV2" = "Аҵаҩыра аартразы аҿацәқәа ацҵа."; + +/* Title for Quick View widget in Gallery View where user can add it to home screen */ +"TodayWidget.QuickViewGalleryTitle" = "Ирласны ахәаԥшра"; + +/* Search in Firefox. Do not translate the word Firefox */ +"TodayWidget.SearchInFirefoxV2" = "Ecosia аҟны аԥшаара"; + +/* Search in private tab */ +"TodayWidget.SearchInPrivateTabLabelV2" = "Ахатә абларҭаҿы аԥшаара"; + +/* Description for top sites widget to add Firefox top sites shotcuts to home screen */ +"TodayWidget.TopSitesGalleryDescription" = "Лассы-лассы иаҭаауа, насгьы ааигәа иаҭаауа асаитқәа рҿы аҿацәқәа ацҵа."; + +/* Title for top sites widget to add Firefox top sites shotcuts to home screen */ +"TodayWidget.TopSitesGalleryTitle" = "Иреиӷьу асаитқәа"; + +/* Title for top sites widget to add Firefox top sites shotcuts to home screen */ +"TodayWidget.TopSitesGalleryTitleV2" = "Асаит аҿацәқәа"; + diff --git a/firefox-ios/Shared/af.lproj/Localizable.strings b/firefox-ios/Shared/af.lproj/Localizable.strings index b8bf8b27270e0..b861d776b9e04 100644 --- a/firefox-ios/Shared/af.lproj/Localizable.strings +++ b/firefox-ios/Shared/af.lproj/Localizable.strings @@ -110,7 +110,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Dit kan ’n opstellingsfout wees, of ’n gepeuter deur ’n aanvaller. Gaan voort op eie risiko."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Die eienaar van %@ het hul webwerf verkeerd opgestel. Om u inligting van diefstal te beskerm, het Firefox nie aan dié webwerf verbind nie."; +"ErrorPages.CertWarning.Description" = "Die eienaar van %@ het hul webwerf verkeerd opgestel. Om u inligting van diefstal te beskerm, het Ecosia nie aan dié webwerf verbind nie."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Hierdie verbinding is onvertroud"; @@ -125,7 +125,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox-tuisblad"; +"Firefox.HomePage.Title" = "Ecosia-tuisblad"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Vorentoe"; @@ -143,7 +143,7 @@ "HistoryPanel.EmptySyncedTabsNullState.Description" = "Oortjies van ander toestelle is hier."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Geskiedenis"; @@ -218,7 +218,7 @@ "LoginsHelper.Update.Button" = "Werk by"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Dit lyk of Firefox vantevore omgeval het. Wil u die oortjies herstel?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Dit lyk of Ecosia vantevore omgeval het. Wil u die oortjies herstel?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Merk as gelees"; @@ -248,7 +248,7 @@ "OK" = "Goed"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Gits! Firefox het omgeval."; +"Oops! Firefox crashed" = "Gits! Ecosia het omgeval."; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Open artikels in Leesaansig deur op die boekikoon te raak as dit op die titelbalk verskyn."; @@ -257,7 +257,7 @@ "Open Settings" = "Open instellings"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kan nie die bladsy oopmaak nie want dit het ’n ongeldige adres."; +"OpenURL.Error.Message" = "Ecosia kan nie die bladsy oopmaak nie want dit het ’n ongeldige adres."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Kan nie bladsy open nie"; @@ -332,7 +332,7 @@ "Search.ThirdPartyEngines.OK" = "Goed"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Stuur ’n omvalverslag na Mozilla sodat ons die probleem kan oplos?"; +"Send a crash report so Mozilla can fix the problem?" = "Stuur ’n omvalverslag na Ecosia sodat ons die probleem kan oplos?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Stuur terugvoer"; @@ -491,7 +491,7 @@ "Yesterday" = "Gister"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Daar is geen oortjies oop in Firefox op ander toestelle nie."; +"You don’t have any tabs open in Firefox on your other devices." = "Daar is geen oortjies oop in Ecosia op ander toestelle nie."; /* Your Rights settings section title */ "Your Rights" = "U regte"; diff --git a/firefox-ios/Shared/af.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/af.lproj/PrivateBrowsing.strings index e6d4e474536cc..b1db939acfee1 100644 --- a/firefox-ios/Shared/af.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/af.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Sluit private oortjies"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox sal nie geskiedenis en koekies onthou nie, maar nuwe boekmerke sal gestoor word."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia sal nie geskiedenis en koekies onthou nie, maar nuwe boekmerke sal gestoor word."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Meer inligting"; diff --git a/firefox-ios/Shared/an.lproj/Default Browser.strings b/firefox-ios/Shared/an.lproj/Default Browser.strings index 3cba99e83c8e7..0233b6283d7ba 100644 --- a/firefox-ios/Shared/an.lproj/Default Browser.strings +++ b/firefox-ios/Shared/an.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Achusta los vinclos de pachinas web, correus y mensaches ta ubrir-se automaticament en Firefox."; +"DefaultBrowserCard.Description" = "Achusta los vinclos de pachinas web, correus y mensaches ta ubrir-se automaticament en Ecosia."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambiar lo tuyo navegador por defecto"; diff --git a/firefox-ios/Shared/an.lproj/Intro.strings b/firefox-ios/Shared/an.lproj/Intro.strings index 05109e611d56f..f1dd87f07b552 100644 --- a/firefox-ios/Shared/an.lproj/Intro.strings +++ b/firefox-ios/Shared/an.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Iniciar la sesión"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bienveniu ta Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bienveniu ta Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Empecipia a navegar"; diff --git a/firefox-ios/Shared/an.lproj/Localizable.strings b/firefox-ios/Shared/an.lproj/Localizable.strings index 36ad1b67806b2..503f7cf3c83d7 100644 --- a/firefox-ios/Shared/an.lproj/Localizable.strings +++ b/firefox-ios/Shared/an.lproj/Localizable.strings @@ -257,7 +257,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Puede estar una mala configuración u una falsificación d'un atacant. Sigue enta debant si acceptas lo risgo potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Lo duenyo de %@ ha configurau lo suyo puesto web incorrectament. Pa privar que furten la tuya información, Firefox no s'ha connectau a este puesto web."; +"ErrorPages.CertWarning.Description" = "Lo duenyo de %@ ha configurau lo suyo puesto web incorrectament. Pa privar que furten la tuya información, Ecosia no s'ha connectau a este puesto web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "La tuya connexión no ye verificada"; @@ -278,13 +278,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pachina d'inicio de Firefox"; +"Firefox.HomePage.Title" = "Pachina d'inicio de Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Enta debant"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cuenta de Firefox"; +"FxA.FirefoxAccount" = "Cuenta de Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administrar la cuenta y los dispositivos"; @@ -296,7 +296,7 @@ "fxa.signin.camera-signin" = "Inicia sesión con a tuya camara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "En o tuyo equipo, ubre Firefox y ves ta firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "En o tuyo equipo, ubre Ecosia y ves ta ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Listo pa escaniar"; @@ -308,7 +308,7 @@ "FxA.SyncNow" = "Sincronizar agora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync s'ha connectau a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync s'ha connectau a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync connectau"; @@ -317,7 +317,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ s'ha desconnectau correctament."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo s'ha desconnectau correctament de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo s'ha desconnectau correctament de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconnectau"; @@ -326,7 +326,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconnectau"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "S'ha desconnectau un dispositivo de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "S'ha desconnectau un dispositivo de Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Aduya"; @@ -359,7 +359,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Dentrar pa veyer una lista de pestanyas d'os atros tuyos dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -482,7 +482,7 @@ "LoginsList.Title" = "INICIOS DE SESIÓN ALZAUS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox s'ha zarrau inesperadament. Te faría goyo de restaurar las tuyas pestanyas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia s'ha zarrau inesperadament. Te faría goyo de restaurar las tuyas pestanyas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como leito"; @@ -584,7 +584,7 @@ "Okay" = "Acceptar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Vai! Firefox s'ha blocau"; +"Oops! Firefox crashed" = "Vai! Ecosia s'ha blocau"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Ubre articlos en a vista de lectura pretando l'icono de libro quan amaneixca en a barra de titols."; @@ -596,7 +596,7 @@ "Open Tabs" = "Ubrir las pestanyas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox no podió trobar la pachina perque l'adreza no ye valida."; +"OpenURL.Error.Message" = "Ecosia no podió trobar la pachina perque l'adreza no ye valida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "No s'ha puesto ubrir la pachina"; @@ -608,7 +608,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Este te permite alzar la imachen en o tuyo carret de fotos."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox quiere acceder a las tuyas fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia quiere acceder a las tuyas fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zarrar"; @@ -719,7 +719,7 @@ "Search.ThirdPartyEngines.OK" = "Acceptar"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Ninviar un informe d'o fallo pa que Mozilla resuelta lo problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Ninviar un informe d'o fallo pa que Ecosia resuelta lo problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Ninviar comentarios"; @@ -746,13 +746,13 @@ "SendTo.NavBar.Title" = "Ninviar pestanya"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "No tiens garra atro dispositivo connectau a esta cuenta de Firefox disponible pa sincronizar-se."; +"SendTo.NoDevicesFound.Message" = "No tiens garra atro dispositivo connectau a esta cuenta de Ecosia disponible pa sincronizar-se."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Ubre Firefox, ves ta Achustes y inicia una sesión pa continar."; +"SendTo.NotSignedIn.Message" = "Ubre Ecosia, ves ta Achustes y inicia una sesión pa continar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "No has iniciau sesión en a tuya cuenta de Firefox."; +"SendTo.NotSignedIn.Title" = "No has iniciau sesión en a tuya cuenta de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Ninviar"; @@ -773,7 +773,7 @@ "SentTab.NoTabArrivingNotification.body" = "Selecciona pa empecipiar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Veyer"; @@ -830,7 +830,7 @@ "Settings.DataManagement.Title" = "Chestión d'os datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox aturará la sincronización con a tuya cuenta, pero no borrará garra dato de navegación en este dispositivo."; +"Settings.Disconnect.Body" = "Ecosia aturará la sincronización con a tuya cuenta, pero no borrará garra dato de navegación en este dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconnectar Sync"; @@ -893,7 +893,7 @@ "Settings.FxA.Sync.SectionName" = "Achustes de sincronización"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cuenta de Firefox"; +"Settings.FxA.Title" = "Cuenta de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Cheneral"; @@ -917,7 +917,7 @@ "Settings.NewTab.Option.BlankPage" = "Pachina en blanco"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pachina d'inicio"; @@ -935,10 +935,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Triar qué cargar quan s'ubre una nueva pestanya"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "En ubrir Firefox"; +"Settings.OfferClipboardBar.Status" = "En ubrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "En ubrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "En ubrir Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Proposar ubrir los vinclos copiaus"; @@ -962,7 +962,7 @@ "Settings.SendUsage.Link" = "Saber-ne mas."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla s'esfuerza a recopilar nomás lo que fa falta pa poder executar y amillorar Firefox pa totz."; +"Settings.SendUsage.Message" = "Ecosia s'esfuerza a recopilar nomás lo que fa falta pa poder executar y amillorar Ecosia pa totz."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Ninviar datos d'uso"; @@ -983,7 +983,7 @@ "Settings.Siri.OpenTabShortcut" = "Ubrir una nueva pestanya"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Usar alcorces de Siri pa ubrir rapidament Firefox vía Siri"; +"Settings.Siri.SectionDescription" = "Usar alcorces de Siri pa ubrir rapidament Ecosia vía Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Alcorces de Siri"; @@ -1052,13 +1052,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Ubrir en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ubrir en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Mirar en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Mirar en Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Amostrar las sucherencias de busqueda"; @@ -1163,7 +1163,7 @@ "UIMenuItem.PasteGo" = "Apegar y ir-ie"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Mirar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Mirar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "no leito"; @@ -1193,7 +1193,7 @@ "Yesterday" = "Ahiere"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "No tiens garra pestanya ubierta en Firefox en as tuyas demás dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "No tiens garra pestanya ubierta en Ecosia en as tuyas demás dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Los tuyos dreitos"; diff --git a/firefox-ios/Shared/an.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/an.lproj/PrivateBrowsing.strings index a5f1bf469c566..27dbe3c5f28e6 100644 --- a/firefox-ios/Shared/an.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/an.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Ubrir en Nueva pestanya privada"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox no recordará lo tuyo historial ni los tuyos cookies, pero alzará los marcadors nuevos."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia no recordará lo tuyo historial ni los tuyos cookies, pero alzará los marcadors nuevos."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saber-ne mas"; diff --git a/firefox-ios/Shared/an.lproj/Today.strings b/firefox-ios/Shared/an.lproj/Today.strings index 5932b60c4b922..c4cc8d0bf6655 100644 --- a/firefox-ios/Shared/an.lproj/Today.strings +++ b/firefox-ios/Shared/an.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zarrar pestanyas privadas"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Anyade alcorces de Firefox ta la tuya pantalla d'inicio"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Anyade alcorces de Ecosia ta la tuya pantalla d'inicio"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir ta lo vinclo copiau"; @@ -23,13 +23,13 @@ "TodayWidget.NoOpenTabsLabel" = "No i hai garra pestanya ubierta."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Ubrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Ubrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Busqueda privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Anyade un alcorce de Firefox a la tuya pantalla d'inicio. Dimpués anyade lo widget, toca y mantén ta editar-lo y seleccionar un alcorce diferent."; +"TodayWidget.QuickActionGalleryDescription" = "Anyade un alcorce de Ecosia a la tuya pantalla d'inicio. Dimpués anyade lo widget, toca y mantén ta editar-lo y seleccionar un alcorce diferent."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Accions rapidas"; @@ -41,7 +41,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rapida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Mirar en Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Mirar en Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Mirar en una Pestanya Privada"; diff --git a/firefox-ios/Shared/anp.lproj/Default Browser.strings b/firefox-ios/Shared/anp.lproj/Default Browser.strings index 24fee60ef1227..e45546f6b7b80 100644 --- a/firefox-ios/Shared/anp.lproj/Default Browser.strings +++ b/firefox-ios/Shared/anp.lproj/Default Browser.strings @@ -8,5 +8,5 @@ "DefaultBrowserOnboarding.Description1" = "1. सेटिंग्स में जाहोॅ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox चुनियै"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia चुनियै"; diff --git a/firefox-ios/Shared/anp.lproj/Intro.strings b/firefox-ios/Shared/anp.lproj/Intro.strings index 7a0dee530f3a0..99529c0fd702c 100644 --- a/firefox-ios/Shared/anp.lproj/Intro.strings +++ b/firefox-ios/Shared/anp.lproj/Intro.strings @@ -2,5 +2,5 @@ "Intro.Slides.Button.SignIn" = "साइन इन करो"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox में स्वागत छै"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia में स्वागत छै"; diff --git a/firefox-ios/Shared/anp.lproj/Localizable.strings b/firefox-ios/Shared/anp.lproj/Localizable.strings index 391a945591897..d7b2b957508c0 100644 --- a/firefox-ios/Shared/anp.lproj/Localizable.strings +++ b/firefox-ios/Shared/anp.lproj/Localizable.strings @@ -281,7 +281,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox होम पेज"; +"Firefox.HomePage.Title" = "Ecosia होम पेज"; /* When a user taps and holds on an item from the Recently Visited section, this label will appear indicating the option to remove that item. */ "FirefoxHome.RecentHistory.Remove" = "हटैइयै"; @@ -293,7 +293,7 @@ "Forward" = "आगू बढ़ावोॅ"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox खाता"; +"FxA.FirefoxAccount" = "Ecosia खाता"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "खाता आरू उपकरण प्रबंधित करोॅ"; @@ -305,7 +305,7 @@ "FxA.SyncNow" = "अब सिंक करोॅ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox सिंक %@ में जोड़ देल गेलै."; +"FxAPush_DeviceConnected_body" = "Ecosia सिंक %@ में जोड़ देल गेलै."; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "सिंक जुड़ गेल"; @@ -314,7 +314,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ सफलतापूर्ण डिसकनेक्ट करलो गेल।"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "कंप्यूटर रो Firefox सिंक सेॅ पूरी तरह से डिसकनेक्ट भे गेल"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "कंप्यूटर रो Ecosia सिंक सेॅ पूरी तरह से डिसकनेक्ट भे गेल"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "सिंक डिसकनेक्ट भेल"; @@ -323,7 +323,7 @@ "FxAPush_DeviceDisconnected_title" = "सिंक डिसकनेक्ट भेल"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ई उपकरण Firefox सिंक सेॅ डिसकनेक्ट होय गेल छै"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ई उपकरण Ecosia सिंक सेॅ डिसकनेक्ट होय गेल छै"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "मदद"; @@ -353,7 +353,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "आपन दोसरो उपकरणों से टैब की सूची देखै ले साइन इन करो।"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox सिंक"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia सिंक"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "इतिहास"; @@ -602,7 +602,7 @@ "SendTo.SendAction.Text" = "भेजोॅ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox सिंक"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia सिंक"; /* Title in the settings view controller title bar */ "Settings" = "सेटिंग्स"; @@ -656,7 +656,7 @@ "Settings.FxA.DeviceName" = "डिवाइस रो नाम"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox खाता"; +"Settings.FxA.Title" = "Ecosia खाता"; /* General settings section title */ "Settings.General.SectionName" = "सामान्य"; @@ -674,7 +674,7 @@ "Settings.NewTab.Option.BlankPage" = "खाली पेज"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox होम"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia होम"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "होमपेज"; @@ -704,7 +704,7 @@ "Settings.SendUsage.Link" = "बेसी जानकारी"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "मोजिला खलि ओ चीज के जमा करै छै जकरा Firefox मे सभक लेली सुधार करै के आवश्यक्ता छै."; +"Settings.SendUsage.Message" = "मोजिला खलि ओ चीज के जमा करै छै जकरा Ecosia मे सभक लेली सुधार करै के आवश्यक्ता छै."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "उपयोग डेटा भेजियै"; @@ -743,13 +743,13 @@ "ShareExtension.BookmarkThisPageActionDone.Title" = "बुकमार्कड"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox में लोड भे रहलो छै"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia में लोड भे रहलो छै"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox में खोलियै"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia में खोलियै"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox में खोजियै"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia में खोजियै"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "खोज सुझाव दिखाबियै"; @@ -809,7 +809,7 @@ "UIMenuItem.PasteGo" = "पेस्ट आरु गो"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox सेॅ खोजोॅ"; +"UIMenuItem.SearchWithFirefox" = "Ecosia सेॅ खोजोॅ"; /* Username textbox in Authentication prompt */ "Username" = "यूजरनाम"; diff --git a/firefox-ios/Shared/ar.lproj/Default Browser.strings b/firefox-ios/Shared/ar.lproj/Default Browser.strings index d1819dca7f8fa..3dc3d23a2e971 100644 --- a/firefox-ios/Shared/ar.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ar.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "إن تعيين Firefox كمتصفحك الافتراضي هو تصويت لإنترنت مفتوح ومتاح."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "إن تعيين Ecosia كمتصفحك الافتراضي هو تصويت لإنترنت مفتوح ومتاح."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "جعله متصفحك الدائم هو طريق إلى إنترنت أفضل"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "اطّلع على الطريقة"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "اضبط روابط المواقع والبريد الإلكتروني والرسائل لتفتح تلقائيًا في Firefox."; +"DefaultBrowserCard.Description" = "اضبط روابط المواقع والبريد الإلكتروني والرسائل لتفتح تلقائيًا في Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "اختر Firefox كمتصفحك الافتراضي لجعل السرعة والأمان والخصوصية تلقائية."; +"DefaultBrowserCard.NextLevel.Description.v108" = "اختر Ecosia كمتصفحك الافتراضي لجعل السرعة والأمان والخصوصية تلقائية."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "اِرتَقي بالتصفح اليومي"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "في المتوسط يحظر Firefox أكثر من 3000 متتبع لكل مستخدم شهريًا. اجعلنا متصفحك الافتراضي للحصول على راحة الخصوصية."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "في المتوسط يحظر Ecosia أكثر من 3000 متتبع لكل مستخدم شهريًا. اجعلنا متصفحك الافتراضي للحصول على راحة الخصوصية."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox تولّى مسألة الخصوصية"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia تولّى مسألة الخصوصية"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "بدّل متصفّحك المبدئي"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. انقر ”تطبيق المتصفح الافتراضي“"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. اختر Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. اختر Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "تطبيق المتصفح الافتراضي"; diff --git a/firefox-ios/Shared/ar.lproj/Intro.strings b/firefox-ios/Shared/ar.lproj/Intro.strings index a9dd74e205533..e05e32c1ea6d5 100644 --- a/firefox-ios/Shared/ar.lproj/Intro.strings +++ b/firefox-ios/Shared/ar.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "لِج"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "أهلا بك على Firefox"; +"Intro.Slides.Welcome.Title.v2" = "أهلا بك على Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ابدأ التصفح"; diff --git a/firefox-ios/Shared/ar.lproj/Localizable.strings b/firefox-ios/Shared/ar.lproj/Localizable.strings index 8d00e80cedca4..197f03b99f269 100644 --- a/firefox-ios/Shared/ar.lproj/Localizable.strings +++ b/firefox-ios/Shared/ar.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "قد تكون مشكلة في إعدادات الموقع أو تلاعب من مخترق. لا تواصل إلا إن كنت تقبل بالمخاطرة."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "ضبط مالك %@ موقعه على نحو غير سليم. لحماية معلوماتك من السرقة، لم يتصل Firefox بهذا الموقع."; +"ErrorPages.CertWarning.Description" = "ضبط مالك %@ موقعه على نحو غير سليم. لحماية معلوماتك من السرقة، لم يتصل Ecosia بهذا الموقع."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "هذا الاتصال غير موثوق به"; @@ -332,7 +332,7 @@ "Facebook" = "فيسبوك"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "صفحة Firefox الرئيسية"; +"Firefox.HomePage.Title" = "صفحة Ecosia الرئيسية"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "خصّص صفحة البداية"; @@ -377,7 +377,7 @@ "Forward" = "التالي"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "حساب Firefox"; +"FxA.FirefoxAccount" = "حساب Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "أدِر الحساب و الأجهزة"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "لِج باستخدام كمرتك"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "افتح Firefox على حاسوبك وانتقل إلى firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "افتح Ecosia على حاسوبك وانتقل إلى ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "جاهز للمسح الضوئي"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "زامِن الآن"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "اتَّصل «تزامن Firefox» بِ‍ %@"; +"FxAPush_DeviceConnected_body" = "اتَّصل «تزامن Ecosia» بِ‍ %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "اتَّصل «تزامن»"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "فُصِلَ %@ بنجاح."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "فُصِلَ هذا الجهاز بنجاح من «تزامن Firefox»."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "فُصِلَ هذا الجهاز بنجاح من «تزامن Ecosia»."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "فُصِلَ «تزامن»"; @@ -422,7 +422,7 @@ "FxAPush_DeviceDisconnected_UnknownDevice_body" = "فُصِلَ جهاز من «تزامن فَيَرفُكس»"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "شعار Firefox - غيّر الخلفية."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "شعار Ecosia - غيّر الخلفية."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "المساعدة"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "لِج لعرض قائمة بالألسنة من أجهزتك الأخرى."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "التأريخ"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "اعرف المزيد"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "عليك تفعيل رمز الجهاز لاستعمال ميزة الملء التلقائي في Firefox."; +"Logins.PasscodeRequirement.Warning" = "عليك تفعيل رمز الجهاز لاستعمال ميزة الملء التلقائي في Ecosia."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "خُذ معك كلمات السر أينما ذهبت"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "الملء التلقائي لكلمات السر في Firefox"; +"Logins.WelcomeView.Title2" = "الملء التلقائي لكلمات السر في Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "فعّل الملء التلقائي"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "جلسات الولوج المحفوظة"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "يبدو أن Firefox انهار مؤخرًا. أتريد استعادة ألسنتك؟"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "يبدو أن Ecosia انهار مؤخرًا. أتريد استعادة ألسنتك؟"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "علّمها مقروءة"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "ابدأ التصفح"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "عذرا! انهار Firefox"; +"Oops! Firefox crashed" = "عذرا! انهار Ecosia"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "افتح المقالات في منظور القارئ بالنقر على أيقونة الكتاب عندما تظهر في شريط العنوان."; @@ -905,7 +905,7 @@ "Open Tabs" = "الألسنة المفتوحة"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "تعذّر على Firefox فتح الصفحة لأن عنوانها غير صحيح."; +"OpenURL.Error.Message" = "تعذّر على Ecosia فتح الصفحة لأن عنوانها غير صحيح."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "تعذّر فتح الصفحة"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "يسمح لك هذا بحفظ الصورة في سجل الكمرة."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "يريد Firefox الوصول إلى صورك"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "يريد Ecosia الوصول إلى صورك"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "أغلق"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "البيانات غير سليمة"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "انتقل إلى ‘إعدادات الجهاز’ > ’Firefox‘. ثم اسمح لـ Firefox بالوصول إلى الكاميرا."; +"ScanQRCode.PermissionError.Message.v100" = "انتقل إلى ‘إعدادات الجهاز’ > ’Ecosia‘. ثم اسمح لـ Ecosia بالوصول إلى الكاميرا."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "امسح رمز QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "انتقل إلى اللسان"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "يقترح Firefox"; +"Search.SuggestSectionTitle.v102" = "يقترح Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "سيظهر محرك البحث الجديد في شريط البحث السريع."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "حسنا"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "أتريد إرسال بلاغ عن الانهيار كي تساعد Mozilla في حل المشكلة؟"; +"Send a crash report so Mozilla can fix the problem?" = "أتريد إرسال بلاغ عن الانهيار كي تساعد Ecosia في حل المشكلة؟"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "أرسِل انطباعك"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "أرسِل اللسان"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "لا أجهزة أخرى متصلة بحساب Firefox لمزامنتها."; +"SendTo.NoDevicesFound.Message" = "لا أجهزة أخرى متصلة بحساب Ecosia لمزامنتها."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "من فضلك افتح Firefox، و افتح الإعدادات، و لِج للمتابعة."; +"SendTo.NotSignedIn.Message" = "من فضلك افتح Ecosia، و افتح الإعدادات، و لِج للمتابعة."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "لم تلج إلى حساب Firefox."; +"SendTo.NotSignedIn.Title" = "لم تلج إلى حساب Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "أرسِل"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "اضغط ليبدأ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "اعرض"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "إدارة البيانات"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "سيتوقف Firefox عن مزامنة حسابك، لكن لن يحذف أيًا من بيانات تصفحك على هذا الجهاز."; +"Settings.Disconnect.Body" = "سيتوقف Ecosia عن مزامنة حسابك، لكن لن يحذف أيًا من بيانات تصفحك على هذا الجهاز."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "اقطع اتصال التزامن"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "إعدادات المزامنة"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "حساب Firefox"; +"Settings.FxA.Title" = "حساب Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "عام"; @@ -1268,7 +1268,7 @@ "Settings.Home.Option.StartAtHome.Always" = "صفحة البداية"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "اختر ما تراه عند العودة إلى Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "اختر ما تراه عند العودة إلى Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "اللسان الأخير"; @@ -1313,7 +1313,7 @@ "Settings.NewTab.Option.Custom" = "مخصّص"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "شاشة بداية Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "شاشة بداية Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "صفحة البداية"; @@ -1334,13 +1334,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "احجب الصور"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "عند فتح Firefox"; +"Settings.OfferClipboardBar.Status" = "عند فتح Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "عند فتح %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "عند فتح Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "عند فتح Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "اطلب فتح الروابط المنسوخة"; @@ -1376,7 +1376,7 @@ "Settings.SendUsage.Link" = "اطّلع على المزيد."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "تحاول Mozilla ألا تجمع أكثر مما تحتاجه لإتاحة Firefox و تحسينه للجميع."; +"Settings.SendUsage.Message" = "تحاول Ecosia ألا تجمع أكثر مما تحتاجه لإتاحة Ecosia و تحسينه للجميع."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "أرسِل بيانات الاستخدام"; @@ -1397,7 +1397,7 @@ "Settings.Siri.OpenTabShortcut" = "افتح لسانًا جديدًا"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "استخدم اختصارات «سيري» لفتح Firefox بسرعة عبر «سيري/Siri»"; +"Settings.Siri.SectionDescription" = "استخدم اختصارات «سيري» لفتح Ecosia بسرعة عبر «سيري/Siri»"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "اختصارات سيري"; @@ -1406,7 +1406,7 @@ "Settings.Studies.Toggle.Link" = "اطّلع على المزيد."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "قد يثبّت Firefox بعض الدراسات ويشغّلها من وقت لآخر."; +"Settings.Studies.Toggle.Message" = "قد يثبّت Ecosia بعض الدراسات ويشغّلها من وقت لآخر."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "الدراسات"; @@ -1523,13 +1523,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "حمِّل في الخلفية"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "حُمّل في Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "حُمّل في Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "افتح في Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "افتح في Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "ابحث في Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "ابحث في Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "انسخ"; @@ -1670,7 +1670,7 @@ "UIMenuItem.PasteGo" = "ألصِق وانتقل"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "ابحث مستخدمًا Firefox"; +"UIMenuItem.SearchWithFirefox" = "ابحث مستخدمًا Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "غير مقروء"; @@ -1700,7 +1700,7 @@ "Yesterday" = "الأمس"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "لا ألسنة مفتوحة في Firefox على أجهزتك الأخرى."; +"You don’t have any tabs open in Firefox on your other devices." = "لا ألسنة مفتوحة في Ecosia على أجهزتك الأخرى."; /* Your Rights settings section title */ "Your Rights" = "حقوقك"; diff --git a/firefox-ios/Shared/ar.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ar.lproj/PrivateBrowsing.strings index 7e5b01c897502..a7437610c2984 100644 --- a/firefox-ios/Shared/ar.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ar.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "افتح في لسان خاص جديد"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "لن يتذكر Firefox أي من التأريخ، أو الكعكات، ولكن ستُحفظ العلامات الجديدة."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "لن يتذكر Ecosia أي من التأريخ، أو الكعكات، ولكن ستُحفظ العلامات الجديدة."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "اطّلع على المزيد"; diff --git a/firefox-ios/Shared/ar.lproj/Today.strings b/firefox-ios/Shared/ar.lproj/Today.strings index 6ddf62ffe14f2..6dc12fb31f165 100644 --- a/firefox-ios/Shared/ar.lproj/Today.strings +++ b/firefox-ios/Shared/ar.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "أغلِق الألسنة الخاصة"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "أضف اختصارات Firefox إلى شاشة البداية."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "أضف اختصارات Ecosia إلى شاشة البداية."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "انتقل إلى الرابط المنسوخ"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "لا ألسنة مفتوحة."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "افتح Firefox"; +"TodayWidget.OpenFirefoxLabel" = "افتح Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "بحث خاص"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "أضف اختصار Firefox إلى الشاشة الرئيسية. المس الأداة بعد أن تُضاف مدةً لتعديلها، ثمّ اختر اختصارًا آخر."; +"TodayWidget.QuickActionGalleryDescription" = "أضف اختصار Ecosia إلى الشاشة الرئيسية. المس الأداة بعد أن تُضاف مدةً لتعديلها، ثمّ اختر اختصارًا آخر."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "إجراءات سريعة"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "اختصارات Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "اختصارات Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "أضِف اختصارات إلى الألسنة المفتوحة."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "منظور سريع"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "ابحث في Firefox"; +"TodayWidget.SearchInFirefoxV2" = "ابحث في Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "ابحث في لسان خاص"; diff --git a/firefox-ios/Shared/ast.lproj/Default Browser.strings b/firefox-ios/Shared/ast.lproj/Default Browser.strings index 1b0989e0493c6..e4a36745d3b15 100644 --- a/firefox-ios/Shared/ast.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ast.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "Lleer cómo"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Fai que los enllaces de sitios web, correos electrónicos y Mensaxes s'abran automáticamente en Firefox."; +"DefaultBrowserCard.Description" = "Fai que los enllaces de sitios web, correos electrónicos y Mensaxes s'abran automáticamente en Ecosia."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambéu del restolador predetermináu"; @@ -11,7 +11,7 @@ "DefaultBrowserOnboarding.Description1" = "1. Vete p'Axustes"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Esbilla Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Esbilla Ecosia"; /* Menu option for setting Firefox as default browser. */ "Settings.DefaultBrowserMenuItem" = "Predeterminar el restolador"; diff --git a/firefox-ios/Shared/ast.lproj/Intro.strings b/firefox-ios/Shared/ast.lproj/Intro.strings index 179c34327a042..508ecb68429a5 100644 --- a/firefox-ios/Shared/ast.lproj/Intro.strings +++ b/firefox-ios/Shared/ast.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Aniciar sesión"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Afáyate en Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Afáyate en Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Comenzar a restolar"; diff --git a/firefox-ios/Shared/ast.lproj/Localizable.strings b/firefox-ios/Shared/ast.lproj/Localizable.strings index e8d4d29a50140..fcc72b7dcd3ee 100644 --- a/firefox-ios/Shared/ast.lproj/Localizable.strings +++ b/firefox-ios/Shared/ast.lproj/Localizable.strings @@ -227,13 +227,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Páxina d'aniciu de Firefox"; +"Firefox.HomePage.Title" = "Páxina d'aniciu de Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Avanzar"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cuenta de Firefox"; +"FxA.FirefoxAccount" = "Cuenta de Ecosia"; /* Label when no internet is present */ "FxA.NoInternetConnection" = "Ensin conexón d'internet"; @@ -242,7 +242,7 @@ "fxa.signin.camera-signin" = "Aniciu de sesión cola cámara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Nun ordenador, abri Firefox y vete a https://firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Nun ordenador, abri Ecosia y vete a https://ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Escaniar"; @@ -254,7 +254,7 @@ "FxA.SyncNow" = "Sincronizar agora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync conectóse con %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync conectóse con %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectóse"; @@ -263,7 +263,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ desconectóse correutamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Esti preséu desconectóse de Firefox Sync correutamente."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Esti preséu desconectóse de Ecosia Sync correutamente."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectóse"; @@ -272,7 +272,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectóse"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un preséu desconectóse de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un preséu desconectóse de Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ayuda"; @@ -305,7 +305,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Anicia sesión pa ver una llista de les llingüetes abiertes n'otros preseos de to."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -440,7 +440,7 @@ "LoginsList.Title" = "ANICIOS DE SESIÓN GUARDAOS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Paez que Firefox cascó enantes. ¿Quies restaurar les llingüetes?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Paez que Ecosia cascó enantes. ¿Quies restaurar les llingüetes?"; /* Toast displayed to the user after a bookmark has been added. */ "Menu.AddBookmark.Confirm" = "Amestóse'l marcador"; @@ -539,7 +539,7 @@ "Okay" = "Val"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "¡Meca! Firefox cascó"; +"Oops! Firefox crashed" = "¡Meca! Ecosia cascó"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abri artículos na Vista de Llectora calcando l'iconu del llibru cuando apaeza na barra de títulos."; @@ -551,7 +551,7 @@ "Open Tabs" = "Llingüetes abiertes"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox nun pue abrir la páxina porque tien una direición que nun ye válida."; +"OpenURL.Error.Message" = "Ecosia nun pue abrir la páxina porque tien una direición que nun ye válida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nun se pue abrir la páxina"; @@ -563,7 +563,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Esto permítete guardar la imaxe nel Carrete."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox quier acceder a les tos semeyes"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia quier acceder a les tos semeyes"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zarrar"; @@ -671,7 +671,7 @@ "Search.ThirdPartyEngines.OK" = "D'acuerdu"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "¿Quies unviar un informe del casque pa que Mozilla puea iguar el problema?"; +"Send a crash report so Mozilla can fix the problem?" = "¿Quies unviar un informe del casque pa que Ecosia puea iguar el problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Opinar"; @@ -695,13 +695,13 @@ "SendTo.Error.Title" = "L'enllaz que tentes de compartir nun se pue compartir."; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Nun tienes más preseos conectaos a esta cuenta de Firefox disponible pa sincronizar."; +"SendTo.NoDevicesFound.Message" = "Nun tienes más preseos conectaos a esta cuenta de Ecosia disponible pa sincronizar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Abri Firefox, vete p'Axustes y anicia sesión pa siguir."; +"SendTo.NotSignedIn.Message" = "Abri Ecosia, vete p'Axustes y anicia sesión pa siguir."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nun aniciesti sesión na to cuenta de Firefox."; +"SendTo.NotSignedIn.Title" = "Nun aniciesti sesión na to cuenta de Ecosia."; /* Body of notification shown when the device is sent one or more tabs from an unnamed device. */ "SentTab_TabArrivingNotification_NoDevice_body" = "Aportaron llingüetes nueves d'otru preséu."; @@ -719,7 +719,7 @@ "SentTab.NoTabArrivingNotification.body" = "Calca pa comenzar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Sepia theme setting in Reading View settings */ "Sepia" = "Sepia"; @@ -764,7 +764,7 @@ "Settings.DataManagement.Title" = "Xestión de datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox va dexar de sincronizase cola cuenta, mas nun va desaniciar nengún datu de restolar d'esti preséu."; +"Settings.Disconnect.Body" = "Ecosia va dexar de sincronizase cola cuenta, mas nun va desaniciar nengún datu de restolar d'esti preséu."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectar Sync"; @@ -809,7 +809,7 @@ "Settings.FxA.Sync.SectionName" = "Axustes de sincronización"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cuenta de Firefox"; +"Settings.FxA.Title" = "Cuenta de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Xeneral"; @@ -848,10 +848,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Escueyi lo que se carga al abrir una llingüeta nueva"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.Status" = "Al abrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Al abrir Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ufrir l'apertura d'enllaces copiaos"; @@ -875,7 +875,7 @@ "Settings.SendUsage.Link" = "Lleer más."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla esfuérciase en recoyer namás lo que precisa pa fornir y ameyorar Firefox pa tol mundu."; +"Settings.SendUsage.Message" = "Ecosia esfuérciase en recoyer namás lo que precisa pa fornir y ameyorar Ecosia pa tol mundu."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Unviar datos d'usu"; @@ -890,7 +890,7 @@ "Settings.Studies.Toggle.Link" = "Lleer más."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox pue instalar y executar estudios de xemes en cuando."; +"Settings.Studies.Toggle.Message" = "Ecosia pue instalar y executar estudios de xemes en cuando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudios"; @@ -1022,7 +1022,7 @@ "UIMenuItem.PasteGo" = "Apegar y dir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Buscar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Buscar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ensin lleer"; @@ -1049,7 +1049,7 @@ "Yesterday" = "Ayeri"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Nun tienes llingüetes abiertes nel Firefox d'otros preseos."; +"You don’t have any tabs open in Firefox on your other devices." = "Nun tienes llingüetes abiertes nel Ecosia d'otros preseos."; /* Your Rights settings section title */ "Your Rights" = "Los tos derechos"; diff --git a/firefox-ios/Shared/ast.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ast.lproj/PrivateBrowsing.strings index 0a1f5eaa01ec2..19604c7fe79c9 100644 --- a/firefox-ios/Shared/ast.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ast.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Zarrar les llingüetes privaes"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox nun va recordar l'historial o les cookies, mas van guardase los marcadores nuevos."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia nun va recordar l'historial o les cookies, mas van guardase los marcadores nuevos."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Lleer más"; diff --git a/firefox-ios/Shared/ast.lproj/Today.strings b/firefox-ios/Shared/ast.lproj/Today.strings index e147c8954f77d..a99936ade0c18 100644 --- a/firefox-ios/Shared/ast.lproj/Today.strings +++ b/firefox-ios/Shared/ast.lproj/Today.strings @@ -2,7 +2,7 @@ "TodayWidget.ClosePrivateTabsButton" = "Zarrar les llingüetes privaes"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Amiesta atayos de Firefox a la pantalla d'aniciu."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Amiesta atayos de Ecosia a la pantalla d'aniciu."; /* %d represents number and it becomes something like +5 more where 5 is the number of open tabs in tab tray beyond what is displayed in the widget */ "TodayWidget.MoreTabsLabel" = "+%d más…"; @@ -17,13 +17,13 @@ "TodayWidget.PrivateTabButtonLabelV1" = "Busca privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Amiesta un atayu de Firefox a la pantalla d'aniciu. Darréu d'amestar el widget, tenlu primíu pa editalu y esbillar un atayu diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Amiesta un atayu de Ecosia a la pantalla d'aniciu. Darréu d'amestar el widget, tenlu primíu pa editalu y esbillar un atayu diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Aiciones rápides"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Atayos de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Atayos de Ecosia"; /* Title for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; diff --git a/firefox-ios/Shared/az.lproj/Default Browser.strings b/firefox-ios/Shared/az.lproj/Default Browser.strings index eb2525159233e..7c686b5fcfe41 100644 --- a/firefox-ios/Shared/az.lproj/Default Browser.strings +++ b/firefox-ios/Shared/az.lproj/Default Browser.strings @@ -14,7 +14,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Əsas Səyyah Tətbiq-inə toxunun"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox seçin"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia seçin"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Əsas Səyyah Tətbiqi"; diff --git a/firefox-ios/Shared/az.lproj/Intro.strings b/firefox-ios/Shared/az.lproj/Intro.strings index 3a81be8febc39..c7638cdc6f05f 100644 --- a/firefox-ios/Shared/az.lproj/Intro.strings +++ b/firefox-ios/Shared/az.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Daxil ol"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox-a Xoş Gəldiniz"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia-a Xoş Gəldiniz"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Səyahətə Başla"; diff --git a/firefox-ios/Shared/az.lproj/Localizable.strings b/firefox-ios/Shared/az.lproj/Localizable.strings index 45e212c80aa2c..90dde3eaca64b 100644 --- a/firefox-ios/Shared/az.lproj/Localizable.strings +++ b/firefox-ios/Shared/az.lproj/Localizable.strings @@ -263,7 +263,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Bu səhv konfiqurasiya və ya pis niyyətli birinin hücumu ola bilər. Əgər potensial riski qəbul edirsinizsə davam edin."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ saytının sahibi saytı səhv konfiqurasiya edib. Məlumatlarınızı oğurlanmaqdan qorumaq üçün Firefox bu sayta qoşulmadı."; +"ErrorPages.CertWarning.Description" = "%@ saytının sahibi saytı səhv konfiqurasiya edib. Məlumatlarınızı oğurlanmaqdan qorumaq üçün Ecosia bu sayta qoşulmadı."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Bu bağlantı etibarlı deyil"; @@ -284,13 +284,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Ana Səhifə"; +"Firefox.HomePage.Title" = "Ecosia Ana Səhifə"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "İrəli"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Hesabı"; +"FxA.FirefoxAccount" = "Ecosia Hesabı"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Hesabı və Cihazları idarə et"; @@ -302,7 +302,7 @@ "FxA.SyncNow" = "İndi Sinxronlaşdır"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync %@ cihazına qoşuldu"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync %@ cihazına qoşuldu"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Qoşuldu"; @@ -311,7 +311,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ uğurla ayrıldı."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Bu cihaz uğurla Firefox Sync-dən ayrıldı."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Bu cihaz uğurla Ecosia Sync-dən ayrıldı."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync əlaqə kəsildi"; @@ -320,7 +320,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync əlaqə kəsildi"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Cihaz Firefox Sync-dən ayrıldı"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Cihaz Ecosia Sync-dən ayrıldı"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Kömək"; @@ -353,7 +353,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Digər cihazlarınızdakı vərəqlərin siyahısını görmək üçün daxil olun."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Tarixçə"; @@ -470,7 +470,7 @@ "LoginsList.Title" = "SAXLANMIŞ GİRİŞLƏR"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Görünür Firefox keçən dəfə çöküb. Vərəqlərinizi bərpa etmək istərdiniz?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Görünür Ecosia keçən dəfə çöküb. Vərəqlərinizi bərpa etmək istərdiniz?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Oxunmuş olaraq işarətlə"; @@ -587,7 +587,7 @@ "Okay" = "Tamam"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Vaxsey! Firefox çökdü"; +"Oops! Firefox crashed" = "Vaxsey! Ecosia çökdü"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Kitab ikonu başlıq sətrində göründüyündə ona basaraq məqalələri Oxuyucu Görünüşündə açın."; @@ -599,7 +599,7 @@ "Open Tabs" = "Açıq Vərəqlər"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox səhifəni aça bilmir, çünki ünvanı səhvdir."; +"OpenURL.Error.Message" = "Ecosia səhifəni aça bilmir, çünki ünvanı səhvdir."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Səhifə Açıla Bilmir"; @@ -611,7 +611,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Bu sizə şəkli Camera Roll-da saxlamağa imkan verir."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox Şəkillərinizə keçid əldə etmək istəyir"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia Şəkillərinizə keçid əldə etmək istəyir"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Qapat"; @@ -722,7 +722,7 @@ "Search.ThirdPartyEngines.OK" = "Tamam"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozillanın problemi həll etməsi üçün çökmə hesabatı göndərilsin?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosianın problemi həll etməsi üçün çökmə hesabatı göndərilsin?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Əks-əlaqə Göndər"; @@ -749,13 +749,13 @@ "SendTo.NavBar.Title" = "Vərəqi Göndər"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Sizin bu Firefox Hesabınıza qoşulmuş sinxornlaşdırmaya uyğun başqa cihaz yoxdur."; +"SendTo.NoDevicesFound.Message" = "Sizin bu Ecosia Hesabınıza qoşulmuş sinxornlaşdırmaya uyğun başqa cihaz yoxdur."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Lütfən, davam etmək üçün Firefox-u açın, Tənzimləmələrə gedin və daxil olun."; +"SendTo.NotSignedIn.Message" = "Lütfən, davam etmək üçün Ecosia-u açın, Tənzimləmələrə gedin və daxil olun."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Firefox Hesabınıza daxil olmamısınız."; +"SendTo.NotSignedIn.Title" = "Ecosia Hesabınıza daxil olmamısınız."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Göndər"; @@ -776,7 +776,7 @@ "SentTab.NoTabArrivingNotification.body" = "Başlamaq üçün basın"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Gör"; @@ -833,7 +833,7 @@ "Settings.DataManagement.Title" = "Məlumat İdarəsi"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox sizin hesabla sinxronlaşdırmanı dayandıracaq, amma bu cihazınızdakı heç bir səyahət məlumatını silməyəcək."; +"Settings.Disconnect.Body" = "Ecosia sizin hesabla sinxronlaşdırmanı dayandıracaq, amma bu cihazınızdakı heç bir səyahət məlumatını silməyəcək."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Syncdən ayrıl"; @@ -896,7 +896,7 @@ "Settings.FxA.Sync.SectionName" = "Sync Tənzimləmələri"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Hesabı"; +"Settings.FxA.Title" = "Ecosia Hesabı"; /* General settings section title */ "Settings.General.SectionName" = "Ümumi"; @@ -920,7 +920,7 @@ "Settings.NewTab.Option.BlankPage" = "Boş Səhifə"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Ev"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Ev"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Ana Səhifə"; @@ -938,10 +938,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Yeni vərəq açdığınızda nəyin yüklənəcəyini seçin"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox səyyahını açarkən"; +"Settings.OfferClipboardBar.Status" = "Ecosia səyyahını açarkən"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox səyyahını açarkən"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia səyyahını açarkən"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Köçürülən keçidləri açmağı təklif et"; @@ -965,7 +965,7 @@ "Settings.SendUsage.Link" = "Ətraflı öyrən."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ancaq Firefox-u hamı üçün yaxşılaşdırmağa lazım olan məlumatları yığır."; +"Settings.SendUsage.Message" = "Ecosia ancaq Ecosia-u hamı üçün yaxşılaşdırmağa lazım olan məlumatları yığır."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "İstifadə məlumatlarını göndər"; @@ -977,7 +977,7 @@ "Settings.Siri.OpenTabShortcut" = "Yeni Vərəq Aç"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Firefox səyyahını Siri ilə tez açmaq üçün Siri qısayollarını istifadə edin"; +"Settings.Siri.SectionDescription" = "Ecosia səyyahını Siri ilə tez açmaq üçün Siri qısayollarını istifadə edin"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Qısayolları"; @@ -1052,13 +1052,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Arxada yüklə"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox-da yüklənir"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia-da yüklənir"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox-da Aç"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia-da Aç"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox-da Axtar"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia-da Axtar"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Axtarış təkliflərini göstər"; @@ -1175,7 +1175,7 @@ "UIMenuItem.PasteGo" = "Yapışdır və get"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox ilə Axtar"; +"UIMenuItem.SearchWithFirefox" = "Ecosia ilə Axtar"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "oxunmamış"; @@ -1202,7 +1202,7 @@ "Yesterday" = "Dünən"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Digər cihazlarınızdaki Firefox-da heç bir açıq vərəq yoxdur."; +"You don’t have any tabs open in Firefox on your other devices." = "Digər cihazlarınızdaki Ecosia-da heç bir açıq vərəq yoxdur."; /* Your Rights settings section title */ "Your Rights" = "Hüquqlarınız"; diff --git a/firefox-ios/Shared/az.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/az.lproj/PrivateBrowsing.strings index 76ed662841293..6b219137abf95 100644 --- a/firefox-ios/Shared/az.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/az.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Yeni Məxfi Vərəqdə aç"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox sizin tarixçə və ya çərəzlərinizi yadda saxlamayacaq, lakin yeni əlfəcinlər yaddaşa yazılacaq."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia sizin tarixçə və ya çərəzlərinizi yadda saxlamayacaq, lakin yeni əlfəcinlər yaddaşa yazılacaq."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Ətraflı Öyrən"; diff --git a/firefox-ios/Shared/be.lproj/Default Browser.strings b/firefox-ios/Shared/be.lproj/Default Browser.strings index c582b1232dfdb..117fee5ae71e7 100644 --- a/firefox-ios/Shared/be.lproj/Default Browser.strings +++ b/firefox-ios/Shared/be.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Зрабіўшы Firefox прадвызначаным браўзерам, вы галасуеце за адкрыты, даступны Інтэрнэт."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Зрабіўшы Ecosia прадвызначаным браўзерам, вы галасуеце за адкрыты, даступны Інтэрнэт."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Перайдзіце да лепшага Інтэрнэту"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Даведацца, як"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Наладзьце аўтаматычнае адкрыццё спасылак з сайтаў, пошты і паведамленняў у Firefox."; +"DefaultBrowserCard.Description" = "Наладзьце аўтаматычнае адкрыццё спасылак з сайтаў, пошты і паведамленняў у Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Выберыце Firefox у якасці прадвызначанага браўзера, каб хуткасць, бяспека і прыватнасць сталі аўтаматычнымі."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Выберыце Ecosia у якасці прадвызначанага браўзера, каб хуткасць, бяспека і прыватнасць сталі аўтаматычнымі."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Палепшыце штодзённае вэб-агляданне"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox блакіруе ў сярэднім больш за 3000 трэкераў на карыстальніка кожны месяц. Зрабіце нас сваім прадвызначаным браўзерам, каб быць упэўненымі ў сваёй прыватнасці."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia блакіруе ў сярэднім больш за 3000 трэкераў на карыстальніка кожны месяц. Зрабіце нас сваім прадвызначаным браўзерам, каб быць упэўненымі ў сваёй прыватнасці."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox абараняе прыватнасць"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia абараняе прыватнасць"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Пераключыць прадвызначаны браўзер"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Націсніце Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Выберыце Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Выберыце Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/be.lproj/Intro.strings b/firefox-ios/Shared/be.lproj/Intro.strings index a9dda938b67a8..ec8368d8a4bfb 100644 --- a/firefox-ios/Shared/be.lproj/Intro.strings +++ b/firefox-ios/Shared/be.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Увайсці"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Вітаем у Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Вітаем у Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Пачаць агляданне"; diff --git a/firefox-ios/Shared/be.lproj/Localizable.strings b/firefox-ios/Shared/be.lproj/Localizable.strings index 0d415d2216bce..ec5cb5dacb7a8 100644 --- a/firefox-ios/Shared/be.lproj/Localizable.strings +++ b/firefox-ios/Shared/be.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Гэта можа быць праз няправільную канфігурацыю альбо праз атаку зламысніка. Працягвайце, калі вы прымаеце патэнцыйную рызыку."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Уладальнік %@ наладзіў свой вэб-сайт няправільна. Каб абараніць вашы звесткі ад крадзяжу, Firefox не злучыўся з гэтым вэб-сайтам."; +"ErrorPages.CertWarning.Description" = "Уладальнік %@ наладзіў свой вэб-сайт няправільна. Каб абараніць вашы звесткі ад крадзяжу, Ecosia не злучыўся з гэтым вэб-сайтам."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Гэтаму злучэнню няма даверу"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Хатняя старонка Firefox"; +"Firefox.HomePage.Title" = "Хатняя старонка Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Уладкаваць хатнюю старонку"; @@ -377,7 +377,7 @@ "Forward" = "Наперад"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Уліковы запіс Firefox"; +"FxA.FirefoxAccount" = "Уліковы запіс Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Кіраванне ўліковым запісам і прыладамі"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Увайдзіце з дапамогай камеры"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Адкрыйце Firefox на сваім камп’ютары і перайдзіце на firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Адкрыйце Ecosia на сваім камп’ютары і перайдзіце на ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Гатовы да сканавання"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Сінхранізаваць зараз"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Сінхранізацыя Firefox злучана з %@"; +"FxAPush_DeviceConnected_body" = "Сінхранізацыя Ecosia злучана з %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Сінхранізацыя падключана"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ была паспяхова адключана."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Гэта прылада паспяхова адключана ад сінхранізацыі Firefox."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Гэта прылада паспяхова адключана ад сінхранізацыі Ecosia."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Сінхранізацыя адключана"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Сінхранізацыя адключана"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Прылада адключана ад сінхранізацыі Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Прылада адключана ад сінхранізацыі Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Лагатып Firefox, змяніць шпалеры."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Лагатып Ecosia, змяніць шпалеры."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Даведка"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Увайдзіце, каб бачыць спіс картак з вашых іншых прылад."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Сінхранізацыя Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Сінхранізацыя Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Гісторыя"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Падрабязней"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Каб карыстацца функцыяй аўтазапаўнення Firefox, уключыце код-пароль прылады."; +"Logins.PasscodeRequirement.Warning" = "Каб карыстацца функцыяй аўтазапаўнення Ecosia, уключыце код-пароль прылады."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Вазьміце свае паролі ўсюды"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Аўтазапаўненне пароляў Firefox"; +"Logins.WelcomeView.Title2" = "Аўтазапаўненне пароляў Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Уключыць аўтазапаўненне"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ЗАХАВАНЫЯ ЛАГІНЫ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Падобна, што Firefox раней пацярпеў крах. Вы хочаце аднавіць карткі?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Падобна, што Ecosia раней пацярпеў крах. Вы хочаце аднавіць карткі?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Пазначыць прачытаным"; @@ -890,7 +890,7 @@ "Onboarding.WhatsNew.Button.Title" = "Пачаць агляданне"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ой! Firefox пацярпеў крах"; +"Oops! Firefox crashed" = "Ой! Ecosia пацярпеў крах"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Адкрывайце артыкулы ў рэжыме чытання дотыкам да значку кнігі, калі ён з'явіцца ў адрасным радку."; @@ -902,7 +902,7 @@ "Open Tabs" = "Адкрытыя карткі"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox не можа адкрыць старонку, бо яе адрас несапраўдны."; +"OpenURL.Error.Message" = "Ecosia не можа адкрыць старонку, бо яе адрас несапраўдны."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Немагчыма адкрыць старонку"; @@ -914,7 +914,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Гэта дазваляе захаваць відарыс у сваю фотастужку."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox хоча атрымаць доступ да вашых фатаграфій"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia хоча атрымаць доступ да вашых фатаграфій"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Закрыць"; @@ -1001,7 +1001,7 @@ "ScanQRCode.InvalidDataError.Message" = "Несапраўдныя звесткі"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Прайдзіце ў ‘Налады’ прылады > ‘Firefox’. Дазвольце Firefox доступ да камеры."; +"ScanQRCode.PermissionError.Message.v100" = "Прайдзіце ў ‘Налады’ прылады > ‘Ecosia’. Дазвольце Ecosia доступ да камеры."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Сканіраваць QR-код"; @@ -1016,7 +1016,7 @@ "Search.Awesomebar.SwitchToTab" = "Перайсці ў картку"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Прапановы Firefox"; +"Search.SuggestSectionTitle.v102" = "Прапановы Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Новая пошукавая сістэма з'явіцца ў панэлі хуткага пошуку."; @@ -1052,7 +1052,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Адправіць паведамленне пра збой, каб Mozilla магла выправіць гэту праблему?"; +"Send a crash report so Mozilla can fix the problem?" = "Адправіць паведамленне пра збой, каб Ecosia магла выправіць гэту праблему?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Даслаць водгук"; @@ -1082,13 +1082,13 @@ "SendTo.NavBar.Title" = "Даслаць картку"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "У вас няма ніводнай прылады, злучанай з гэтым уліковым запісам Firefox і даступнай для сінхранізацыі."; +"SendTo.NoDevicesFound.Message" = "У вас няма ніводнай прылады, злучанай з гэтым уліковым запісам Ecosia і даступнай для сінхранізацыі."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Калі ласка, адкрыйце Firefox, прайдзіце ў Налады і ўвайдзіце для працягу."; +"SendTo.NotSignedIn.Message" = "Калі ласка, адкрыйце Ecosia, прайдзіце ў Налады і ўвайдзіце для працягу."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Вы не увайшлі ў свой ўліковы запіс Firefox."; +"SendTo.NotSignedIn.Title" = "Вы не увайшлі ў свой ўліковы запіс Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Даслаць"; @@ -1109,7 +1109,7 @@ "SentTab.NoTabArrivingNotification.body" = "Націсніце каб пачаць"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Сінхранізацыя Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Сінхранізацыя Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Прагледзець"; @@ -1169,7 +1169,7 @@ "Settings.DataManagement.Title" = "Кіраванне данымі"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox спыніць сінхранізацыю з вашым уліковым запісам, але не выдаліць даныя прагляду на гэтай прыладзе."; +"Settings.Disconnect.Body" = "Ecosia спыніць сінхранізацыю з вашым уліковым запісам, але не выдаліць даныя прагляду на гэтай прыладзе."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Адлучыць"; @@ -1235,7 +1235,7 @@ "Settings.FxA.Sync.SectionName" = "Налады сінхранізацыі"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Уліковы запіс Firefox"; +"Settings.FxA.Title" = "Уліковы запіс Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Агульныя"; @@ -1244,7 +1244,7 @@ "Settings.Home.Current.Description.v101" = "Выберыце, што паказваць у якасці хатняй старонкі."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Выберыце, што будзе паказвацца на хатняй старонцы Firefox."; +"Settings.Home.Option.Description.v101" = "Выберыце, што будзе паказвацца на хатняй старонцы Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Адкрыць зноў"; @@ -1268,7 +1268,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Хатняя старонка"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Выберыце, што паказваць, калі вы вернецеся ў Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Выберыце, што паказваць, калі вы вернецеся ў Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Апошняя картка"; @@ -1283,25 +1283,25 @@ "Settings.Home.Option.Wallpaper" = "Шпалеры"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Шпалеры Firefox, аметыставы ўзор."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Шпалеры Ecosia, аметыставы ўзор."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Шпалеры Firefox, узор пляжных узгоркаў."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Шпалеры Ecosia, узор пляжных узгоркаў."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Шпалеры Firefox, васільковы узор."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Шпалеры Ecosia, васільковы узор."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Чыстыя тыповыя шпалеры."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Шпалеры Firefox, узор узыходу сонца."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Шпалеры Ecosia, узор узыходу сонца."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Пераключальнік цыкла шпалер на хатняй старонцы"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Шпалеры Firefox, узор узгоркаў у прыцемках."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Шпалеры Ecosia, узор узгоркаў у прыцемках."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Класічны %@"; @@ -1322,7 +1322,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Лімітаванае выданне"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Змяняць шпалеры дотыкам да лагатыпа хатняй старонкі Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Змяняць шпалеры дотыкам да лагатыпа хатняй старонкі Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Шпалеры"; @@ -1376,7 +1376,7 @@ "Settings.NewTab.Option.Custom" = "Уласная"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Хатняя старонка Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Хатняя старонка Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Хатняя старонка"; @@ -1397,10 +1397,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Блакіраваць відарысы"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Пры адкрыцці Firefox"; +"Settings.OfferClipboardBar.Status" = "Пры адкрыцці Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Пры адкрыцці Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Пры адкрыцці Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Прапаноўваць адкрыццё скапіяваных спасылак"; @@ -1436,7 +1436,7 @@ "Settings.SendUsage.Link" = "Падрабязней."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla імкнецца збіраць толькі тыя звесткі, якія патрэбны для работы і паляпшэння Firefox для ўсіх."; +"Settings.SendUsage.Message" = "Ecosia імкнецца збіраць толькі тыя звесткі, якія патрэбны для работы і паляпшэння Ecosia для ўсіх."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Адпраўляць звесткі аб выкарыстанні"; @@ -1457,7 +1457,7 @@ "Settings.Siri.OpenTabShortcut" = "Адкрыць новую картку"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Выкарыстоўвайце каманды Siri, каб хутка адкрыць Firefox праз Siri"; +"Settings.Siri.SectionDescription" = "Выкарыстоўвайце каманды Siri, каб хутка адкрыць Ecosia праз Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Каманды Siri"; @@ -1466,7 +1466,7 @@ "Settings.Studies.Toggle.Link" = "Даведацца больш."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox можа ўсталёўваць і выконваць даследаванні час ад часу."; +"Settings.Studies.Toggle.Message" = "Ecosia можа ўсталёўваць і выконваць даследаванні час ад часу."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Даследаванні"; @@ -1583,13 +1583,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Загрузіць у фонавым рэжыме"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Загрузка ў Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Загрузка ў Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Адкрыць у Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Адкрыць у Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Шукаць у Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Шукаць у Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Капіяваць"; @@ -1736,7 +1736,7 @@ "UIMenuItem.PasteGo" = "Уставіць і перайсці"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Шукаць з дапамогай Firefox"; +"UIMenuItem.SearchWithFirefox" = "Шукаць з дапамогай Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "непрачытана"; @@ -1784,7 +1784,7 @@ "Yesterday" = "Учора"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "На іншых вашых прыладах у Firefox не адкрыта ніводнай карткі."; +"You don’t have any tabs open in Firefox on your other devices." = "На іншых вашых прыладах у Ecosia не адкрыта ніводнай карткі."; /* Your Rights settings section title */ "Your Rights" = "Вашы правы"; diff --git a/firefox-ios/Shared/be.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/be.lproj/PrivateBrowsing.strings index badeb5e7117fd..37bd0d5aa7608 100644 --- a/firefox-ios/Shared/be.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/be.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Адкрыць у новай прыватнай картцы"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox не будзе запамінаць нічога з вашай гісторыі ці кук, але новыя закладкі будуць захоўвацца."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia не будзе запамінаць нічога з вашай гісторыі ці кук, але новыя закладкі будуць захоўвацца."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Падрабязней"; diff --git a/firefox-ios/Shared/be.lproj/Today.strings b/firefox-ios/Shared/be.lproj/Today.strings index a28d9931cae71..3b96aa67214fb 100644 --- a/firefox-ios/Shared/be.lproj/Today.strings +++ b/firefox-ios/Shared/be.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Закрыць\nпрыватныя карткі"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Дадайце цэтлікі Firefox на Хатні экран."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Дадайце цэтлікі Ecosia на Хатні экран."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Перайсці па скапіяванай спасылцы"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Няма адкрытых картак."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Адкрыць Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Адкрыць Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Прыватны пошук"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Дадайце цэтлік Firefox на Хатні экран. Пасля дадання віджэта, націсніце і ўтрымлівайце, каб адрэдагаваць яго і выбраць іншы цэтлік."; +"TodayWidget.QuickActionGalleryDescription" = "Дадайце цэтлік Ecosia на Хатні экран. Пасля дадання віджэта, націсніце і ўтрымлівайце, каб адрэдагаваць яго і выбраць іншы цэтлік."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Хуткія дзеянні"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Цэтлікі Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Цэтлікі Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Дадайце цэтлікі на адкрытыя карткі."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Хуткі прагляд"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Шукаць у\nFirefox"; +"TodayWidget.SearchInFirefoxV2" = "Шукаць у\nEcosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Шукаць у\nпрыватнай картцы"; diff --git a/firefox-ios/Shared/bg.lproj/Localizable.strings b/firefox-ios/Shared/bg.lproj/Localizable.strings index ff6bf2b3fea63..da91c23eacb5f 100644 --- a/firefox-ios/Shared/bg.lproj/Localizable.strings +++ b/firefox-ios/Shared/bg.lproj/Localizable.strings @@ -236,7 +236,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Може да е погрешна конфигурация или хакерски опит. Продължете, ако поемате понециалния риск."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Собственикът на %@ е настроил страницата неправилно. За да ви защити от кражба на информация, Firefox отказа да направи връзката с тази страница."; +"ErrorPages.CertWarning.Description" = "Собственикът на %@ е настроил страницата неправилно. За да ви защити от кражба на информация, Ecosia отказа да направи връзката с тази страница."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Вашата връзка не е лична"; @@ -257,13 +257,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Начална страница на Firefox"; +"Firefox.HomePage.Title" = "Начална страница на Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Напред"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Управление на сметката и устройствата"; @@ -275,7 +275,7 @@ "FxA.SyncNow" = "Синхронизиране"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync е свързан към %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync е свързан към %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync е свързан"; @@ -284,7 +284,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ е успешно изключено."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Устройството е изключено успешно от Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Устройството е изключено успешно от Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync е изключен"; @@ -293,7 +293,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync е изключен"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Устройство е изключено успешно от Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Устройство е изключено успешно от Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Помощ"; @@ -326,7 +326,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Впишете се, за да видите списък с раздели от други ваши устройства."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Синхронизиране на Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Синхронизиране на Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "История"; @@ -452,7 +452,7 @@ "LoginsHelper.Update.Button" = "Обновяване"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Изглежда, че Firefox се е сринал преди. Искате ли разделите да бъдат възстановени?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Изглежда, че Ecosia се е сринал преди. Искате ли разделите да бъдат възстановени?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Отбелязване като прочетено"; @@ -524,7 +524,7 @@ "Okay" = "Добре"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ами сега! Firefox се срина"; +"Oops! Firefox crashed" = "Ами сега! Ecosia се срина"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Отваряйте статии в изгледа за четене, докосвайки пиктограмата на книга, когато е налична в заглавната лента."; @@ -536,7 +536,7 @@ "Open Tabs" = "Отворени раздели"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox не може да отвори страницата, защото съдържа невалидни адреси."; +"OpenURL.Error.Message" = "Ecosia не може да отвори страницата, защото съдържа невалидни адреси."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Страница не може да бъде отворена"; @@ -641,7 +641,7 @@ "Search.ThirdPartyEngines.OK" = "ОК"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Ше изпратете ли доклад за срива, така че Mozilla да реши проблема?"; +"Send a crash report so Mozilla can fix the problem?" = "Ше изпратете ли доклад за срива, така че Ecosia да реши проблема?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Изпращане обратна връзка"; @@ -680,7 +680,7 @@ "SentTab.NoTabArrivingNotification.body" = "Докоснете за начало"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Изглед"; @@ -737,7 +737,7 @@ "Settings.DataManagement.Title" = "Управление на данните"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ще спре синхронизирането на вашата сметка, но няма да изтрие никакви данни на разглеждането от устройството."; +"Settings.Disconnect.Body" = "Ecosia ще спре синхронизирането на вашата сметка, но няма да изтрие никакви данни на разглеждането от устройството."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Изключване от Sync"; @@ -779,7 +779,7 @@ "Settings.FxA.Sync.SectionName" = "Настройки за синхронизиране"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "Основни"; @@ -815,10 +815,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Изберете какво да виждате в нов раздел"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "При отваряне на Firefox"; +"Settings.OfferClipboardBar.Status" = "При отваряне на Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "При отваряне на Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "При отваряне на Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Питане за отваряне на копираните препратки"; @@ -839,7 +839,7 @@ "Settings.SendUsage.Link" = "Научете повече."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla се старае да събира само нужните данни за подобряване на Firefox за всички."; +"Settings.SendUsage.Message" = "Ecosia се старае да събира само нужните данни за подобряване на Ecosia за всички."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Изпращане данни за използването"; @@ -848,7 +848,7 @@ "Settings.Siri.OpenTabShortcut" = "Отваряне в раздел"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Използване на действия чрез Siri за бързо отваряне на Firefox чрез Siri"; +"Settings.Siri.SectionDescription" = "Използване на действия чрез Siri за бързо отваряне на Ecosia чрез Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Действия чрез Siri"; @@ -902,13 +902,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Зареждане във фонов режим"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Зареждане в Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Зареждане в Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Отваряне в Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Отваряне в Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Търсене в Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Търсене в Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Предложения при търсене"; @@ -1001,7 +1001,7 @@ "UIMenuItem.PasteGo" = "Поставяне и посещаване"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Търсене с Firefox"; +"UIMenuItem.SearchWithFirefox" = "Търсене с Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "непрочетена"; @@ -1028,7 +1028,7 @@ "Yesterday" = "Вчера"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Нямате отворени във Firefox раздели от другите ваши устройства."; +"You don’t have any tabs open in Firefox on your other devices." = "Нямате отворени във Ecosia раздели от другите ваши устройства."; /* Your Rights settings section title */ "Your Rights" = "Вашите права"; diff --git a/firefox-ios/Shared/bg.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/bg.lproj/PrivateBrowsing.strings index 5d9891a8aa6c5..7ccd7c2a8ea58 100644 --- a/firefox-ios/Shared/bg.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/bg.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Затваряне на поверителните раздели"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox няма да запази нищо от вашата история или бисквитки, но новите отметки ще бъдат запазени."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia няма да запази нищо от вашата история или бисквитки, но новите отметки ще бъдат запазени."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Научете повече"; diff --git a/firefox-ios/Shared/bn.lproj/Default Browser.strings b/firefox-ios/Shared/bn.lproj/Default Browser.strings index f348949b04330..dc688b67b4314 100644 --- a/firefox-ios/Shared/bn.lproj/Default Browser.strings +++ b/firefox-ios/Shared/bn.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "শিখুন কিভাবে হয়"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Firefox এ স্বয়ংক্রিয়ভাবে খোলার জন্য ওয়েবসাইট, ইমেল এবং বার্তাগুলি থেকে লিঙ্ক সেট করুন।"; +"DefaultBrowserCard.Description" = "Ecosia এ স্বয়ংক্রিয়ভাবে খোলার জন্য ওয়েবসাইট, ইমেল এবং বার্তাগুলি থেকে লিঙ্ক সেট করুন।"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "আপনার ডিফল্ট ব্রাউজারটিতে স্যুইচ করুন"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. ডিফল্ট ব্রাউজার অ্যাপ এ ট্যাপ করুন"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox নির্বাচন করুন"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia নির্বাচন করুন"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "ডিফল্ট ব্রাউজার অ্যাপ"; diff --git a/firefox-ios/Shared/bn.lproj/Intro.strings b/firefox-ios/Shared/bn.lproj/Intro.strings index ee41d3c2163cd..dcaca328be032 100644 --- a/firefox-ios/Shared/bn.lproj/Intro.strings +++ b/firefox-ios/Shared/bn.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "সাইন ইন"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox এ আপনাকে স্বাগতম"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia এ আপনাকে স্বাগতম"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ব্রাউজিং করা শুরু করুন"; diff --git a/firefox-ios/Shared/bn.lproj/Localizable.strings b/firefox-ios/Shared/bn.lproj/Localizable.strings index 5fbef6a2fbb60..00a54e0b59cf4 100644 --- a/firefox-ios/Shared/bn.lproj/Localizable.strings +++ b/firefox-ios/Shared/bn.lproj/Localizable.strings @@ -296,7 +296,7 @@ "ErrorPages.AdvancedWarning2.Text" = "এটা কোন ভুল কনফিগারেশনের অথবা একটি আক্রমণকারী দ্বারা গরমিল হতে পারে। ঝুঁকি নিতে হলে এগিয়ে যান।"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ টির মালিক ওয়েবসাইট টাকে অন্যায় ভাবে কনফিগার করেছেন। আপানার তথ্য চুরি হওয়া থেকে রক্ষা করতে, Firefox ওয়েবসাইট টির সাথে সংযোগ করেনি।"; +"ErrorPages.CertWarning.Description" = "%@ টির মালিক ওয়েবসাইট টাকে অন্যায় ভাবে কনফিগার করেছেন। আপানার তথ্য চুরি হওয়া থেকে রক্ষা করতে, Ecosia ওয়েবসাইট টির সাথে সংযোগ করেনি।"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "এই সংযুক্তিটি বিশ্বাসযজ্ঞ নয়"; @@ -317,7 +317,7 @@ "Facebook" = "ফেইসবুক"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Home Page"; +"Firefox.HomePage.Title" = "Ecosia Home Page"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "হোমপেজ কাস্টমাইজ করুন"; @@ -326,7 +326,7 @@ "Forward" = "পরবর্তী"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox অ্যাকাউন্ট"; +"FxA.FirefoxAccount" = "Ecosia অ্যাকাউন্ট"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "একাউন্ট এবং ডিভাইস ব্যবস্থাপনা করুন"; @@ -338,7 +338,7 @@ "fxa.signin.camera-signin" = "আপনার ক্যামেরা দিয়ে সাইন ইন করুন"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "আপনার কম্পিউটারে Firefox খুলুন এবং firefox.com/pair এ যান"; +"fxa.signin.qr-link-instruction" = "আপনার কম্পিউটারে Ecosia খুলুন এবং ecosia.org/pair এ যান"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "স্ক্যান করার জন্য প্রস্তুত"; @@ -350,7 +350,7 @@ "FxA.SyncNow" = "এখন সিঙ্ক করুন"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox সিঙ্ক %@ এর সাথে যুক্ত হয়েছে"; +"FxAPush_DeviceConnected_body" = "Ecosia সিঙ্ক %@ এর সাথে যুক্ত হয়েছে"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "সিঙ্ক যুক্ত হয়েছে"; @@ -359,7 +359,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ সফলতার সহিত সংযোগ বিচ্ছিন্ন হয়েছে।"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "এই ডিভাইসটি Firefox সিঙ্ক থেকে সফলভাবে সংযোগ বিচ্ছিন্ন করেছে।"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "এই ডিভাইসটি Ecosia সিঙ্ক থেকে সফলভাবে সংযোগ বিচ্ছিন্ন করেছে।"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "সিঙ্ক সংযোগ বিচ্ছিন্ন হয়েছে"; @@ -368,7 +368,7 @@ "FxAPush_DeviceDisconnected_title" = "সিঙ্ক সংযোগ বিচ্ছিন্ন হয়েছে"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "একটি ডিভাইস Firefox সিঙ্ক থেকে সংযোগ বিচ্ছিন্ন হয়েছে"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "একটি ডিভাইস Ecosia সিঙ্ক থেকে সংযোগ বিচ্ছিন্ন হয়েছে"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "সাহায্য"; @@ -401,7 +401,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "আপনার অন্যান্য ডিভাইস সমূহের ট্যাবের তালিকা দেখতে সাইন ইন করুন।"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox সিঙ্ক"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia সিঙ্ক"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "পূর্ববর্তী তথ্য"; @@ -560,7 +560,7 @@ "Logins.WelcomeView.Tagline" = "আপনার পাসওয়ার্ড সর্বত্র নিন"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox পাসওয়ার্ড AutoFill করুন"; +"Logins.WelcomeView.Title2" = "Ecosia পাসওয়ার্ড AutoFill করুন"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "AutoFill চালু করুন"; @@ -605,7 +605,7 @@ "LoginsList.Title" = "সংরক্ষিত লগইন"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "মনে হচ্ছে Firefox আগে ক্র্যাশ করেছে। আপনি কী আপনার ট্যাবগুলি পুনরুদ্ধার করতে চান?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "মনে হচ্ছে Ecosia আগে ক্র্যাশ করেছে। আপনি কী আপনার ট্যাবগুলি পুনরুদ্ধার করতে চান?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "পড়া হয়েছে হিসাবে চিহ্নিত করুন"; @@ -737,7 +737,7 @@ "Onboarding.WhatsNew.Button.Title" = "ব্রাউজিং শুরু করুন"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ওহহ! Firefox ক্রাশ করেছে"; +"Oops! Firefox crashed" = "ওহহ! Ecosia ক্রাশ করেছে"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "টাইটেলবারে বইয়ের আইকনটি এলে সেটিতে ট্যাপ করে রিডার ভিউতে আর্টিকেলগুলি দেখুন।"; @@ -749,7 +749,7 @@ "Open Tabs" = "খোলা ট্যাব"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox পাতাটি খুলতে পাচ্ছে না কারণ এটিই অবৈধ ঠিকানা।"; +"OpenURL.Error.Message" = "Ecosia পাতাটি খুলতে পাচ্ছে না কারণ এটিই অবৈধ ঠিকানা।"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "পাতা খোলা সম্ভব নয়"; @@ -761,7 +761,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "এটা আপনার ছবি ক্যামেরা রোলে সংরক্ষণ অনুমোদন করবে।"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox আপনার ফোনে প্রবেশাধিকার চায়"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia আপনার ফোনে প্রবেশাধিকার চায়"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "বন্ধ করুন"; @@ -887,7 +887,7 @@ "Search.ThirdPartyEngines.OK" = "ঠিক আছে"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "ক্রাশ সম্পর্কে একটি রিপোর্ট Mozilla-কে পাঠান যাতে এই সমস্যাটি ঠিক করতে পারে?"; +"Send a crash report so Mozilla can fix the problem?" = "ক্রাশ সম্পর্কে একটি রিপোর্ট Ecosia-কে পাঠান যাতে এই সমস্যাটি ঠিক করতে পারে?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "প্রতিক্রিয়া পাঠান"; @@ -914,13 +914,13 @@ "SendTo.NavBar.Title" = "ট্যাব পাঠান"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "সিঙ্ক করার জন্য আপনার কোন ডিভাইস এই Firefox Account এর সাথে যুক্ত নেই।"; +"SendTo.NoDevicesFound.Message" = "সিঙ্ক করার জন্য আপনার কোন ডিভাইস এই Ecosia Account এর সাথে যুক্ত নেই।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "অনুগ্রহ করে Firefox খুলুন, সেটিং এ যান এবং সাইন ইন করুন।"; +"SendTo.NotSignedIn.Message" = "অনুগ্রহ করে Ecosia খুলুন, সেটিং এ যান এবং সাইন ইন করুন।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "আপনি আপনার Firefox Account এ সাইন ইন করেননি।"; +"SendTo.NotSignedIn.Title" = "আপনি আপনার Ecosia Account এ সাইন ইন করেননি।"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "পাঠান"; @@ -941,7 +941,7 @@ "SentTab.NoTabArrivingNotification.body" = "শুরু করতে স্পর্শ করুন"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox সিঙ্ক"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia সিঙ্ক"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "দেখুন"; @@ -998,7 +998,7 @@ "Settings.DataManagement.Title" = "ডাটা ব্যাবস্থাপনা"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox আপনার অ্যাকাউন্টের সাথে সিঙ্ক করা বন্ধ করবে, কিন্তু এই ডিভাইসে আপনার ব্রাউজ করা কোন তথ্য মুছে ফেলা হবে না।"; +"Settings.Disconnect.Body" = "Ecosia আপনার অ্যাকাউন্টের সাথে সিঙ্ক করা বন্ধ করবে, কিন্তু এই ডিভাইসে আপনার ব্রাউজ করা কোন তথ্য মুছে ফেলা হবে না।"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "সিঙ্ক সংযোগ বিচ্ছিন্ন"; @@ -1061,7 +1061,7 @@ "Settings.FxA.Sync.SectionName" = "সিঙ্ক সেটিংস"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox অ্যাকাউন্ট"; +"Settings.FxA.Title" = "Ecosia অ্যাকাউন্ট"; /* General settings section title */ "Settings.General.SectionName" = "সাধারণ"; @@ -1106,7 +1106,7 @@ "Settings.NewTab.Option.BlankPage" = "ফাঁকা পেজ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "হোমপেজ"; @@ -1124,10 +1124,10 @@ "Settings.NewTab.TopSectionNameFooter" = "নতুন ট্যাব খোলার পর কি দেখতে চান পছন্দ করুন"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox চালু হওয়ার সময়"; +"Settings.OfferClipboardBar.Status" = "Ecosia চালু হওয়ার সময়"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox চালু হওয়ার সময়"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia চালু হওয়ার সময়"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "অনুলিপি করে রাখা লিঙ্ক খোলার প্রস্তাব"; @@ -1151,7 +1151,7 @@ "Settings.SendUsage.Link" = "আরও জানুন।"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla শুধু এমন কিছু সংগ্রহ করে যা অত্যাবশ্যক প্রয়োজনীয় এবং সকলের জন্যে আরও উন্নত Firefox সংস্করণ তৈরীতে ব্যবহার্য।"; +"Settings.SendUsage.Message" = "Ecosia শুধু এমন কিছু সংগ্রহ করে যা অত্যাবশ্যক প্রয়োজনীয় এবং সকলের জন্যে আরও উন্নত Ecosia সংস্করণ তৈরীতে ব্যবহার্য।"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ব্যবহারের উপাত্ত প্রেরণ করুন"; @@ -1172,7 +1172,7 @@ "Settings.Siri.OpenTabShortcut" = "নতুন ট্যাব খুলুন"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri ব্যবহার করে দ্রুত Firefox চালু করতে Siri শর্টকাট ব্যবহার করুন"; +"Settings.Siri.SectionDescription" = "Siri ব্যবহার করে দ্রুত Ecosia চালু করতে Siri শর্টকাট ব্যবহার করুন"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri শর্টকাটসমূহ"; @@ -1181,7 +1181,7 @@ "Settings.Studies.Toggle.Link" = "আরও জানুন।"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "কিছু দিন পর Firefox স্টাডিস ইনস্টল করতে এবং চালাতে পারে।"; +"Settings.Studies.Toggle.Message" = "কিছু দিন পর Ecosia স্টাডিস ইনস্টল করতে এবং চালাতে পারে।"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "অধ্যয়ন"; @@ -1262,13 +1262,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ব্যাকগ্রাউন্ডে লোড করুন"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox এ লোড হচ্ছে"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia এ লোড হচ্ছে"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox এ খুলুন"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia এ খুলুন"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox এ অনুসন্ধান করুন"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia এ অনুসন্ধান করুন"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "সার্চইঞ্জিন সাজেশন দেখাও"; @@ -1394,7 +1394,7 @@ "UIMenuItem.PasteGo" = "পেস্ট করুন এবং যান"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox দিয়ে অনুসন্ধান করুন"; +"UIMenuItem.SearchWithFirefox" = "Ecosia দিয়ে অনুসন্ধান করুন"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "পড়া হয়নি"; @@ -1424,7 +1424,7 @@ "Yesterday" = "গতকাল"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Firefox-এ আপনার কোনও ট্যাব আপনার অন্যান্য ডিভাইসে খোলা নেই।"; +"You don’t have any tabs open in Firefox on your other devices." = "Ecosia-এ আপনার কোনও ট্যাব আপনার অন্যান্য ডিভাইসে খোলা নেই।"; /* Your Rights settings section title */ "Your Rights" = "আপনার অধিকার"; diff --git a/firefox-ios/Shared/bn.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/bn.lproj/PrivateBrowsing.strings index 79c28f8322d18..c7a36eea9c7cc 100644 --- a/firefox-ios/Shared/bn.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/bn.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "নতুন ব্যক্তিগত ট্যাবে খুলুন"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox আপনার কোনো ইতিহাস বা কুকি স্মরণ করবে না, কিন্তু নতুন বুকমার্ক সংরক্ষণ করা হবে।"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia আপনার কোনো ইতিহাস বা কুকি স্মরণ করবে না, কিন্তু নতুন বুকমার্ক সংরক্ষণ করা হবে।"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "আরও জানুন"; diff --git a/firefox-ios/Shared/bn.lproj/Today.strings b/firefox-ios/Shared/bn.lproj/Today.strings index e8fe417f04981..d5e4c4717003f 100644 --- a/firefox-ios/Shared/bn.lproj/Today.strings +++ b/firefox-ios/Shared/bn.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "ব্যক্তিগত\nট্যাবগুলি বন্ধ করুন"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "আপনার হোম স্ক্রিনে Firefox শর্টকাট যুক্ত করুন।"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "আপনার হোম স্ক্রিনে Ecosia শর্টকাট যুক্ত করুন।"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "কপি করা লিঙ্কে যান"; @@ -23,13 +23,13 @@ "TodayWidget.NoOpenTabsLabel" = "কোনো খোলা ট্যাব নেই।"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox খুলুন"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia খুলুন"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "ব্যক্তিগত অনুসন্ধান"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "আপনার হোম স্ক্রিনে একটি Firefox শর্টকাট যুক্ত করুন। উইজেট যুক্ত করার পরে, এটি সম্পাদনা করতে স্পর্শ করুন এবং ধরে রাখুন এবং একটি আলাদা শর্টকাট নির্বাচন করুন।"; +"TodayWidget.QuickActionGalleryDescription" = "আপনার হোম স্ক্রিনে একটি Ecosia শর্টকাট যুক্ত করুন। উইজেট যুক্ত করার পরে, এটি সম্পাদনা করতে স্পর্শ করুন এবং ধরে রাখুন এবং একটি আলাদা শর্টকাট নির্বাচন করুন।"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "দ্রুত পদক্ষেপ"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "কুইক ভিউ"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox এ\nঅনুসন্ধান করুন"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia এ\nঅনুসন্ধান করুন"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "ব্যক্তিগত ট্যাবে\nঅনুসন্ধান করুন"; diff --git a/firefox-ios/Shared/br.lproj/Default Browser.strings b/firefox-ios/Shared/br.lproj/Default Browser.strings index 37607b461721d..cf5dbac72b551 100644 --- a/firefox-ios/Shared/br.lproj/Default Browser.strings +++ b/firefox-ios/Shared/br.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Dibab Firefox evel merdeer dre ziouer a dalv kement ha votiñ evit un internet digor hag aes."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Dibab Ecosia evel merdeer dre ziouer a dalv kement ha votiñ evit un internet digor hag aes."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Un internet gwelloc’h dre ziouer"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Gouzout penaos"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Lakaat da zigeriñ e Firefox en un doare emgefreek liammoù al lec’hiennoù, posteloù, ha kemennadennoù."; +"DefaultBrowserCard.Description" = "Lakaat da zigeriñ e Ecosia en un doare emgefreek liammoù al lec’hiennoù, posteloù, ha kemennadennoù."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Dibabit Firefox evit ma vefe emgefreek an tizh, ar surentez hag ar prevezded."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Dibabit Ecosia evit ma vefe emgefreek an tizh, ar surentez hag ar prevezded."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Gwellait ho merdeiñ pemdeziek"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Bep miz e vez stanket ouzhpenn 3,000 heulier dre implijer gant Firefox. Dibabit ac'hanomp evel merdeer dre ziouer evit chom dinec'h en ho prevezded."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Bep miz e vez stanket ouzhpenn 3,000 heulier dre implijer gant Ecosia. Dibabit ac'hanomp evel merdeer dre ziouer evit chom dinec'h en ho prevezded."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Gant Firefox eo prevez ho puhez"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Gant Ecosia eo prevez ho puhez"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cheñch ho merdeer dre ziouer"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Stokañ an arload merdeer dre ziouer"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Dibab Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Dibab Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Arload merdeer dre ziouer"; diff --git a/firefox-ios/Shared/br.lproj/Intro.strings b/firefox-ios/Shared/br.lproj/Intro.strings index 6e45881530db7..f194ed2a57a35 100644 --- a/firefox-ios/Shared/br.lproj/Intro.strings +++ b/firefox-ios/Shared/br.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Kennaskañ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Donemat e Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Donemat e Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Kregiñ gant ar merdeiñ"; diff --git a/firefox-ios/Shared/br.lproj/Localizable.strings b/firefox-ios/Shared/br.lproj/Localizable.strings index 6035784e5ddba..aa0e69a56a752 100644 --- a/firefox-ios/Shared/br.lproj/Localizable.strings +++ b/firefox-ios/Shared/br.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Ur c’hefluniadur a-dreuz pe un dagadenn e c’hell bezañ. Kendalc’hit mar asantit ar riskl."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "En un doare dizereat eo bet kefluniet o lec’hienn gant %@. Evit gwareziñ ho titouroù da vezañ laeret, n’eo ket kennasket Firefox d’al lec’hienn."; +"ErrorPages.CertWarning.Description" = "En un doare dizereat eo bet kefluniet o lec’hienn gant %@. Evit gwareziñ ho titouroù da vezañ laeret, n’eo ket kennasket Ecosia d’al lec’hienn."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "N’haller ket fiziout er c’hennask-mañ"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pennbajenn Firefox"; +"Firefox.HomePage.Title" = "Pennbajenn Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personelaat an degemer"; @@ -377,7 +377,7 @@ "Forward" = "War-lerc’h"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Kont Firefox"; +"FxA.FirefoxAccount" = "Kont Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Ardeiñ ar gont"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Kennaskit gant ho penveg skeudennoù"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "War hoc’h urzhiataer, digorit Firefox ha kit war ar bajenn firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "War hoc’h urzhiataer, digorit Ecosia ha kit war ar bajenn ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Prest da c’hwilerviñ"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Goubredañ bremañ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Kennasket eo Firefox Sync ouzh %@"; +"FxAPush_DeviceConnected_body" = "Kennasket eo Ecosia Sync ouzh %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Kennasket"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync digennasket"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Digennasket eo bet un trevnad diouzh Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Digennasket eo bet un trevnad diouzh Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Arlun Firefox - kemmañ an drekleur."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Arlun Ecosia - kemmañ an drekleur."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Skoazell"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Kennaskañ evit gwelet roll ivinelloù ho trevnadoù all."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Roll istor"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Gouzout hiroc’h"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Evit ober gant al leuniañ emgefreek war Firefox e rankit lakaat ur boneg tremen."; +"Logins.PasscodeRequirement.Warning" = "Evit ober gant al leuniañ emgefreek war Ecosia e rankit lakaat ur boneg tremen."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Kemerit ho kerioù-tremen e pep lec’h"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Leuniañ gerioù-tremen Firefox ent emgefreek"; +"Logins.WelcomeView.Title2" = "Leuniañ gerioù-tremen Ecosia ent emgefreek"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Gweredekaat al leuniañ emgefreek"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "TITOUROÙ KENNASKAÑ ENROLLET"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sac'het en deus Firefox war a-seblant. Fellout a ra deoc’h assav ho ivinelloù?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sac'het en deus Ecosia war a-seblant. Fellout a ra deoc’h assav ho ivinelloù?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Merkañ evel lennet"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Kregiñ gant ar merdeiñ"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Chaous! Sac’het eo Firefox"; +"Oops! Firefox crashed" = "Chaous! Sac’het eo Ecosia"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Digorit ar pennadoù er mod lenn en ur stekiñ ouzh an arlun levr p’eo skrammet er varrenn-ditl."; @@ -905,7 +905,7 @@ "Open Tabs" = "Digeriñ an ivinelloù"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox n’hall ket digeriñ ar bajenn dre m’eo didalvoudek ar chomlec’h."; +"OpenURL.Error.Message" = "Ecosia n’hall ket digeriñ ar bajenn dre m’eo didalvoudek ar chomlec’h."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "N’haller ket digeriñ ar bajenn"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Aotren a ra kement-se da enrollañ ar skeudenn d’ho koc’henn."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Fellout a rafe da Firefox haeziñ ho Skeudennoù"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Fellout a rafe da Ecosia haeziñ ho Skeudennoù"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Serriñ"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Didalvoudek eo ar roadennoù"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Mont en arventennoù > Firefox, Aotren Firefox da haeziñ ar c'hamera."; +"ScanQRCode.PermissionError.Message.v100" = "Mont en arventennoù > Ecosia, Aotren Ecosia da haeziñ ar c'hamera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "C’hwilerviñ ar boneg QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Mont d'an ivinell"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox a ginnig"; +"Search.SuggestSectionTitle.v102" = "Ecosia a ginnig"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Er varrenn glask prim e vo skrammet al lusker enklask nevez."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Mat eo"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Kas un danevell sac’hadenn evit ma c’hallfe Mozilla ratreañ ar gudenn?"; +"Send a crash report so Mozilla can fix the problem?" = "Kas un danevell sac’hadenn evit ma c’hallfe Ecosia ratreañ ar gudenn?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Kas ho meno"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Kas an ivinell"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "N’ho peus trevnad ebet all kennasket d’ar gont Firefox-mañ hegerz evit goubredañ."; +"SendTo.NoDevicesFound.Message" = "N’ho peus trevnad ebet all kennasket d’ar gont Ecosia-mañ hegerz evit goubredañ."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Digorit Firefox, kit en arventennoù ha kennaskit evit kenderc’hel."; +"SendTo.NotSignedIn.Message" = "Digorit Ecosia, kit en arventennoù ha kennaskit evit kenderc’hel."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "N’hoc’h ket kennasket d’ho kont Firefox."; +"SendTo.NotSignedIn.Title" = "N’hoc’h ket kennasket d’ho kont Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Kas"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Stokit da gregiñ ganti"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Gwel"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Merañ ar roadennoù"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox a baouezo da c’houbredañ gant ho kont, met ne zilamo ket ho roadennoù merdeiñ war an trevnad-mañ."; +"Settings.Disconnect.Body" = "Ecosia a baouezo da c’houbredañ gant ho kont, met ne zilamo ket ho roadennoù merdeiñ war an trevnad-mañ."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Digennaskañ Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Arventennoù Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Kont Firefox"; +"Settings.FxA.Title" = "Kont Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Hollek"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Dibabit petra a vo diskouezet war bajenn an degemer."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Dibabit an danvezioù ebarzhet war bajenn zegemer Firefox."; +"Settings.Home.Option.Description.v101" = "Dibabit an danvezioù ebarzhet war bajenn zegemer Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Distreiñ"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pennbajenn"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Dibabit ar pezh a welit pa tistroit da Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Dibabit ar pezh a welit pa tistroit da Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Ivinell diwezhañ"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Drekleur"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Drekleur Firefox, patrom ametist."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Drekleur Ecosia, patrom ametist."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Drekleur Firefox, patrom tornaodoù."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Drekleur Ecosia, patrom tornaodoù."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Drekleur Firefox, patrom damc'hlas."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Drekleur Ecosia, patrom damc'hlas."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Drekleur sklaer dre ziouer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Drekleur Firefox, patrom gouloù-deiz."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Drekleur Ecosia, patrom gouloù-deiz."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Trec'haoler kelc'hiañ an drekleur pennbajenn"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Drekleur Firefox, patrom serr-noz."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Drekleur Ecosia, patrom serr-noz."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ klasel"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Embann termenet"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Kemmañ an drekleur en ur stokañ arlun pennbajenn Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Kemmañ an drekleur en ur stokañ arlun pennbajenn Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Drekleur"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personelaet"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Degemer Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Degemer Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pennbajenn"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Stakañ ar skeudennoù"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Pa loc’han Firefox"; +"Settings.OfferClipboardBar.Status" = "Pa loc’han Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Pa vez digoret %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Pa loc’han Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Pa loc’han Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Kinnig digeriñ ereoù eiet"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Gouzout hiroc’h."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Strivañ a ra Mozilla evit dastum ar pezh hon eus ezhomm hepken evit gwellaat ha kinnig Firefox d’an holl."; +"Settings.SendUsage.Message" = "Strivañ a ra Ecosia evit dastum ar pezh hon eus ezhomm hepken evit gwellaat ha kinnig Ecosia d’an holl."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Kas roadennoù arver"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Digeriñ un ivinell nevez"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Arverañ berradennoù Siri evit digeriñ buan Firefox dre Siri"; +"Settings.Siri.SectionDescription" = "Arverañ berradennoù Siri evit digeriñ buan Ecosia dre Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Berradennoù Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Gouzout hiroc'h."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Gallout a ra Firefox staliañ ha lañsañ studioù bep an amzer."; +"Settings.Studies.Toggle.Message" = "Gallout a ra Ecosia staliañ ha lañsañ studioù bep an amzer."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studioù"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Kargañ en drekleur"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "O kargañ e Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "O kargañ e Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Digeriñ e Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Digeriñ e Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Klask e Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Klask e Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Eilañ"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Pegañ & Mont"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Klask gant Firefox"; +"UIMenuItem.SearchWithFirefox" = "Klask gant Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "anlennet"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Dec’h"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "N’ho peus ivinell digor ebet e Firefox war ho trevnadoù all."; +"You don’t have any tabs open in Firefox on your other devices." = "N’ho peus ivinell digor ebet e Ecosia war ho trevnadoù all."; /* Your Rights settings section title */ "Your Rights" = "Ho kwirioù"; diff --git a/firefox-ios/Shared/br.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/br.lproj/PrivateBrowsing.strings index b92f53fa4a5e2..3ace88cfa0c58 100644 --- a/firefox-ios/Shared/br.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/br.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Digeriñ en un ivinell prevez nevez"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ne vo ket dalc’het soñj eus ho roll istor nag eus ho toupinoù gant Firefox, met enrollet e vo ar sinedoù nevez."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ne vo ket dalc’het soñj eus ho roll istor nag eus ho toupinoù gant Ecosia, met enrollet e vo ar sinedoù nevez."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Gouzout hiroc’h"; diff --git a/firefox-ios/Shared/br.lproj/Today.strings b/firefox-ios/Shared/br.lproj/Today.strings index c26c3fb8b402a..e690c2d0227cd 100644 --- a/firefox-ios/Shared/br.lproj/Today.strings +++ b/firefox-ios/Shared/br.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Serriñ an ivinelloù prevez"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Ouzhpennañ berradennoù Firefox d’ho skramm degemer."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ouzhpennañ berradennoù Ecosia d’ho skramm degemer."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Mont d’an ere eilet"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ivinell digor ebet."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Digeriñ Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Digeriñ Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Klask prevez"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Ouzhpennit ur verradenn Firefox d’ho skramm degemer. Goude bezañ ouzhpennet ar widjet, klikit ha dalc’hit evit kemmañ ha diuzit ur verradenn all."; +"TodayWidget.QuickActionGalleryDescription" = "Ouzhpennit ur verradenn Ecosia d’ho skramm degemer. Goude bezañ ouzhpennet ar widjet, klikit ha dalc’hit evit kemmañ ha diuzit ur verradenn all."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Oberoù prim"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Berradennoù Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Berradennoù Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Ouzhpennit berradennoù betek hoc’h ivinelloù digor."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Alberz prim"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Klask e Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Klask e Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Klask en un ivinell brevez"; diff --git a/firefox-ios/Shared/bs.lproj/Default Browser.strings b/firefox-ios/Shared/bs.lproj/Default Browser.strings index c0e76018c9496..7bebd56413e92 100644 --- a/firefox-ios/Shared/bs.lproj/Default Browser.strings +++ b/firefox-ios/Shared/bs.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Postavljanje Firefoxa kao zadanog pretraživača je glas za otvoren, pristupačan internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Postavljanje Ecosiaa kao zadanog pretraživača je glas za otvoren, pristupačan internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Zadano za bolji internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Saznajte kako"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Postavi automatsko otvaranje linkova web stranica, e-maila i poruka u Firefoxu."; +"DefaultBrowserCard.Description" = "Postavi automatsko otvaranje linkova web stranica, e-maila i poruka u Ecosiau."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Odaberite Firefox kao svoj zadani pretraživač kako biste brzinu, sigurnost i privatnost učinili automatskim."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Odaberite Ecosia kao svoj zadani pretraživač kako biste brzinu, sigurnost i privatnost učinili automatskim."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Unaprijedite svakodnevno pretraživanje"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokira preko 3.000 programa za praćenje po korisniku u prosjeku mjesečno. Učinite nas vašim zadanim pretraživačem za sigurnost privatnosti."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokira preko 3.000 programa za praćenje po korisniku u prosjeku mjesečno. Učinite nas vašim zadanim pretraživačem za sigurnost privatnosti."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ima zaštitu privatnosti"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ima zaštitu privatnosti"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Promijenite svoj zadani pretraživač"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Dodirni Zadana aplikacija pretraživača"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Odaberi Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Odaberi Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Zadana aplikacija pretraživača"; diff --git a/firefox-ios/Shared/bs.lproj/Intro.strings b/firefox-ios/Shared/bs.lproj/Intro.strings index c71f917e71e6c..a58602fb80d91 100644 --- a/firefox-ios/Shared/bs.lproj/Intro.strings +++ b/firefox-ios/Shared/bs.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Prijava"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Dobrodošli u Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Dobrodošli u Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Započnite pretraživanje"; diff --git a/firefox-ios/Shared/bs.lproj/Localizable.strings b/firefox-ios/Shared/bs.lproj/Localizable.strings index 82f201c604535..1e03893452fbd 100644 --- a/firefox-ios/Shared/bs.lproj/Localizable.strings +++ b/firefox-ios/Shared/bs.lproj/Localizable.strings @@ -14,7 +14,7 @@ "Aa" = "Aa"; /* About settings section title */ -"About" = "O Firefoxu"; +"About" = "O Ecosiau"; /* The title for the pinning a shortcut action */ "ActivityStream.ContextMenu.AddToShortcuts" = "Dodaj u prečice"; @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "To može biti pogrešna konfiguracija ili ometanje od strane napadača. Nastavite ako prihvatate potencijalni rizik."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Vlasnik %@ je nepravilno konfigurisao svoju web stranicu. Za zaštitu vaših podataka od krađe, Firefox nije povezao ovu web stranicu."; +"ErrorPages.CertWarning.Description" = "Vlasnik %@ je nepravilno konfigurisao svoju web stranicu. Za zaštitu vaših podataka od krađe, Ecosia nije povezao ovu web stranicu."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ova veza nije povjerljiva"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox početna stranica"; +"Firefox.HomePage.Title" = "Ecosia početna stranica"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Prilagodi početnu stranicu"; @@ -377,7 +377,7 @@ "Forward" = "Naprijed"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox račun"; +"FxA.FirefoxAccount" = "Ecosia račun"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Upravljanje računom i uređajima"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Prijavite se pomoću kamere"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Otvorite Firefox na vašem računaru i posjetite firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Otvorite Ecosia na vašem računaru i posjetite ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Spremno za skeniranje"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sinhronizujte sada"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox sinhronizacija je povezana na %@"; +"FxAPush_DeviceConnected_body" = "Ecosia sinhronizacija je povezana na %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sinhronizacija povezana"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ je uspješno odspojen."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ovaj uređaj je uspješno odspojen iz Firefox sinhronizacije."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ovaj uređaj je uspješno odspojen iz Ecosia sinhronizacije."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sinhronizacija odspojena"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sinhronizacija odspojena"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Uređaj je odspojen iz Firefox sinhronizacije"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Uređaj je odspojen iz Ecosia sinhronizacije"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logo, promijenite pozadinu."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logo, promijenite pozadinu."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomoć"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Prijavite se da vidite listu tabova s vaših ostalih uređaja."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sinhronizacija"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sinhronizacija"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historija"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Saznajte više"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Da biste koristili funkciju automatskog popunjavanja za Firefox, morate imati omogućenu šifru uređaja."; +"Logins.PasscodeRequirement.Warning" = "Da biste koristili funkciju automatskog popunjavanja za Ecosia, morate imati omogućenu šifru uređaja."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ponesite svoje lozinke svugdje"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Automatski popuni Firefoxove lozinke"; +"Logins.WelcomeView.Title2" = "Automatski popuni Ecosiaove lozinke"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Uključi automatsko popunjavanje"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAČUVANE PRIJAVE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Izgleda da se Firefox ranije srušio. Želite li vratit tabove?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Izgleda da se Ecosia ranije srušio. Želite li vratit tabove?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Označi kao pročitano"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Započnite pretraživanje"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! Firefox se srušio"; +"Oops! Firefox crashed" = "Ups! Ecosia se srušio"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Otvorite članke u prikazu za čitanje dodirivanjem ikone knjige kada se pojavi u naslovnoj traci."; @@ -905,7 +905,7 @@ "Open Tabs" = "Otvoreni tabovi"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ne može otvoriti stranicu jer stranica ima netačnu adresu."; +"OpenURL.Error.Message" = "Ecosia ne može otvoriti stranicu jer stranica ima netačnu adresu."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nije moguće otvoriti stranicu"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Ovo vam omogućava da sačuvate sliku u albume."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox želi pristupiti vašim fotografijama"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia želi pristupiti vašim fotografijama"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zatvori"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Podaci su nevažeći"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Idi na ‘Postavke’ uređaja > ‘Firefox’. Dozvoli Firefoxu pristup kameri."; +"ScanQRCode.PermissionError.Message.v100" = "Idi na ‘Postavke’ uređaja > ‘Ecosia’. Dozvoli Ecosiau pristup kameri."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skenirajte QR kod"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Prebaci se na tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox prijedlozi"; +"Search.SuggestSectionTitle.v102" = "Ecosia prijedlozi"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Novi pretraživač će se pojaviti u traci za brzo pretraživanje."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "U redu"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Poslati izvještaj o rušenju tako da Mozilla može riješiti problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Poslati izvještaj o rušenju tako da Ecosia može riješiti problem?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Pošaljite povratnu informaciju"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Pošalji tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Nemate ni jedan drugi uređaj za sinhronizaciju sa ovim Firefox računom."; +"SendTo.NoDevicesFound.Message" = "Nemate ni jedan drugi uređaj za sinhronizaciju sa ovim Ecosia računom."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Molimo otvorite Firefox, idite na Postavke i prijavite se da nastavite."; +"SendTo.NotSignedIn.Message" = "Molimo otvorite Ecosia, idite na Postavke i prijavite se da nastavite."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Niste prijavljeni na vaš Firefox račun."; +"SendTo.NotSignedIn.Title" = "Niste prijavljeni na vaš Ecosia račun."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Pošalji"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Dodirnite za početak"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sinhronizacija"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sinhronizacija"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Prikaz"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Upravljanje podacima"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox će zaustaviti sinhronizaciju s vašim računom, ali neće izbrisati ništa od vaših podataka pretraživanja na ovom uređaju."; +"Settings.Disconnect.Body" = "Ecosia će zaustaviti sinhronizaciju s vašim računom, ali neće izbrisati ništa od vaših podataka pretraživanja na ovom uređaju."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Prekini Sinhronizaciju"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync postavke"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox račun"; +"Settings.FxA.Title" = "Ecosia račun"; /* General settings section title */ "Settings.General.SectionName" = "Opće"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Odaberite šta se prikazuje kao početna stranica."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Odaberite šta je uključeno na Firefox početnoj stranici."; +"Settings.Home.Option.Description.v101" = "Odaberite šta je uključeno na Ecosia početnoj stranici."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Vrati se nazad"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Početna stranica"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Odaberite šta ćete vidjeti kada se vratite u Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Odaberite šta ćete vidjeti kada se vratite u Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Zadnji tab"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Pozadina"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox pozadina, ametist uzorak."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia pozadina, ametist uzorak."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox pozadina, uzorak brda na plaži."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia pozadina, uzorak brda na plaži."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox pozadina, cerulean uzorak."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia pozadina, cerulean uzorak."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Zadana čista pozadina."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox pozadina, uzorak izlaska sunca."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia pozadina, uzorak izlaska sunca."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Prebacivanje ciklusa pozadine početne stranice"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox pozadina, uzorak sumraka."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia pozadina, uzorak sumraka."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasična %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Ograničeno izdanje"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Promijenite pozadinu dodirom na logo Firefox početne stranice"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Promijenite pozadinu dodirom na logo Ecosia početne stranice"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Pozadina"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Prilagođeno"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox početna stranica"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia početna stranica"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Početna stranica"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokiraj slike"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Pri otvaranju Firefoxa"; +"Settings.OfferClipboardBar.Status" = "Pri otvaranju Ecosiaa"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Prilikom otvaranja %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Pri otvaranju Firefoxa"; +"Settings.OfferClipboardBar.StatusV2" = "Pri otvaranju Ecosiaa"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ponuda za otvaranje kopiranih veza"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Saznajte više."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozila nastoji prikupljati samo ono što trebamo da osiguramo i poboljšamo Firefox za sve."; +"Settings.SendUsage.Message" = "Mozila nastoji prikupljati samo ono što trebamo da osiguramo i poboljšamo Ecosia za sve."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Šalji podatke o korištenju"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Otvori novi tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Koristite Siri prečice da brzo otvorite Firefox putem Sirija"; +"Settings.Siri.SectionDescription" = "Koristite Siri prečice da brzo otvorite Ecosia putem Sirija"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri prečice"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Saznajte više."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox može s vremena na vrijeme instalirati i pokrenuti studije."; +"Settings.Studies.Toggle.Message" = "Ecosia može s vremena na vrijeme instalirati i pokrenuti studije."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studije"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Pokreni u pozadini"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Učitavanje u Firefoxu"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Učitavanje u Ecosiau"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Otvori u Firefoxu"; +"ShareExtension.OpenInFirefoxAction.Title" = "Otvori u Ecosiau"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Traži u Firefoxu"; +"ShareExtension.SeachInFirefoxAction.Title" = "Traži u Ecosiau"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiraj"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Zalijepi i idi"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Traži s Firefoxom"; +"UIMenuItem.SearchWithFirefox" = "Traži s Ecosiaom"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "nepročitano"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Juče"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Nemate otvorenih tabova u Firefoxu na vašim drugim uređajima."; +"You don’t have any tabs open in Firefox on your other devices." = "Nemate otvorenih tabova u Ecosiau na vašim drugim uređajima."; /* Your Rights settings section title */ "Your Rights" = "Vaša prava"; diff --git a/firefox-ios/Shared/bs.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/bs.lproj/PrivateBrowsing.strings index 78102aa887e07..0bdd2338cf526 100644 --- a/firefox-ios/Shared/bs.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/bs.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Otvori u novom privatnom tabu"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox neće pamtiti ništa od historije ili kolačića, ali nove zabilješke će biti sačuvane."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia neće pamtiti ništa od historije ili kolačića, ali nove zabilješke će biti sačuvane."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saznajte više"; diff --git a/firefox-ios/Shared/bs.lproj/Today.strings b/firefox-ios/Shared/bs.lproj/Today.strings index 5cd23a6243791..25abffe62554f 100644 --- a/firefox-ios/Shared/bs.lproj/Today.strings +++ b/firefox-ios/Shared/bs.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zatvori\nprivatne tabove"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodajte Firefox prečice na svoj početni ekran."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodajte Ecosia prečice na svoj početni ekran."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Idite na kopirani link"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nema otvorenih tabova."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Otvori Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Otvori Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privatna pretraga"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Dodajte Firefox prečicu na vaš početni ekran. Nakon što dodate widget, dodirnite i držite da ga uredite i odaberete drugu prečicu."; +"TodayWidget.QuickActionGalleryDescription" = "Dodajte Ecosia prečicu na vaš početni ekran. Nakon što dodate widget, dodirnite i držite da ga uredite i odaberete drugu prečicu."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Brze akcije"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox prečice"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia prečice"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Dodajte prečice na otvorene tabove."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Brzi pogled"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Traži u\nFirefoxu"; +"TodayWidget.SearchInFirefoxV2" = "Traži u\nEcosiau"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Traži u\nprivatnom tabu"; diff --git a/firefox-ios/Shared/ca.lproj/Default Browser.strings b/firefox-ios/Shared/ca.lproj/Default Browser.strings index c8eb0b9179173..61d9fb5a2ed60 100644 --- a/firefox-ios/Shared/ca.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ca.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Fer del Firefox el vostre navegador per defecte és un vot a favor d'una Internet oberta i accessible."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Fer del Ecosia el vostre navegador per defecte és un vot a favor d'una Internet oberta i accessible."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Trieu una Internet millor"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Vegeu com fer-ho"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Feu que els enllaços dels llocs web, del correu electrònic i de Missatges s'obrin automàticament en el Firefox."; +"DefaultBrowserCard.Description" = "Feu que els enllaços dels llocs web, del correu electrònic i de Missatges s'obrin automàticament en el Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Trieu el Firefox com a navegador per defecte i obtindreu velocitat, seguretat i privadesa de forma automàtica."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Trieu el Ecosia com a navegador per defecte i obtindreu velocitat, seguretat i privadesa de forma automàtica."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Milloreu la navegació diària"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloca una mitjana de més de 3.000 elements de seguiment per usuari al mes. Feu que sigui el vostre navegador per defecte, per a la vostra tranquil·litat i privadesa."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloca una mitjana de més de 3.000 elements de seguiment per usuari al mes. Feu que sigui el vostre navegador per defecte, per a la vostra tranquil·litat i privadesa."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "El Firefox protegeix la vostra privadesa"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "El Ecosia protegeix la vostra privadesa"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Canvieu el navegador per defecte"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Toqueu «Aplicació de navegador per defecte»"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleccioneu «Firefox»"; +"DefaultBrowserOnboarding.Description3" = "3. Seleccioneu «Ecosia»"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplicació de navegador per defecte"; diff --git a/firefox-ios/Shared/ca.lproj/Intro.strings b/firefox-ios/Shared/ca.lproj/Intro.strings index cf2d87d3e6c1d..656b986024b20 100644 --- a/firefox-ios/Shared/ca.lproj/Intro.strings +++ b/firefox-ios/Shared/ca.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Inicia la sessió"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Us donem la benvinguda al Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Us donem la benvinguda al Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Comença a navegar"; diff --git a/firefox-ios/Shared/ca.lproj/Localizable.strings b/firefox-ios/Shared/ca.lproj/Localizable.strings index ec7176b401d1e..c7a1c396ff5b8 100644 --- a/firefox-ios/Shared/ca.lproj/Localizable.strings +++ b/firefox-ios/Shared/ca.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Pot ser que sigui una mala configuració o una interferència per part d'un atacant. Continueu si accepteu el possible risc."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "El propietari de %@ no ha configurat el lloc web correctament. Per protegir la vostra informació contra robatori, el Firefox no s'ha connectat a aquest lloc web."; +"ErrorPages.CertWarning.Description" = "El propietari de %@ no ha configurat el lloc web correctament. Per protegir la vostra informació contra robatori, el Ecosia no s'ha connectat a aquest lloc web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "No es pot confiar en la connexió"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pàgina d'inici del Firefox"; +"Firefox.HomePage.Title" = "Pàgina d'inici del Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalitza la pàgina d’inici"; @@ -377,7 +377,7 @@ "Forward" = "Endavant"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Compte del Firefox"; +"FxA.FirefoxAccount" = "Compte del Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gestiona el compte i els dispositius"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Inicieu la sessió amb la càmera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Obriu el Firefox a l'ordinador i aneu a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Obriu el Ecosia a l'ordinador i aneu a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Llest per escanejar"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronitza ara"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "El Firefox Sync s'ha connectat a %@"; +"FxAPush_DeviceConnected_body" = "El Ecosia Sync s'ha connectat a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "El Sync s'ha connectat"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ s'ha desconnectat correctament."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Aquest dispositiu s'ha desconnectat correctament del Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Aquest dispositiu s'ha desconnectat correctament del Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "El Sync s'ha desconnectat"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "El Sync s'ha desconnectat"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "S'ha desconnectat un dispositiu del Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "S'ha desconnectat un dispositiu del Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotip del Firefox, canvieu el fons de pantalla."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotip del Ecosia, canvieu el fons de pantalla."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ajuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Inicieu la sessió per veure una llista de les pestanyes que teniu obertes en altres dispositius."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Més informació"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Per utilitzar la funció d'emplenament automàtic per al Firefox, heu de tenir activat un codi del dispositiu."; +"Logins.PasscodeRequirement.Warning" = "Per utilitzar la funció d'emplenament automàtic per al Ecosia, heu de tenir activat un codi del dispositiu."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Accediu a les vostres contrasenyes des de qualsevol lloc"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Emplena automàticament les contrasenyes del Firefox"; +"Logins.WelcomeView.Title2" = "Emplena automàticament les contrasenyes del Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activa l'emplenament automàtic"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "INICIS DE SESSIÓ DESATS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sembla que el Firefox va fallar anteriorment. Voleu restaurar les pestanyes?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sembla que el Ecosia va fallar anteriorment. Voleu restaurar les pestanyes?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marca com a llegit"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Comença a navegar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups. El Firefox ha fallat."; +"Oops! Firefox crashed" = "Ups. El Ecosia ha fallat."; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Obriu articles en la Vista de lectura tocant la icona de llibre quan aparegui a la barra de títol."; @@ -905,7 +905,7 @@ "Open Tabs" = "Pestanyes obertes"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "El Firefox no pot obrir la pàgina perquè l'adreça no és vàlida."; +"OpenURL.Error.Message" = "El Ecosia no pot obrir la pàgina perquè l'adreça no és vàlida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "No es pot obrir la pàgina"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Això permet desar la imatge al vostre Rodet."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "El Firefox vol accedir a les vostres fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "El Ecosia vol accedir a les vostres fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Tanca"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Les dades no són vàlides"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Aneu a «Configuració» > «Firefox». Doneu permís al Firefox per accedir a la càmera."; +"ScanQRCode.PermissionError.Message.v100" = "Aneu a «Configuració» > «Ecosia». Doneu permís al Ecosia per accedir a la càmera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Escaneja un codi QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Canvia a la pestanya"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Suggeriments del Firefox"; +"Search.SuggestSectionTitle.v102" = "Suggeriments del Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "El motor de cerca nou apareixerà a la barra de cerca ràpida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "D'acord"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Voleu enviar un informe de fallada perquè Mozilla pugui solucionar el problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Voleu enviar un informe de fallada perquè Ecosia pugui solucionar el problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Envia comentaris"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Envia la pestanya"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "No hi ha cap altre dispositiu connectat a aquest compte del Firefox disponible per sincronitzar."; +"SendTo.NoDevicesFound.Message" = "No hi ha cap altre dispositiu connectat a aquest compte del Ecosia disponible per sincronitzar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Obriu el Firefox, aneu als Paràmetres i inicieu la sessió per continuar."; +"SendTo.NotSignedIn.Message" = "Obriu el Ecosia, aneu als Paràmetres i inicieu la sessió per continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "No heu iniciat la sessió en el compte del Firefox."; +"SendTo.NotSignedIn.Title" = "No heu iniciat la sessió en el compte del Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Envia"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Toqueu per començar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Mostra"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gestió de dades"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "El Firefox deixarà de sincronitzar-se amb el vostre compte, però les vostres dades de navegació d'aquest dispositiu no se suprimiran."; +"Settings.Disconnect.Body" = "El Ecosia deixarà de sincronitzar-se amb el vostre compte, però les vostres dades de navegació d'aquest dispositiu no se suprimiran."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconnecta del Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Paràmetres de sincronització"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Compte del Firefox"; +"Settings.FxA.Title" = "Compte del Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Trieu què voleu que es mostri com a pantalla d'inici."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Trieu què voleu incloure en la pantalla d'inici del Firefox."; +"Settings.Home.Option.Description.v101" = "Trieu què voleu incloure en la pantalla d'inici del Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Torneu a la pestanya"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pàgina d'inici"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Trieu què voleu veure quan torneu al Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Trieu què voleu veure quan torneu al Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Última pestanya"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fons de pantalla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fons de pantalla del Firefox, patró ametista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fons de pantalla del Ecosia, patró ametista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fons de pantalla del Firefox, patró de dunes de platja."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fons de pantalla del Ecosia, patró de dunes de platja."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fons de pantalla del Firefox, patró ceruli."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fons de pantalla del Ecosia, patró ceruli."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fons de pantalla clar per defecte."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fons de pantalla del Firefox, patró de sortida del sol."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fons de pantalla del Ecosia, patró de sortida del sol."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Commutador per alternar el fons de pantalla de la pàgina d’inici"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fons de pantalla del Firefox, patró de turons al capvespre."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fons de pantalla del Ecosia, patró de turons al capvespre."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clàssic"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edició limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Canvieu el fons de pantalla tocant el logotip de la pàgina d'inici del Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Canvieu el fons de pantalla tocant el logotip de la pàgina d'inici del Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fons de pantalla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalitzat"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Inici del Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Inici del Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pàgina d'inici"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloca les imatges"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "En obrir el Firefox"; +"Settings.OfferClipboardBar.Status" = "En obrir el Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "En obrir el %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "En obrir el Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "En obrir el Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Demana obrir els enllaços copiats"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Més informació."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla s'esforça per recollir només les dades necessàries per oferir i millorar el Firefox per a tothom."; +"Settings.SendUsage.Message" = "Ecosia s'esforça per recollir només les dades necessàries per oferir i millorar el Ecosia per a tothom."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Envia dades d'ús"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Obre una pestanya nova"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilitza dreceres de Siri per obrir ràpidament el Firefox mitjançant Siri"; +"Settings.Siri.SectionDescription" = "Utilitza dreceres de Siri per obrir ràpidament el Ecosia mitjançant Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Dreceres de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Més informació."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "El Firefox pot instal·lar i executar estudis de tant en tant."; +"Settings.Studies.Toggle.Message" = "El Ecosia pot instal·lar i executar estudis de tant en tant."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudis"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Carrega en segon pla"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "S'està carregant al Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "S'està carregant al Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Obre al Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Obre al Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Cerca al Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Cerca al Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copia"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Enganxa i vés-hi"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Cerca amb el Firefox"; +"UIMenuItem.SearchWithFirefox" = "Cerca amb el Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "no llegit"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ahir"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "No teniu cap pestanya oberta al Firefox dels altres dispositius."; +"You don’t have any tabs open in Firefox on your other devices." = "No teniu cap pestanya oberta al Ecosia dels altres dispositius."; /* Your Rights settings section title */ "Your Rights" = "Els vostres drets"; diff --git a/firefox-ios/Shared/ca.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ca.lproj/PrivateBrowsing.strings index f634d35ce72b2..dd7c3afc7a2aa 100644 --- a/firefox-ios/Shared/ca.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ca.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Obre en una pestanya privada nova"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "El Firefox no recordarà el vostre historial ni les galetes, però les adreces d'interès noves sí que es desaran."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "El Ecosia no recordarà el vostre historial ni les galetes, però les adreces d'interès noves sí que es desaran."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Més informació"; @@ -22,6 +22,9 @@ /* Accessibility label for toggling on/off private mode */ "Private Mode" = "Mode privat"; +/* Accessibility label for toggling on/off private mode */ +"PrivateBrowsing.Toggle.A11y.Label.v132" = "Navegació privada"; + /* Accessiblity hint for toggling on/off private mode */ "Turns private mode on or off" = "Activa o desactiva el mode privat"; diff --git a/firefox-ios/Shared/ca.lproj/Today.strings b/firefox-ios/Shared/ca.lproj/Today.strings index adb3dda82fb54..0f4f93e7b8418 100644 --- a/firefox-ios/Shared/ca.lproj/Today.strings +++ b/firefox-ios/Shared/ca.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Tanca les pestanyes privades"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Afegiu dreceres del Firefox a la pantalla d'inici."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Afegiu dreceres del Ecosia a la pantalla d'inici."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Vés a l'enllaç copiat"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No hi ha cap pestanya oberta."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Obre el Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Obre el Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Cerca privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Afegiu una drecera del Firefox a la pantalla d'inici. Després d'afegir el giny, manteniu-lo premut per editar-lo i triar una altra drecera."; +"TodayWidget.QuickActionGalleryDescription" = "Afegiu una drecera del Ecosia a la pantalla d'inici. Després d'afegir el giny, manteniu-lo premut per editar-lo i triar una altra drecera."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Accions ràpides"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Dreceres del Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Dreceres del Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Afegiu dreceres a les pestanyes obertes."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista ràpida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Cerca en el Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Cerca en el Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Cerca en una pestanya privada"; diff --git a/firefox-ios/Shared/co.lproj/Default Browser.strings b/firefox-ios/Shared/co.lproj/Default Browser.strings index a43401140bed0..2851d165fb63f 100644 --- a/firefox-ios/Shared/co.lproj/Default Browser.strings +++ b/firefox-ios/Shared/co.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Fà di Firefox u vostru navigatore predefinitu ghjè vutà per un Internet apertu è accessibile."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Fà di Ecosia u vostru navigatore predefinitu ghjè vutà per un Internet apertu è accessibile."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Scelta d’un Internet più bellu"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Scopre cumu fà"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Definisce chì i liami di i siti web, i currieri elettronichi è i messaghji s’aprinu autumaticamente in Firefox."; +"DefaultBrowserCard.Description" = "Definisce chì i liami di i siti web, i currieri elettronichi è i messaghji s’aprinu autumaticamente in Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Sciglite Firefox cum’è navigatore predefinitu per rende autumatica a vitezza, a sicurità è a cunfidenzialità."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Sciglite Ecosia cum’è navigatore predefinitu per rende autumatica a vitezza, a sicurità è a cunfidenzialità."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Migliurà a navigazione cutidiana"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bluccheghja ogni mese una media di più di 3000 perseguitatori per un utilizatore. Scigliteci cum’è navigatore predefinitu per una vita privata senza penseru."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bluccheghja ogni mese una media di più di 3000 perseguitatori per un utilizatore. Scigliteci cum’è navigatore predefinitu per una vita privata senza penseru."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox s’imprimurisce di a cunfidenzialità"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia s’imprimurisce di a cunfidenzialità"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambià di navigatore predefinitu"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Picchichjà l’appiecazione predefinita di navigazione"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Selezziunà Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Selezziunà Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Appiecazione predefinita di navigazione"; diff --git a/firefox-ios/Shared/co.lproj/Intro.strings b/firefox-ios/Shared/co.lproj/Intro.strings index fa5cabbac0444..b1408596412aa 100644 --- a/firefox-ios/Shared/co.lproj/Intro.strings +++ b/firefox-ios/Shared/co.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Cunnettesi"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Benvenuta in Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Benvenuta in Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Principià a navigazione"; diff --git a/firefox-ios/Shared/co.lproj/Localizable.strings b/firefox-ios/Shared/co.lproj/Localizable.strings index 8054abe741a5b..ced79a3def337 100644 --- a/firefox-ios/Shared/co.lproj/Localizable.strings +++ b/firefox-ios/Shared/co.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Forse ghjè un sbagliu di cunfigurazione o un tentativu d’attaca. Cuntinuate essendu cusciente di u risicu incorsu."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "U pruprietariu di %@ hà cunfiguratu u so situ d’una manera gattiva. Per impedisce chì i vostri dati sianu arrubati, Firefox ùn s’hè micca cunnessu à stu situ."; +"ErrorPages.CertWarning.Description" = "U pruprietariu di %@ hà cunfiguratu u so situ d’una manera gattiva. Per impedisce chì i vostri dati sianu arrubati, Ecosia ùn s’hè micca cunnessu à stu situ."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Sta cunnessione ùn hè micca accertata"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pagina d’accolta di Firefox"; +"Firefox.HomePage.Title" = "Pagina d’accolta di Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Persunalizà a pagina d’accolta"; @@ -377,7 +377,7 @@ "Forward" = "Pagina seguente"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Contu Firefox"; +"FxA.FirefoxAccount" = "Contu Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Amministrà u contu è l’apparechji"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Cunnittitevi cù u vostru apparechju-fotò"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Nant’à u vostru urdinatore, aprite Firefox è andate à firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Nant’à u vostru urdinatore, aprite Ecosia è andate à ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Prontu à analizà"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincrunizà avà"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync s'hè cunnessu à %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync s'hè cunnessu à %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync cunnessu"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ hè statu discunnessu bè."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "St’apparechju hè statu discunnessu bè da Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "St’apparechju hè statu discunnessu bè da Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync discunnessu"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync discunnessu"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un apparechju hè statu discunnessu da Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un apparechju hè statu discunnessu da Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, cambià u sfondu di screnu."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, cambià u sfondu di screnu."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Aiutu"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Cunnittitevi per affissà a lista di l’unghjette di i vostri altri apparechji."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Cronolugia"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Sapene di più"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Per impiegà a funzione di riempiimentu autumaticu di Firefox, ci vole à attivà un codice d’accessu per l’apparechju."; +"Logins.PasscodeRequirement.Warning" = "Per impiegà a funzione di riempiimentu autumaticu di Ecosia, ci vole à attivà un codice d’accessu per l’apparechju."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Purtatevi e vostre parolle d’intesa dapertuttu"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Riempiimentu autumaticu da Firefox di e parolle d’intesa"; +"Logins.WelcomeView.Title2" = "Riempiimentu autumaticu da Ecosia di e parolle d’intesa"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Attivà u riempiimentu autumaticu"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "IDENTIFICAZIONI DI CUNNESSIONE ARREGISTRATE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Pare chì Firefox s’hè lampatu. Vulete risturà e vostre unghjette ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Pare chì Ecosia s’hè lampatu. Vulete risturà e vostre unghjette ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcà cum’è lettu"; @@ -845,7 +845,7 @@ "Menu.TrackingProtectionDescription.CrossSiteNew" = "Quelli canistrelli vi seguitanu da un situ à l’altru per coglie dati nant’à ciò chì vo fate in linea. Sò definiti da terze parti cum’è sucietà di publicità è d’analisa di dati."; /* Description of cryptominers. */ -"Menu.TrackingProtectionDescription.CryptominersNew" = "I minatori di crittomuneta impieganu à l’appiattu a putenza di calculu di u vostru urdinatore per minà muneta virtuale. I so scenarii viotanu a vostra batteria, freneghjanu u vostru urdinatore, è ponu aummantà a vostra fattura d’eletricità."; +"Menu.TrackingProtectionDescription.CryptominersNew" = "I minatori di crittomuneta impieganu à l’appiattu a putenza di calculu di u vostru urdinatore per minà muneta virtuale. I so scenarii viotanu a vostra batteria, freneghjanu u vostru urdinatore, è ponu aumantà a vostra fattura d’elettricità."; /* Description of fingerprinters. */ "Menu.TrackingProtectionDescription.Fingerprinters" = "I parametri di u vostru navigatore et di u vostru apparechju sò unichi. I detettori d’impronta numerica culletteghjanu una mansa di sti parametri unichi per creà un prufilu persunalizatu chì pò dopu esse impiegatu per spiunavvi durante a vostra navigazione."; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Principià a navigazione"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Umbè ! Firefox s’hè lampatu"; +"Oops! Firefox crashed" = "Umbè ! Ecosia s’hè lampatu"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Aprite l’articuli in modu di lettura picchichjendu l’icona di libru quand’ella s’affacca in a barra di titulu."; @@ -905,7 +905,7 @@ "Open Tabs" = "Unghjette aperte"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ùn pò micca apre a pagina perchè hà un indirizzu inaccettevule."; +"OpenURL.Error.Message" = "Ecosia ùn pò micca apre a pagina perchè hà un indirizzu inaccettevule."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Impussibule d’apre a pagina"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Quessu vi permette d’arregistrà a fiura in u vostru Rotulu di filmu."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vole accede à e vostre fotò"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vole accede à e vostre fotò"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Chjode"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "I dati sò inaccetevule"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Andà à « Parametri » > « Firefox » di l’apparechju. Permette à Firefox d’accede à l’apparechju-fotò."; +"ScanQRCode.PermissionError.Message.v100" = "Andà à « Parametri » > « Ecosia » di l’apparechju. Permette à Ecosia d’accede à l’apparechju-fotò."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Analizà un codice QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Passà à l’unghjetta"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox suggerisce"; +"Search.SuggestSectionTitle.v102" = "Ecosia suggerisce"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "U novu mutore di ricerca serà messu in a barra di ricerca rapida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Vai"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mandà un raportu d’accidente per chì Mozilla possa currege u penseru ?"; +"Send a crash report so Mozilla can fix the problem?" = "Mandà un raportu d’accidente per chì Ecosia possa currege u penseru ?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Dà u mo avisu"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Mandà l’unghjetta"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Ùn avete alcunu altru apparechju prontu à esse sincrunizatu è cunnessu à stu contu Firefox."; +"SendTo.NoDevicesFound.Message" = "Ùn avete alcunu altru apparechju prontu à esse sincrunizatu è cunnessu à stu contu Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Ci vole à apre Firefox, andà in Parametri è identificassi per cuntinuà."; +"SendTo.NotSignedIn.Message" = "Ci vole à apre Ecosia, andà in Parametri è identificassi per cuntinuà."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Ùn site micca cunnessu à u vostru contu Firefox."; +"SendTo.NotSignedIn.Title" = "Ùn site micca cunnessu à u vostru contu Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Mandà"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Picchichjà per principià"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Affissà"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Ghjestione di i dati"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ùn si sincruniserà più cù u vostru contu, ma ùn squasserà alcunu datu di navigazione nant’à st’apparechju."; +"Settings.Disconnect.Body" = "Ecosia ùn si sincruniserà più cù u vostru contu, ma ùn squasserà alcunu datu di navigazione nant’à st’apparechju."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Discunnette Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Parametri di sincrunizazione"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Contu Firefox"; +"Settings.FxA.Title" = "Contu Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Generale"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Sciglite ciò chì s’affissa cum’è pagina d’accolta."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Sciglite u cuntenutu di a pagina d’accolta di Firefox."; +"Settings.Home.Option.Description.v101" = "Sciglite u cuntenutu di a pagina d’accolta di Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Rivene à st’unghjetta"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pagina d’accolta"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Sciglite ciò chì si vede quandu vi rivinite à Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Sciglite ciò chì si vede quandu vi rivinite à Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Ultima unghjetta"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Sfondulu di screnu"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Sfondulu di screnu Firefox, mudellu amatista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Sfondulu di screnu Ecosia, mudellu amatista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Sfondulu di screnu di Firefox, mudellu d’un tombulu di rena."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Sfondulu di screnu di Ecosia, mudellu d’un tombulu di rena."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Sfondulu di screnu Firefox, mudellu turchinu azuratu."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Sfondulu di screnu Ecosia, mudellu turchinu azuratu."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Sfondulu chjaru predefinitu."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Sfondulu di screnu Firefox, mudellu assulanatu."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Sfondulu di screnu Ecosia, mudellu assulanatu."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Cummutatore di u ciclu di sfondulu di screnu di a pagina d’accolta"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Sfondulu di screnu di Firefox, mudellu d’un’attrachju marina."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Sfondulu di screnu di Ecosia, mudellu d’un’attrachju marina."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ classicu"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Persunalizatu"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Accolta di Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Accolta di Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "A pagina d’accolta"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bluccà e fiure"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "À l’apertura di Firefox"; +"Settings.OfferClipboardBar.Status" = "À l’apertura di Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "À l’apertura di %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "À l’apertura di Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "À l’apertura di Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Prupone d’apre i liami cupiati"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Sapene di più."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla face casu à cullettà solu i dati chì sò ghjuvevule per rigalà è bunificà Firefox per tutti."; +"Settings.SendUsage.Message" = "Ecosia face casu à cullettà solu i dati chì sò ghjuvevule per rigalà è bunificà Ecosia per tutti."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Mandà i dati d’adopru"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Apre una nova unghjetta"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Impiegà l’accurtatoghji Siri per apre Firefox in furia via Siri"; +"Settings.Siri.SectionDescription" = "Impiegà l’accurtatoghji Siri per apre Ecosia in furia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Accurtatoghji Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Sapene di più."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox pò installà è lancià studii di quandu in quandu."; +"Settings.Studies.Toggle.Message" = "Ecosia pò installà è lancià studii di quandu in quandu."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studii"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Caricà in tacca di fondu"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Caricamentu in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Caricamentu in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Apre in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Apre in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Ricercà in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ricercà in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Cupià"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Incullà è apre"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Ricercà cù Firefox"; +"UIMenuItem.SearchWithFirefox" = "Ricercà cù Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "Micca lettu"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Eri"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Ùn avete alcuna unghjetta aperta in Firefox nant’à i vostri altri apparechji."; +"You don’t have any tabs open in Firefox on your other devices." = "Ùn avete alcuna unghjetta aperta in Ecosia nant’à i vostri altri apparechji."; /* Your Rights settings section title */ "Your Rights" = "Diritti di l’utilizatore"; diff --git a/firefox-ios/Shared/co.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/co.lproj/PrivateBrowsing.strings index b6479b275f955..4d5e1331fe7fb 100644 --- a/firefox-ios/Shared/co.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/co.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Apre in una nova unghjetta privata"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ùn cunserverà alcuna cronolugia nè canistrelli, ma e nove indette seranu arregistrate."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ùn cunserverà alcuna cronolugia nè canistrelli, ma e nove indette seranu arregistrate."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Sapene di più"; @@ -22,6 +22,9 @@ /* Accessibility label for toggling on/off private mode */ "Private Mode" = "Modu di navigazione privata"; +/* Accessibility label for toggling on/off private mode */ +"PrivateBrowsing.Toggle.A11y.Label.v132" = "Navigazione privata"; + /* Accessiblity hint for toggling on/off private mode */ "Turns private mode on or off" = "Attiva o disattiva a navigazione privata"; diff --git a/firefox-ios/Shared/co.lproj/Today.strings b/firefox-ios/Shared/co.lproj/Today.strings index d2863b02a0867..1b80e028b96e4 100644 --- a/firefox-ios/Shared/co.lproj/Today.strings +++ b/firefox-ios/Shared/co.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Chjode\nl’unghjette private"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Aghjunghjite accurtatoghji Firefox à u vostru screnu d’accolta."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Aghjunghjite accurtatoghji Ecosia à u vostru screnu d’accolta."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Apre u liame cupiatu"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Alcuna unghjetta aperta."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Apre Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Apre Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Ricerca privata"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Aghjunghjite un accurtatoghju Firefox à u vostru screnu d’accolta. Dopu l’aghjuntu di u widget, tuccatelu è mantenelu per mudificalu è selezziunate un altru accurtatoghju."; +"TodayWidget.QuickActionGalleryDescription" = "Aghjunghjite un accurtatoghju Ecosia à u vostru screnu d’accolta. Dopu l’aghjuntu di u widget, tuccatelu è mantenelu per mudificalu è selezziunate un altru accurtatoghju."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Azzioni preste"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Accurtatoghji Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Accurtatoghji Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Aghjunghjite accurtatoghji à e vostre unghjette aperte."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista presta"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Ricercà\nin Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Ricercà\nin Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Ricercà in un’\nunghjetta privata"; diff --git a/firefox-ios/Shared/cs.lproj/Default Browser.strings b/firefox-ios/Shared/cs.lproj/Default Browser.strings index 8dac96b7a6186..bc9c31ad74690 100644 --- a/firefox-ios/Shared/cs.lproj/Default Browser.strings +++ b/firefox-ios/Shared/cs.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Nastavení Firefoxu jako výchozího prohlížeče je volbou pro otevřený a přístupný internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Nastavení Ecosiau jako výchozího prohlížeče je volbou pro otevřený a přístupný internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Výchozí nastavení pro lepší internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Zjistit více"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Nastavte si automatické otevírání odkazů, e-mailů a zpráv ve Firefoxu."; +"DefaultBrowserCard.Description" = "Nastavte si automatické otevírání odkazů, e-mailů a zpráv ve Ecosiau."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Zvolte Firefox jako svůj výchozí prohlížeč, aby byla rychlost, bezpečnost a soukromí automatické."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Zvolte Ecosia jako svůj výchozí prohlížeč, aby byla rychlost, bezpečnost a soukromí automatické."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Zvyšte úroveň každodenního prohlížení"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox průměrně blokuje 3 000 sledovacích prvků měsíčně na uživatele. Udělejte si z nás výchozí webový prohlížeč, abyste měli jistotu soukromí."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia průměrně blokuje 3 000 sledovacích prvků měsíčně na uživatele. Udělejte si z nás výchozí webový prohlížeč, abyste měli jistotu soukromí."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox má soukromí pod kontrolou"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia má soukromí pod kontrolou"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Nastavte si svůj výchozí prohlížeč"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Klepněte na „Výchozí prohlížeč“"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Vyberte Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Vyberte Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Výchozí prohlížeč"; diff --git a/firefox-ios/Shared/cs.lproj/Intro.strings b/firefox-ios/Shared/cs.lproj/Intro.strings index 28c194fc2a102..81312dd3a7453 100644 --- a/firefox-ios/Shared/cs.lproj/Intro.strings +++ b/firefox-ios/Shared/cs.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Přihlásit se"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Vítá vás Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Vítá vás Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Začít prohlížet"; diff --git a/firefox-ios/Shared/cs.lproj/Localizable.strings b/firefox-ios/Shared/cs.lproj/Localizable.strings index 2e5c03dcd2b63..811334640b860 100644 --- a/firefox-ios/Shared/cs.lproj/Localizable.strings +++ b/firefox-ios/Shared/cs.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Může to být chybná konfigurace nebo manipulace útočníkem. Pokračujte, pokud příjímáte potenciální riziko."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Majitel webu %@ nakonfiguroval svůj server nesprávně. Abychom chránili vaše údaje před odcizením, Firefox se k tomuto webu nepřipojil."; +"ErrorPages.CertWarning.Description" = "Majitel webu %@ nakonfiguroval svůj server nesprávně. Abychom chránili vaše údaje před odcizením, Ecosia se k tomuto webu nepřipojil."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Toto připojení není důvěryhodné"; @@ -377,7 +377,7 @@ "Forward" = "Vpřed"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Účet Firefoxu"; +"FxA.FirefoxAccount" = "Účet Ecosiau"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Spravovat účet a zařízení"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Přihlásit se pomocí fotoaparátu"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Otevřete Firefox na svém počítači a navštivte firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Otevřete Ecosia na svém počítači a navštivte ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Připraveno na skenování"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synchronizovat"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync byl připojen k zařízení %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync byl připojen k zařízení %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Služba Sync připojena"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Zařízení %@ bylo úspěšně odpojeno."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Toto zařízení bylo úspěšně odpojeno od služby Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Toto zařízení bylo úspěšně odpojeno od služby Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Služba Sync odpojena"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Služba Sync odpojena"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Zařízení bylo odpojeno od služby Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Zařízení bylo odpojeno od služby Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox - klepnutím změní tapetu."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia - klepnutím změní tapetu."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Nápověda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Pro zobrazení seznamu panelů z vašich ostatních zařízení se prosím přihlaste."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historie"; @@ -659,7 +659,7 @@ "Logins.WelcomeView.Tagline" = "Vezměte si svá hesla všude s sebou"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Automatické vyplňování hesel z Firefoxu"; +"Logins.WelcomeView.Title2" = "Automatické vyplňování hesel z Ecosiau"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Zapnout automatické vyplňování"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ULOŽENÉ PŘIHLAŠOVACÍ ÚDAJE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zdá se, že dříve došlo k pádu Firefoxu. Přejete si obnovit své panely?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zdá se, že dříve došlo k pádu Ecosiau. Přejete si obnovit své panely?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Označit jako přečtené"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Začít prohlížet"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Jejda! Firefox spadl"; +"Oops! Firefox crashed" = "Jejda! Ecosia spadl"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Otevírejte články ve čtečce klepnutím na ikonu knihy zobrazené v záhlaví."; @@ -905,7 +905,7 @@ "Open Tabs" = "Otevřené panely"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox nemůže stránku otevřít, protože má stránka neplatnou adresu."; +"OpenURL.Error.Message" = "Ecosia nemůže stránku otevřít, protože má stránka neplatnou adresu."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Stránku nelze otevřít"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Toto vám umožní ukládat obrázky do vaší složky Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox žádá o přístup k vašim Fotografiím"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia žádá o přístup k vašim Fotografiím"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zavřít"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Data nejsou platná"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "V zařízení přejděte do „Nastavení“ > „Firefox“. Povolte Firefoxu přístup k fotoaparátu."; +"ScanQRCode.PermissionError.Message.v100" = "V zařízení přejděte do „Nastavení“ > „Ecosia“. Povolte Ecosiau přístup k fotoaparátu."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skenovat QR kód"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Přepnout na panel"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Návrhy od Firefoxu"; +"Search.SuggestSectionTitle.v102" = "Návrhy od Ecosiau"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nový vyhledávač se zobrazi v panelu rychlého hledání."; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Poslat panel"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "K tomuto účtu Firefoxu nemáte připojena žádná další zařízení dostupná pro synchronizaci."; +"SendTo.NoDevicesFound.Message" = "K tomuto účtu Ecosiau nemáte připojena žádná další zařízení dostupná pro synchronizaci."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Pro pokračování prosím otevřete Firefox, přejděte do Nastavení a přihlaste se."; +"SendTo.NotSignedIn.Message" = "Pro pokračování prosím otevřete Ecosia, přejděte do Nastavení a přihlaste se."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nejste přihlášeni k vašemu účtu Firefoxu."; +"SendTo.NotSignedIn.Title" = "Nejste přihlášeni k vašemu účtu Ecosiau."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Poslat"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Klepněte pro zahájení"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Zobrazit"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Správa dat"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ukončí synchronizaci s vaším účtem, ale nesmaže z tohoto zařízení žádná vaše data."; +"Settings.Disconnect.Body" = "Ecosia ukončí synchronizaci s vaším účtem, ale nesmaže z tohoto zařízení žádná vaše data."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Odpojit Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Nastavení synchronizace"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Účet Firefoxu"; +"Settings.FxA.Title" = "Účet Ecosiau"; /* General settings section title */ "Settings.General.SectionName" = "Obecné"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Vyberte, co se má zobrazit na domovské stránce."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Vyberte, co chcete vidět na domovské stránce Firefoxu."; +"Settings.Home.Option.Description.v101" = "Vyberte, co chcete vidět na domovské stránce Ecosiau."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Návrat zpět"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Domovská stránka"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Vyberte si, co chcete vidět při návratu do Firefoxu."; +"Settings.Home.Option.StartAtHome.Description" = "Vyberte si, co chcete vidět při návratu do Ecosiau."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Poslední panel"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Tapeta"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tapeta Firefoxu, ametystový vzor."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tapeta Ecosiau, ametystový vzor."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tapeta Firefoxu, vzor s pláží a kopci."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tapeta Ecosiau, vzor s pláží a kopci."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tapeta Firefoxu, blankytný vzor."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tapeta Ecosiau, blankytný vzor."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Výchozí prázdná tapeta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tapeta Firefoxu, východ slunce."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tapeta Ecosiau, východ slunce."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Přepínač tapety domovské stránky"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tapeta Firefoxu, vzor s kopci za soumraku."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tapeta Ecosiau, vzor s kopci za soumraku."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasický %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limitovaná edice"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Klepnutím na logo Firefoxu na domovské stránce změníte její tapetu"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Klepnutím na logo Ecosiau na domovské stránce změníte její tapetu"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Tapeta"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokovat obrázky"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "při otevření Firefoxu"; +"Settings.OfferClipboardBar.Status" = "při otevření Ecosiau"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Při otevření %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "při otevření Firefoxu"; +"Settings.OfferClipboardBar.StatusV2" = "při otevření Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Nabízet otevření zkopírovaných adres"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Zjistit více."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla sbírá jenom informace potřené pro vylepšování Firefoxu."; +"Settings.SendUsage.Message" = "Ecosia sbírá jenom informace potřené pro vylepšování Ecosiau."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Odesílat údaje o používání"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Otevřít nový panel"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Použijte zkratky Siri pro rychlé otevření Firefoxu přes Siri"; +"Settings.Siri.SectionDescription" = "Použijte zkratky Siri pro rychlé otevření Ecosiau přes Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Zkratky Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Zjistit více."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox může čas od času instalovat a spouštět studie."; +"Settings.Studies.Toggle.Message" = "Ecosia může čas od času instalovat a spouštět studie."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studie"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Načíst v pozadí"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Načítání ve Firefoxu"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Načítání ve Ecosiau"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Otevřít ve Firefoxu"; +"ShareExtension.OpenInFirefoxAction.Title" = "Otevřít ve Ecosiau"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Vyhledat ve Firefoxu"; +"ShareExtension.SeachInFirefoxAction.Title" = "Vyhledat ve Ecosiau"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopírovat"; diff --git a/firefox-ios/Shared/cs.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/cs.lproj/PrivateBrowsing.strings index cb0e1cf5639e2..1332d55d215f7 100644 --- a/firefox-ios/Shared/cs.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/cs.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Otevřít v anonymním panelu"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox si nebude pamatovat vaši historii ani ukládat cookies, ale vytvořené záložky budou uloženy."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia si nebude pamatovat vaši historii ani ukládat cookies, ale vytvořené záložky budou uloženy."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Zjistit více"; diff --git a/firefox-ios/Shared/cs.lproj/Today.strings b/firefox-ios/Shared/cs.lproj/Today.strings index 2a3aae7201b1a..70d437d408e5b 100644 --- a/firefox-ios/Shared/cs.lproj/Today.strings +++ b/firefox-ios/Shared/cs.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zavřít anonymní panely"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Přidejte si zkratku Firefoxu na domovskou obrazovku."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Přidejte si zkratku Ecosiau na domovskou obrazovku."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Přejít na zkopírovaný odkaz"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Žádné otevřené panely."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Otevřít Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Otevřít Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Soukromé vyhledávání"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Přidejte si zkratku Firefoxu na domovskou obrazovku. Pro úpravu nebo výběr jiné zkratky na widgetu podržte prst."; +"TodayWidget.QuickActionGalleryDescription" = "Přidejte si zkratku Ecosiau na domovskou obrazovku. Pro úpravu nebo výběr jiné zkratky na widgetu podržte prst."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Rychlé akce"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Zkratky Firefoxu"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Zkratky Ecosiau"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Přidejte si zkratky na vaše otevřené panely."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Rychlý pohled"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Vyhledat ve Firefoxu"; +"TodayWidget.SearchInFirefoxV2" = "Vyhledat ve Ecosiau"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Vyhledat v anonymním panelu"; diff --git a/firefox-ios/Shared/cy.lproj/Default Browser.strings b/firefox-ios/Shared/cy.lproj/Default Browser.strings index dcfe7ab0258ab..c5eb09ca7897f 100644 --- a/firefox-ios/Shared/cy.lproj/Default Browser.strings +++ b/firefox-ios/Shared/cy.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Mae gwneud Firefox yn borwr rhagosodedig yn bleidlais o blaid rhyngrwyd agored, hygyrch."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Mae gwneud Ecosia yn borwr rhagosodedig yn bleidlais o blaid rhyngrwyd agored, hygyrch."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Rhagosod i Rhyngrwyd Gwell"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Dysgu Sut"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Gosod dolenni o wefannau, e-byst, a Messages i agor yn awtomatig yn Firefox."; +"DefaultBrowserCard.Description" = "Gosod dolenni o wefannau, e-byst, a Messages i agor yn awtomatig yn Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Dewiswch Firefox fel eich porwr rhagosodedig a gwneud cyflymder, diogelwch a phreifatrwydd yn nodweddion awtomatig."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Dewiswch Ecosia fel eich porwr rhagosodedig a gwneud cyflymder, diogelwch a phreifatrwydd yn nodweddion awtomatig."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Uwchraddio Pori Bob Dydd"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ar gyfartaledd, mae Firefox yn rhwystro dros 3,000 o dracwyr fesul defnyddiwr bob mis. Gwnewch ni eich porwr rhagosodedig ar gyfer tawelwch meddwl preifatrwydd."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ar gyfartaledd, mae Ecosia yn rhwystro dros 3,000 o dracwyr fesul defnyddiwr bob mis. Gwnewch ni eich porwr rhagosodedig ar gyfer tawelwch meddwl preifatrwydd."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Mae Firefox yn Sicrhau Preifatrwydd"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Mae Ecosia yn Sicrhau Preifatrwydd"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Newid Eich Porwr Rhagosodedig"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tapio Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Dewis Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Dewis Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Ap Porwr Rhagosodedig"; diff --git a/firefox-ios/Shared/cy.lproj/Intro.strings b/firefox-ios/Shared/cy.lproj/Intro.strings index 3a8abb3ec2903..f1f6271b639e5 100644 --- a/firefox-ios/Shared/cy.lproj/Intro.strings +++ b/firefox-ios/Shared/cy.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Mewngofnodi"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Croeso i Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Croeso i Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Cychwyn Pori"; diff --git a/firefox-ios/Shared/cy.lproj/Localizable.strings b/firefox-ios/Shared/cy.lproj/Localizable.strings index 38ebd442a07d6..97c0637d64e1d 100644 --- a/firefox-ios/Shared/cy.lproj/Localizable.strings +++ b/firefox-ios/Shared/cy.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Gall fod yn un ai cam ffurfweddu neu ymyrraeth gan ymosodwr. Parhewch os ydych yn derbyn y risg posib."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Mae perchennog %@ wedi cam ffurfweddu eu gwefan. I rwystro eich manylion rhag cael eu dwyn, nid yw Firefox wedi cysylltu â'r wefan hon."; +"ErrorPages.CertWarning.Description" = "Mae perchennog %@ wedi cam ffurfweddu eu gwefan. I rwystro eich manylion rhag cael eu dwyn, nid yw Ecosia wedi cysylltu â'r wefan hon."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Dim Ymddiriedaeth i'r Cysylltiad"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Tudalen Cartref Firefox"; +"Firefox.HomePage.Title" = "Tudalen Cartref Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Cyfaddasu’r Dudalen Cartref"; @@ -377,7 +377,7 @@ "Forward" = "Ymlaen"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cyfrif Firefox"; +"FxA.FirefoxAccount" = "Cyfrif Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Rheoli Cyfrif a Dyfeisiau"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Mewngofnodi gyda’ch camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Ar eich cyfrifiadur, agorwch Firefox ac ewch i firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Ar eich cyfrifiadur, agorwch Ecosia ac ewch i ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Yn Barod i Sganio"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Cydweddu Nawr"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Mae Firefox Sync wedi cysylltu i %@"; +"FxAPush_DeviceConnected_body" = "Mae Ecosia Sync wedi cysylltu i %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Mae Sync wedi Cysylltu"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Mae %@ wedi ei ddatgysylltu'n llwyddiannus."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Mae'r ddyfais wedi'i ddatgysylltu'n llwyddiannus o Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Mae'r ddyfais wedi'i ddatgysylltu'n llwyddiannus o Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Datgysylltodd Sync"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Datgysylltodd Sync"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Mae'r ddyfais wedi'i ddatgysylltu o Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Mae'r ddyfais wedi'i ddatgysylltu o Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, newid y papur wal."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, newid y papur wal."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Cymorth"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Mewngofnodwch i weld rhestr o dabiau o'ch dyfeisiau eraill."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Hanes"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Dysgu Rhagor"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "I ddefnyddio nodwedd AwtoLanw Firefox, mae'n rhaid bod gennych god pas dyfais wedi'i alluogi."; +"Logins.PasscodeRequirement.Warning" = "I ddefnyddio nodwedd AwtoLanw Ecosia, mae'n rhaid bod gennych god pas dyfais wedi'i alluogi."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ewch â'ch cyfrineiriau i bob man"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Cyfrineiriau AwtoLanw Firefox"; +"Logins.WelcomeView.Title2" = "Cyfrineiriau AwtoLanw Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Troi AwtoLanw Ymlaen"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "MEWNGOFNODION WEDI'U CADW"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Bosib fod Firefox wedi chwalu y tro diwethaf. Hoffech chi adfer eich tabiau?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Bosib fod Ecosia wedi chwalu y tro diwethaf. Hoffech chi adfer eich tabiau?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcio fel wedi ei Darllen"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Cychwyn Pori"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Wps! Mae Firefox wedi chwalu"; +"Oops! Firefox crashed" = "Wps! Mae Ecosia wedi chwalu"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Agorwch erthyglau yn y Golwg Darllen drwy dapio ar yr eicon llyfr pan mae'n ymddangos yn y bar teitl."; @@ -905,7 +905,7 @@ "Open Tabs" = "Agor Tabiau"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Nid yw Firefox yn gallu agor y dudalen gan fod ganddo gyfeiriad annilys."; +"OpenURL.Error.Message" = "Nid yw Ecosia yn gallu agor y dudalen gan fod ganddo gyfeiriad annilys."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Methu Agor Tudalen"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Mae hyn yn caniatáu i chi gadw'r ddelwedd i'ch Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Hoffai Firefox gael mynediad at eich Lluniau"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Hoffai Ecosia gael mynediad at eich Lluniau"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Cau"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Mae'r data'n annilys"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ewch i ‘Gosodiadau’ > ‘Firefox’ y ddyfais. Caniatewch i Firefox gael mynediad i’r camera."; +"ScanQRCode.PermissionError.Message.v100" = "Ewch i ‘Gosodiadau’ > ‘Ecosia’ y ddyfais. Caniatewch i Ecosia gael mynediad i’r camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Sganiwch y Cod QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Newid i'r tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Awgrymadau Firefox"; +"Search.SuggestSectionTitle.v102" = "Awgrymadau Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Bydd y peiriant chwilio newydd yn ymddangos yn y bar chwilio cyflym."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Iawn"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Anfon adroddiad chwalu fel bod modd i Mozilla drwsio'r anhawster?"; +"Send a crash report so Mozilla can fix the problem?" = "Anfon adroddiad chwalu fel bod modd i Ecosia drwsio'r anhawster?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Anfon Adborth"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Anfon Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Nid oes gennych unrhyw ddyfeisiau eraill sy'n gysylltiedig â'r Cyfrif Firefox sydd ar gael i'w gydweddu."; +"SendTo.NoDevicesFound.Message" = "Nid oes gennych unrhyw ddyfeisiau eraill sy'n gysylltiedig â'r Cyfrif Ecosia sydd ar gael i'w gydweddu."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Agorwch Firefox, ewch i'r Gosodiadau a mewngofnodi i barhau."; +"SendTo.NotSignedIn.Message" = "Agorwch Ecosia, ewch i'r Gosodiadau a mewngofnodi i barhau."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nid ydych wedi eich mewngofnodi i'ch Cyfrif Firefox."; +"SendTo.NotSignedIn.Title" = "Nid ydych wedi eich mewngofnodi i'ch Cyfrif Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Anfon"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tapio i gychwyn"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Golwg"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Rheoli Data"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Bydd Firefox yn peidio cydweddu eich cyfrif ond ni fydd yn dileu eich data pori ar y ddyfais hon."; +"Settings.Disconnect.Body" = "Bydd Ecosia yn peidio cydweddu eich cyfrif ond ni fydd yn dileu eich data pori ar y ddyfais hon."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Datgysylltu Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Gosodiadau Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cyfrif Firefox"; +"Settings.FxA.Title" = "Cyfrif Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Cyffredinol"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Dewis beth yw eich tudalen cartref."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Dewis beth sydd ar dudalen cartref Firefox."; +"Settings.Home.Option.Description.v101" = "Dewis beth sydd ar dudalen cartref Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Mynd Nôl I"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Tudalen Cartref"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Dewis beth rydych yn ei weld pan fyddwch yn dychwelyd i Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Dewis beth rydych yn ei weld pan fyddwch yn dychwelyd i Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Y tab diwethaf"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Papur Wal"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Papur wal Firefox, patrwm amethyst."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Papur wal Ecosia, patrwm amethyst."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Papur wal Firefox, patrwm bryniau traethog."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Papur wal Ecosia, patrwm bryniau traethog."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Papur wal Firefox, patrwm ceruleaidd."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Papur wal Ecosia, patrwm ceruleaidd."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Papur wal clir rhagosodedig."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Papur wal Firefox, patrwm gwawrio."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Papur wal Ecosia, patrwm gwawrio."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Togl cylchu papur wal tudalen cartref"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Papur wal Firefox, patrwm bryniau'r gwyll."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Papur wal Ecosia, patrwm bryniau'r gwyll."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Clasurol %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Fersiynau Cyfyngedig"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Newidiwch y papur wal trwy dapio logo tudalen cartref Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Newidiwch y papur wal trwy dapio logo tudalen cartref Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Papur Wal"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Cyfaddasu"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Cartref Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Cartref Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Tudalen Cartref"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Rhwystro Delweddau"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Wrth Agor Firefox"; +"Settings.OfferClipboardBar.Status" = "Wrth Agor Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Wrth agor %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Wrth Agor Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Wrth Agor Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Cynnig Agor Dolenni Wedi'u Copïo"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Dysgu Rhagor."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mae Mozilla'n ceisio casglu dim ond yr hyn sydd ei angen arnom i ddarparu a gwella Firefox ar gyfer pawb."; +"Settings.SendUsage.Message" = "Mae Ecosia'n ceisio casglu dim ond yr hyn sydd ei angen arnom i ddarparu a gwella Ecosia ar gyfer pawb."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Anfon Data Defnydd"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Agor Tab Newydd"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Defnyddio llwybrau byr Siri ni agor Firefox yn sydyn drwy Siri"; +"Settings.Siri.SectionDescription" = "Defnyddio llwybrau byr Siri ni agor Ecosia yn sydyn drwy Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Llwybrau Byr Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Dysgu Rhagor."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Gall Firefox osod a rhedeg astudiaethau o bryd i'w gilydd."; +"Settings.Studies.Toggle.Message" = "Gall Ecosia osod a rhedeg astudiaethau o bryd i'w gilydd."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Astudiaethau"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Llwytho yn y Cefndir"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Llwytho yn Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Llwytho yn Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Agor yn Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Agor yn Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Chwilio yn Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Chwilio yn Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copïo"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Gludo a Mynd"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Chwilio gyda Firefox"; +"UIMenuItem.SearchWithFirefox" = "Chwilio gyda Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "heb ei ddarllen"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ddoe"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Nid oes gennych unrhyw dabiau ar agor yn Firefox ar eich dyfeisiau eraill."; +"You don’t have any tabs open in Firefox on your other devices." = "Nid oes gennych unrhyw dabiau ar agor yn Ecosia ar eich dyfeisiau eraill."; /* Your Rights settings section title */ "Your Rights" = "Eich Hawliau"; diff --git a/firefox-ios/Shared/cy.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/cy.lproj/PrivateBrowsing.strings index 4566475ea1383..b3e5711b1909a 100644 --- a/firefox-ios/Shared/cy.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/cy.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Agor mewn Tab Preifat Newydd"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Fydd Firefox ddim yn cofio unrhyw hanes na chwcis ond bydd nodau tudalen newydd yn cael eu cadw."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Fydd Ecosia ddim yn cofio unrhyw hanes na chwcis ond bydd nodau tudalen newydd yn cael eu cadw."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Dysgu Rhagor"; diff --git a/firefox-ios/Shared/cy.lproj/Today.strings b/firefox-ios/Shared/cy.lproj/Today.strings index 06f4fa07e710a..4545518675239 100644 --- a/firefox-ios/Shared/cy.lproj/Today.strings +++ b/firefox-ios/Shared/cy.lproj/Today.strings @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Dim tabiau agored"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Agor Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Agor Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Chwilio Preifat"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Ychwanegwch llwybr byr Firefox i'ch sgrin Cartref. Ar ôl ychwanegu'r teclyn, cyffwrdd a dal i'w olygu a dewis llwybr byr gwahanol."; +"TodayWidget.QuickActionGalleryDescription" = "Ychwanegwch llwybr byr Ecosia i'ch sgrin Cartref. Ar ôl ychwanegu'r teclyn, cyffwrdd a dal i'w olygu a dewis llwybr byr gwahanol."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Gweithredoedd Cyflym"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Llwybrau Byr Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Llwybrau Byr Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Ychwanegwch lwybrau byr i'ch tabiau agored."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Golwg Gyflym"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Chwilio yn Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Chwilio yn Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Chwilio mewn Tab Preifat"; diff --git a/firefox-ios/Shared/da.lproj/Default Browser.strings b/firefox-ios/Shared/da.lproj/Default Browser.strings index 88cb990e51843..d5d395f14e980 100644 --- a/firefox-ios/Shared/da.lproj/Default Browser.strings +++ b/firefox-ios/Shared/da.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "At gøre Firefox til din standard-browser er en stemme for et åbent og tilgængeligt internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "At gøre Ecosia til din standard-browser er en stemme for et åbent og tilgængeligt internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard for et bedre internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Læs hvordan"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Indstil links fra websteder, mails og beskeder til automatisk at blive åbnet i Firefox."; +"DefaultBrowserCard.Description" = "Indstil links fra websteder, mails og beskeder til automatisk at blive åbnet i Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Vælg Firefox som din standard-browser for automatisk at få hastighed, sikkerhed og beskyttelse af dit privatliv."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Vælg Ecosia som din standard-browser for automatisk at få hastighed, sikkerhed og beskyttelse af dit privatliv."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Bedre daglig browsing"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokerer i gennemsnit over 3.000 sporingsmekanismer per bruger om måneden. Gør os til din standard-browser for at beskytte dit privatliv."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokerer i gennemsnit over 3.000 sporingsmekanismer per bruger om måneden. Gør os til din standard-browser for at beskytte dit privatliv."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox beskytter dit privatliv"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia beskytter dit privatliv"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Skift din standardbrowser"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tryk på Standardapp til browsing"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Vælg Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Vælg Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standardapp til browsing"; diff --git a/firefox-ios/Shared/da.lproj/Intro.strings b/firefox-ios/Shared/da.lproj/Intro.strings index 257f391cd110b..2b77f20d1d485 100644 --- a/firefox-ios/Shared/da.lproj/Intro.strings +++ b/firefox-ios/Shared/da.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Log ind"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Velkommen til Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Velkommen til Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Gå i gang"; diff --git a/firefox-ios/Shared/da.lproj/Localizable.strings b/firefox-ios/Shared/da.lproj/Localizable.strings index b37c2770c7964..7a4eb962c1f5c 100644 --- a/firefox-ios/Shared/da.lproj/Localizable.strings +++ b/firefox-ios/Shared/da.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Det kan skyldes en forkert opsætning eller at nogen manipulerer din forbindelse. Fortsæt kun, hvis du kan acceptere den mulige risiko."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Ejeren af %@ har opsat sit websted forkert. Firefox har ikke oprettet forbindelse til webstedet for at forhindre, at dine informationer bliver stjålet."; +"ErrorPages.CertWarning.Description" = "Ejeren af %@ har opsat sit websted forkert. Ecosia har ikke oprettet forbindelse til webstedet for at forhindre, at dine informationer bliver stjålet."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Webstedets identifikation kunne ikke bekræftes"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox startside"; +"Firefox.HomePage.Title" = "Ecosia startside"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Tilpas startside"; @@ -377,7 +377,7 @@ "Forward" = "Fremad"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-konto"; +"FxA.FirefoxAccount" = "Ecosia-konto"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Håndter konto og enheder"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Log ind med dit kamera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Åbn Firefox på din computer og gå til firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Åbn Ecosia på din computer og gå til ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Klar til at skanne"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synkroniser nu"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync er forbundet til %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync er forbundet til %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync forbundet"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Forbindelsen til %@ er blevet afbrudt."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Forbindelsen mellem denne enhed og Firefox Sync er blevet afbrudt."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Forbindelsen mellem denne enhed og Ecosia Sync er blevet afbrudt."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync er blevet afbrudt"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync er blevet afbrudt"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Forbindelsen mellem en enhed og Firefox Sync er blevet afbrudt"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Forbindelsen mellem en enhed og Ecosia Sync er blevet afbrudt"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-logo, skift baggrund."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-logo, skift baggrund."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Hjælp"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Log ind for at se en liste med faneblade fra dine andre enheder."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historik"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Læs mere"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "For at bruge funktionen Autoudfyld for Firefox skal du have en enhedsadgangskode aktiveret."; +"Logins.PasscodeRequirement.Warning" = "For at bruge funktionen Autoudfyld for Ecosia skal du have en enhedsadgangskode aktiveret."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Tag dine adgangskoder med overalt"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autoudfyld adgangskoder i Firefox"; +"Logins.WelcomeView.Title2" = "Autoudfyld adgangskoder i Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Slå Autoudfyld til"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "GEMTE LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Det ser ud som om, Firefox tidligere gik ned. Vil du gendanne dine faneblade?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Det ser ud som om, Ecosia tidligere gik ned. Vil du gendanne dine faneblade?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marker som læst"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Gå i gang"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hov! Firefox gik ned"; +"Oops! Firefox crashed" = "Hov! Ecosia gik ned"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Åbn artikler i læsevisning ved at trykke på bog-ikonet, når det vises i titellinjen."; @@ -905,7 +905,7 @@ "Open Tabs" = "Åbne faneblade"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kan ikke åbne siden, fordi den har en ugyldig adresse."; +"OpenURL.Error.Message" = "Ecosia kan ikke åbne siden, fordi den har en ugyldig adresse."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Siden kan ikke åbnes"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Dette lader dig gemme billedet til dine fotos."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vil gerne have adgang til dine fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vil gerne have adgang til dine fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Luk"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Data er ugyldige"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Gå til enhedens \"Indstillinger\" > \"Firefox\", og giv Firefox adgang til kameraet."; +"ScanQRCode.PermissionError.Message.v100" = "Gå til enhedens \"Indstillinger\" > \"Ecosia\", og giv Ecosia adgang til kameraet."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skan QR-kode"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Skift til faneblad"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-forslag"; +"Search.SuggestSectionTitle.v102" = "Ecosia-forslag"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Den nye søgetjeneste vil blive vist i linjen \"Hurtig søgning\""; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Vil du sende en fejlrapport, så Mozilla kan løse problemet?"; +"Send a crash report so Mozilla can fix the problem?" = "Vil du sende en fejlrapport, så Ecosia kan løse problemet?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Send feedback"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Send faneblad"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Du har ikke andre enheder tilknyttet denne Firefox-konto, som er tilgængelige for synkronisering."; +"SendTo.NoDevicesFound.Message" = "Du har ikke andre enheder tilknyttet denne Ecosia-konto, som er tilgængelige for synkronisering."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Åbn Firefox, gå til Indstillinger og log ind for at fortsætte."; +"SendTo.NotSignedIn.Message" = "Åbn Ecosia, gå til Indstillinger og log ind for at fortsætte."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Du er ikke logget ind på din Firefox-konto."; +"SendTo.NotSignedIn.Title" = "Du er ikke logget ind på din Ecosia-konto."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tryk for at begynde"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vis"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Håndtering af data"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox vil ikke længere synkronisere med din konto, men sletter ikke dine browsing-data på denne enhed."; +"Settings.Disconnect.Body" = "Ecosia vil ikke længere synkronisere med din konto, men sletter ikke dine browsing-data på denne enhed."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Afbryd forbindelse"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Indstillinger for synkronisering"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-konto"; +"Settings.FxA.Title" = "Ecosia-konto"; /* General settings section title */ "Settings.General.SectionName" = "Generelt"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Vælg, hvad der skal vises som startside."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Vælg, hvad startsiden i Firefox skal vise."; +"Settings.Home.Option.Description.v101" = "Vælg, hvad startsiden i Ecosia skal vise."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Hop tilbage til"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startside"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Vælg, hvad der vises, når du vender tilbage til Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Vælg, hvad der vises, når du vender tilbage til Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Seneste faneblad"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Baggrund"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-baggrund, ametyst-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-baggrund, ametyst-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-baggrund, strandbakker-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-baggrund, strandbakker-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-baggrund, himmelblåt mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-baggrund, himmelblåt mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standard-baggrund."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox-baggrund, solopgangs-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia-baggrund, solopgangs-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Knap til at skifte baggrund på startsiden"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-baggrund, skumringsbakker-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-baggrund, skumringsbakker-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassisk %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Begrænset udgave"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Skift baggrund ved at trykke på Firefox-logoet på startsiden"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Skift baggrund ved at trykke på Ecosia-logoet på startsiden"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Baggrund"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Tilpasset"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox' startside"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia' startside"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startside"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloker billeder"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Når Firefox bliver åbnet"; +"Settings.OfferClipboardBar.Status" = "Når Ecosia bliver åbnet"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Når du åbner %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Når Firefox bliver åbnet"; +"Settings.OfferClipboardBar.StatusV2" = "Når Ecosia bliver åbnet"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Tilbyd at åbne kopierede links"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Læs mere."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla stræber efter kun at indsamle, hvad vi skal bruge for at kunne tilbyde og forbedre Firefox for alle."; +"Settings.SendUsage.Message" = "Ecosia stræber efter kun at indsamle, hvad vi skal bruge for at kunne tilbyde og forbedre Ecosia for alle."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send data for anvendelse"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Åbn et nyt faneblad"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Brug genveje i Siri til hurtigt at åbne Firefox"; +"Settings.Siri.SectionDescription" = "Brug genveje i Siri til hurtigt at åbne Ecosia"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-genveje"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Læs mere."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox kan installere og afvikle undersøgelser."; +"Settings.Studies.Toggle.Message" = "Ecosia kan installere og afvikle undersøgelser."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Undersøgelser"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Indlæs i baggrunden"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Indlæses i Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Indlæses i Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Åbn i Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Åbn i Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Søg i Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Søg i Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopier"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Indsæt og gå"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Søg med Firefox"; +"UIMenuItem.SearchWithFirefox" = "Søg med Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ulæst"; @@ -1793,7 +1793,7 @@ "Yesterday" = "I går"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Du har ikke nogle åbne faneblade i Firefox på dine andre enheder."; +"You don’t have any tabs open in Firefox on your other devices." = "Du har ikke nogle åbne faneblade i Ecosia på dine andre enheder."; /* Your Rights settings section title */ "Your Rights" = "Dine rettigheder"; diff --git a/firefox-ios/Shared/da.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/da.lproj/PrivateBrowsing.strings index ec3b2a47c5da1..095e869d7b34f 100644 --- a/firefox-ios/Shared/da.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/da.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Åbn i nyt privat faneblad"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox gemmer ikke din historik eller cookies, men nye bogmærker vil blive gemt."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia gemmer ikke din historik eller cookies, men nye bogmærker vil blive gemt."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Læs mere"; diff --git a/firefox-ios/Shared/da.lproj/Today.strings b/firefox-ios/Shared/da.lproj/Today.strings index 7c9eacd48a800..3723f3c18a2f7 100644 --- a/firefox-ios/Shared/da.lproj/Today.strings +++ b/firefox-ios/Shared/da.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Luk private faneblade"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Tilføj Firefox-genveje til din startskærm."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Tilføj Ecosia-genveje til din startskærm."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Gå til kopieret link"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ingen åbne faneblade."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Åbn Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Åbn Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privat søgning"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Tilføj en Firefox-genvej til din startskærm. Efter at du har tilføjet en ny widget, skal du trykke og holde for at redigere den og vælge en anden genvej."; +"TodayWidget.QuickActionGalleryDescription" = "Tilføj en Ecosia-genvej til din startskærm. Efter at du har tilføjet en ny widget, skal du trykke og holde for at redigere den og vælge en anden genvej."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Hurtige handlinger"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-genveje"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-genveje"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Tilføj genveje til dine åbne faneblade."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Hurtigt overblik"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Søg i Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Søg i Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Søg i private faneblade"; diff --git a/firefox-ios/Shared/de.lproj/Default Browser.strings b/firefox-ios/Shared/de.lproj/Default Browser.strings index 854e6796c154a..66f5394d3ac98 100644 --- a/firefox-ios/Shared/de.lproj/Default Browser.strings +++ b/firefox-ios/Shared/de.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox zu Ihrem Standardbrowser zu machen, ist eine Stimme für ein offenes, zugängliches Internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia zu Ihrem Standardbrowser zu machen, ist eine Stimme für ein offenes, zugängliches Internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard für ein besseres Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Erfahren Sie, wie es geht"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Stellen Sie Links von Websites, E-Mails und Nachrichten so ein, dass sie in Firefox automatisch geöffnet werden."; +"DefaultBrowserCard.Description" = "Stellen Sie Links von Websites, E-Mails und Nachrichten so ein, dass sie in Ecosia automatisch geöffnet werden."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Wählen Sie Firefox als Ihren Standardbrowser, um Geschwindigkeit, Sicherheit und Datenschutz automatisch zu erhalten."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Wählen Sie Ecosia als Ihren Standardbrowser, um Geschwindigkeit, Sicherheit und Datenschutz automatisch zu erhalten."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Tägliches Surfen – noch besser"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blockiert durchschnittlich über 3000 Tracker pro Nutzer pro Monat. Machen Sie uns zu Ihrem Standardbrowser, um die Privatsphäre zu schützen."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blockiert durchschnittlich über 3000 Tracker pro Nutzer pro Monat. Machen Sie uns zu Ihrem Standardbrowser, um die Privatsphäre zu schützen."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox schützt Ihre Privatsphäre"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia schützt Ihre Privatsphäre"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Ihren Standardbrowser wechseln"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Standard-Browser-App antippen"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox auswählen"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia auswählen"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standard-Browser-App"; diff --git a/firefox-ios/Shared/de.lproj/Intro.strings b/firefox-ios/Shared/de.lproj/Intro.strings index 56f9c239629bb..d0af97cba1ece 100644 --- a/firefox-ios/Shared/de.lproj/Intro.strings +++ b/firefox-ios/Shared/de.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Anmelden"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Willkommen bei Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Willkommen bei Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Lossurfen"; diff --git a/firefox-ios/Shared/de.lproj/Localizable.strings b/firefox-ios/Shared/de.lproj/Localizable.strings index 073418b0eab5b..5ab316721bd87 100644 --- a/firefox-ios/Shared/de.lproj/Localizable.strings +++ b/firefox-ios/Shared/de.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Dies kann eine Fehlkonfiguration oder eine Manipulation eines Angreifers sein. Fahren Sie fort, wenn Sie das potenzielle Risiko akzeptieren."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Der Besitzer von %@ hat seine Website falsch konfiguriert. Um Diebstahl Ihrer Daten zu verhindern, verweigert Firefox die Verbindung zu dieser Website."; +"ErrorPages.CertWarning.Description" = "Der Besitzer von %@ hat seine Website falsch konfiguriert. Um Diebstahl Ihrer Daten zu verhindern, verweigert Ecosia die Verbindung zu dieser Website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Dieser Verbindung wird nicht vertraut"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox-Startseite"; +"Firefox.HomePage.Title" = "Ecosia-Startseite"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Startbildschirm anpassen"; @@ -377,7 +377,7 @@ "Forward" = "Vorwärts"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-Konto"; +"FxA.FirefoxAccount" = "Ecosia-Konto"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Konto & Geräte verwalten"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Melden Sie sich mit Ihrer Kamera an"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Öffnen Sie Firefox auf Ihrem Computer und besuchen Sie firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Öffnen Sie Ecosia auf Ihrem Computer und besuchen Sie ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Bereit zum Scannen"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Jetzt synchronisieren"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync ist mit %@ verbunden"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync ist mit %@ verbunden"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync verbunden"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ wurde erfolgreich getrennt."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Dieses Gerät wurde erfolgreich von Firefox Sync getrennt."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Dieses Gerät wurde erfolgreich von Ecosia Sync getrennt."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync getrennt"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync getrennt"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ein Gerät wurde von Firefox Sync getrennt"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ein Gerät wurde von Ecosia Sync getrennt"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-Logo, Hintergrundbild ändern."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-Logo, Hintergrundbild ändern."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Hilfe"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Melden Sie sich an, um eine Liste der Tabs auf Ihren anderen Geräten zu sehen."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Chronik"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Weitere Informationen"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Um die Funktion „Automatisches Ausfüllen“ für Firefox verwenden zu können, muss ein Gerätepasssatz aktiviert sein."; +"Logins.PasscodeRequirement.Warning" = "Um die Funktion „Automatisches Ausfüllen“ für Ecosia verwenden zu können, muss ein Gerätepasssatz aktiviert sein."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Nehmen Sie Ihre Passwörter überall mit"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox-Passwörter automatisch ausfüllen"; +"Logins.WelcomeView.Title2" = "Ecosia-Passwörter automatisch ausfüllen"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Automatisches Ausfüllen aktivieren"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "GESPEICHERTE ZUGANGSDATEN"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox scheint zuvor abgestürzt zu sein. Sollen Ihre Tabs wiederhergestellt werden?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia scheint zuvor abgestürzt zu sein. Sollen Ihre Tabs wiederhergestellt werden?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Als gelesen markieren"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Lossurfen"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hoppla! Firefox ist abgestürzt"; +"Oops! Firefox crashed" = "Hoppla! Ecosia ist abgestürzt"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Öffnen Sie Artikel in der Leseansicht, indem Sie auf das Buch-Symbol tippen, wenn es in der Titelleiste angezeigt wird."; @@ -905,7 +905,7 @@ "Open Tabs" = "Offene Tabs"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kann die Seite nicht öffnen, weil ihre Adresse ungültig ist."; +"OpenURL.Error.Message" = "Ecosia kann die Seite nicht öffnen, weil ihre Adresse ungültig ist."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Seite kann nicht geöffnet werden"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Hiermit können Sie die Grafik in Ihren Aufnahmen speichern."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox möchte auf Ihre Fotos zugreifen"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia möchte auf Ihre Fotos zugreifen"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Schließen"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Die Daten sind ungültig"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Gehen Sie zu „Einstellungen“ des Geräts > „Firefox“. Erlauben Sie Firefox, auf die Kamera zuzugreifen."; +"ScanQRCode.PermissionError.Message.v100" = "Gehen Sie zu „Einstellungen“ des Geräts > „Ecosia“. Erlauben Sie Ecosia, auf die Kamera zuzugreifen."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR-Code scannen"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Zum Tab wechseln"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-Vorschläge"; +"Search.SuggestSectionTitle.v102" = "Ecosia-Vorschläge"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Die neue Suchmaschine erscheint in der Schnellsuchleiste."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Einen Absturzbericht senden, damit Mozilla das Problem beheben kann?"; +"Send a crash report so Mozilla can fix the problem?" = "Einen Absturzbericht senden, damit Ecosia das Problem beheben kann?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Feedback senden"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Tab senden"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Derzeit sind keine anderen Geräte mit diesem Firefox-Konto verbunden, die synchronisiert werden könnten."; +"SendTo.NoDevicesFound.Message" = "Derzeit sind keine anderen Geräte mit diesem Ecosia-Konto verbunden, die synchronisiert werden könnten."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Bitte starten Sie Firefox, öffnen Sie die Einstellungen und melden Sie sich an, um fortzufahren."; +"SendTo.NotSignedIn.Message" = "Bitte starten Sie Ecosia, öffnen Sie die Einstellungen und melden Sie sich an, um fortzufahren."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Sie sind nicht mit Ihrem Firefox-Konto angemeldet."; +"SendTo.NotSignedIn.Title" = "Sie sind nicht mit Ihrem Ecosia-Konto angemeldet."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Senden"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Zum Beginnen berühren"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ansehen"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Datenverwaltung"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox beendet die Synchronisation mit Ihrem Konto, löscht aber keine Surf-Daten auf diesem Gerät."; +"Settings.Disconnect.Body" = "Ecosia beendet die Synchronisation mit Ihrem Konto, löscht aber keine Surf-Daten auf diesem Gerät."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync trennen"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync-Einstellungen"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-Konto"; +"Settings.FxA.Title" = "Ecosia-Konto"; /* General settings section title */ "Settings.General.SectionName" = "Allgemein"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Wählen Sie aus, was als Startseite angezeigt wird."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Wählen Sie aus, welche Inhalte auf der Firefox-Startseite angezeigt werden."; +"Settings.Home.Option.Description.v101" = "Wählen Sie aus, welche Inhalte auf der Ecosia-Startseite angezeigt werden."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Zurückspringen"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startseite"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Entscheiden Sie, was bei der Rückkehr zu Firefox angezeigt wird."; +"Settings.Home.Option.StartAtHome.Description" = "Entscheiden Sie, was bei der Rückkehr zu Ecosia angezeigt wird."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Letzter Tab"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Hintergrundbild"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-Hintergrundbild, Amethystmuster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-Hintergrundbild, Amethystmuster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-Hintergrundbild, Muster mit Strandhügeln."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-Hintergrundbild, Muster mit Strandhügeln."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-Hintergrundbild, himmelblaues Muster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-Hintergrundbild, himmelblaues Muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standardmäßiges transparentes Hintergrundbild."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox-Hintergrundbild, Sonnenaufgangsmuster."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia-Hintergrundbild, Sonnenaufgangsmuster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Umschalter für den Hintergrundbild-Zyklus der Startseite"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-Hintergrundbild, Muster mit Hügeln in der Dämmerung."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-Hintergrundbild, Muster mit Hügeln in der Dämmerung."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ klassisch"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limitierte Auflage"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Hintergrundbild durch Tippen auf das Logo der Firefox-Startseite ändern"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Hintergrundbild durch Tippen auf das Logo der Ecosia-Startseite ändern"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Hintergrundbild"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Benutzerdefiniert"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startseite"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Grafiken blockieren"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Wenn Firefox geöffnet wird"; +"Settings.OfferClipboardBar.Status" = "Wenn Ecosia geöffnet wird"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Beim Öffnen von %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Wenn Firefox geöffnet wird"; +"Settings.OfferClipboardBar.StatusV2" = "Wenn Ecosia geöffnet wird"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Anbieten, kopierte Links zu öffnen"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Weitere Informationen."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ist bestrebt, nur die Informationen zu sammeln, mit denen wir Firefox anbieten und für alle Nutzer verbessern können."; +"Settings.SendUsage.Message" = "Ecosia ist bestrebt, nur die Informationen zu sammeln, mit denen wir Ecosia anbieten und für alle Nutzer verbessern können."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Nutzungsdaten senden"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Neuen Tab öffnen"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri-Kürzel verwenden, um Firefox schnell per Siri zu öffnen"; +"Settings.Siri.SectionDescription" = "Siri-Kürzel verwenden, um Ecosia schnell per Siri zu öffnen"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-Kürzel"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Weitere Informationen"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox darf von Zeit zu Zeit Studien installieren und laufen lassen."; +"Settings.Studies.Toggle.Message" = "Ecosia darf von Zeit zu Zeit Studien installieren und laufen lassen."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studien"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Im Hintergrund laden"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Wird in Firefox geladen"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Wird in Ecosia geladen"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "In Firefox öffnen"; +"ShareExtension.OpenInFirefoxAction.Title" = "In Ecosia öffnen"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "In Firefox suchen"; +"ShareExtension.SeachInFirefoxAction.Title" = "In Ecosia suchen"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopieren"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Einfügen & Los"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Mit Firefox suchen"; +"UIMenuItem.SearchWithFirefox" = "Mit Ecosia suchen"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ungelesen"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Gestern"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Sie haben keine offenen Tabs in Firefox auf Ihren anderen Geräten."; +"You don’t have any tabs open in Firefox on your other devices." = "Sie haben keine offenen Tabs in Ecosia auf Ihren anderen Geräten."; /* Your Rights settings section title */ "Your Rights" = "Ihre Rechte"; diff --git a/firefox-ios/Shared/de.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/de.lproj/PrivateBrowsing.strings index fc4c0c97d5796..6d04325a4ea7b 100644 --- a/firefox-ios/Shared/de.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/de.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "In neuem privaten Tab öffnen"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox wird weder Chronik noch Cookies speichern. Neue Lesezeichen können jedoch gespeichert werden."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia wird weder Chronik noch Cookies speichern. Neue Lesezeichen können jedoch gespeichert werden."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Mehr erfahren"; diff --git a/firefox-ios/Shared/de.lproj/Today.strings b/firefox-ios/Shared/de.lproj/Today.strings index a9437f68beede..09f5d62cbcdcf 100644 --- a/firefox-ios/Shared/de.lproj/Today.strings +++ b/firefox-ios/Shared/de.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Private Tabs schließen"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Fügen Sie Firefox-Verknüpfungen auf Ihrem Startbildschirm hinzu."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Fügen Sie Ecosia-Verknüpfungen auf Ihrem Startbildschirm hinzu."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Link in der Zwischenablage aufrufen"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Keine offenen Tabs."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox öffnen"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia öffnen"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Private Suche"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Fügen Sie Ihrem Startbildschirm eine Firefox-Verknüpfung hinzu. Berühren und halten Sie das Widget nach dem Hinzufügen, um es zu bearbeiten und eine andere Verknüpfung auszuwählen."; +"TodayWidget.QuickActionGalleryDescription" = "Fügen Sie Ihrem Startbildschirm eine Ecosia-Verknüpfung hinzu. Berühren und halten Sie das Widget nach dem Hinzufügen, um es zu bearbeiten und eine andere Verknüpfung auszuwählen."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Schnellaktionen"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-Verknüpfungen"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-Verknüpfungen"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Fügen Sie Verknüpfungen zu Ihren offenen Tabs hinzu."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Schnellansicht"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "In Firefox suchen"; +"TodayWidget.SearchInFirefoxV2" = "In Ecosia suchen"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "In privatem Tab suchen"; diff --git a/firefox-ios/Shared/dsb.lproj/Default Browser.strings b/firefox-ios/Shared/dsb.lproj/Default Browser.strings index feb313fda8494..3eb479a605c64 100644 --- a/firefox-ios/Shared/dsb.lproj/Default Browser.strings +++ b/firefox-ios/Shared/dsb.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Gaž Firefox k swójomu standardnemu wobglědowakoju cyniśo, jo to głos za wótwórjony, pśistupny internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Gaž Ecosia k swójomu standardnemu wobglědowakoju cyniśo, jo to głos za wótwórjony, pśistupny internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard za lěpšy internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Zgóńśo, kak to źo"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Nastajśo wótkaze z websedłow, mejlkow a powěsćow, aby se awtomatiski we Firefox wócynili."; +"DefaultBrowserCard.Description" = "Nastajśo wótkaze z websedłow, mejlkow a powěsćow, aby se awtomatiski we Ecosia wócynili."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Wubjeŕśo Firefox ako swój standardny wobglědowak, aby malsnosć, wěstotu a priwatnosć awtomatiski dostał."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Wubjeŕśo Ecosia ako swój standardny wobglědowak, aby malsnosć, wěstotu a priwatnosć awtomatiski dostał."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Wšedne pśeglědowanje – hyšći lěpše"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox w pśerězku wěcej ako 3000 pśeslědowakow na wužywarja kuždy mjasec blokěrujo. Cyńśo jen k swójomu standardnemu wobglědowakoju, aby w pokoju pśeglědował."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia w pśerězku wěcej ako 3000 pśeslědowakow na wužywarja kuždy mjasec blokěrujo. Cyńśo jen k swójomu standardnemu wobglědowakoju, aby w pokoju pśeglědował."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox wašu priwatnosć šćita"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia wašu priwatnosć šćita"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Standardny wobglědowak změniś"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Standardne nałoženje wobglědowaka pótusniś"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox wubraś"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia wubraś"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standardne nałoženje wobglědowaka"; diff --git a/firefox-ios/Shared/dsb.lproj/Intro.strings b/firefox-ios/Shared/dsb.lproj/Intro.strings index 1cfaf5721b987..5ec806c6ef853 100644 --- a/firefox-ios/Shared/dsb.lproj/Intro.strings +++ b/firefox-ios/Shared/dsb.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Pśizjawiś"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Witajśo k Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Witajśo k Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Pśeglědowaś zachopiś"; diff --git a/firefox-ios/Shared/dsb.lproj/Localizable.strings b/firefox-ios/Shared/dsb.lproj/Localizable.strings index dd5d94ccd4977..7ff0f9abbffac 100644 --- a/firefox-ios/Shared/dsb.lproj/Localizable.strings +++ b/firefox-ios/Shared/dsb.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "To jo snaź wopacna konfigracija abo manipulacija napadowarja. Cyńśo dalej, jolic potencielne riziko akceptěrujośo."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Wobsejźaŕ %@ jo websedło wopak konfigurěrował. Aby waše daty pśed złoéjstwom šćitał, Firefox njejo was z toś tym websedłom zwězał."; +"ErrorPages.CertWarning.Description" = "Wobsejźaŕ %@ jo websedło wopak konfigurěrował. Aby waše daty pśed złoéjstwom šćitał, Ecosia njejo was z toś tym websedłom zwězał."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Toś ten zwisk jo dowěry njegódny"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Startowy bok Firefox"; +"Firefox.HomePage.Title" = "Startowy bok Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Startowy bok pśiměriś"; @@ -377,7 +377,7 @@ "Forward" = "Doprědka"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Konto Firefox"; +"FxA.FirefoxAccount" = "Konto Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Konto a rědy zastojaś"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Pśizjawśo se ze swójeju kameru"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Wócyńśo Firefox na swójom licadle a źiśo k firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Wócyńśo Ecosia na swójom licadle a źiśo k ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Gótowy za skannowanje"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Něnto synchronizěrowaś"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync jo z %@ zwězany"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync jo z %@ zwězany"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync jo zwězany"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ jo se wuspěšnje wótźělił."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Toś ten rěd jo se wuspěšnje wót Firefox Sync wótźělił."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Toś ten rěd jo se wuspěšnje wót Ecosia Sync wótźělił."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync jo wótźělony"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync jo wótźělony"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Rěd jo se wót Firefox Sync wótźělił"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Rěd jo se wót Ecosia Sync wótźělił"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, slězynowy wobraz změniś."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, slězynowy wobraz změniś."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomoc"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Pśizjawśo se, aby se lisćinu rejtarikow ze swójich drugich rědow woglědał."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historija"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Dalšne informacije"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Aby funkciju awtomatiskego wupołnjenja Firefox wužywał, musyśo rědowy gronidłowy kod zmóžnjony byś."; +"Logins.PasscodeRequirement.Warning" = "Aby funkciju awtomatiskego wupołnjenja Ecosia wužywał, musyśo rědowy gronidłowy kod zmóžnjony byś."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Wzejśo swóje gronidła wšuźi sobu"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Gronidła Firefox awtomatiski wupołniś"; +"Logins.WelcomeView.Title2" = "Gronidła Ecosia awtomatiski wupołniś"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Awtomatiske wupołnjenje zmóžniś"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SKŁAŹONE PŚIZJAWJENJA"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zda se, až Firefox jo se do togo wowalił. Cośo swóje rejtariki wótnowiś?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zda se, až Ecosia jo se do togo wowalił. Cośo swóje rejtariki wótnowiś?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Ako pśecytany markěrowaś"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Pśeglědowanje startowaś"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hopla! Firefox jo se wowalił"; +"Oops! Firefox crashed" = "Hopla! Ecosia jo se wowalił"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Wócyńsó nastawki w cytańskem naglěźe, z tym až pótuskujośo knigłowy symbol, gaž pokazujo se w titelnej rědce."; @@ -905,7 +905,7 @@ "Open Tabs" = "Wócynjone rejtariki"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox njamóžo bok wócyniś, dokulaž ma njepłaśiwu adresu."; +"OpenURL.Error.Message" = "Ecosia njamóžo bok wócyniś, dokulaž ma njepłaśiwu adresu."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Bok njedajo se wócyniś"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "To wam zmóžnja, wobraz do Camera Roll składowaś."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox by rady pśistup k wašym fotam měł"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia by rady pśistup k wašym fotam měł"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zacyniś"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Daty su njepłaśiwe"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Źiśo k rědoju „Nastajenja“ > „Firefox“. Dowólśo Firefox pśistup ku kamerje."; +"ScanQRCode.PermissionError.Message.v100" = "Źiśo k rědoju „Nastajenja“ > „Ecosia“. Dowólśo Ecosia pśistup ku kamerje."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR-kod skannowaś"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "K rejtarikoju pśejś"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Naraźenje Firefox"; +"Search.SuggestSectionTitle.v102" = "Naraźenje Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nowa pytnica pokažo se w pólu malsnego pytanja."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "W pórěźe"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Cośo rozpšawu wó wowalenju słaś, aby Mozilla mógał problem rozwězaś?"; +"Send a crash report so Mozilla can fix the problem?" = "Cośo rozpšawu wó wowalenju słaś, aby Ecosia mógał problem rozwězaś?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Komentar pósłaś"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Rejtarik słaś"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Njejsu žedne druge rědy z toś tym kontom Firefox zwězane, kótarež by se dali synchronizěrowaś."; +"SendTo.NoDevicesFound.Message" = "Njejsu žedne druge rědy z toś tym kontom Ecosia zwězane, kótarež by se dali synchronizěrowaś."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Pšosym wócyńśo Firefox, źiśo k nastajenjam a pśizjawśo se, aby pókšacował."; +"SendTo.NotSignedIn.Message" = "Pšosym wócyńśo Ecosia, źiśo k nastajenjam a pśizjawśo se, aby pókšacował."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Njejsćo pla swójogo konta Firefox pśizjawjony."; +"SendTo.NotSignedIn.Title" = "Njejsćo pla swójogo konta Ecosia pśizjawjony."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Pósłaś"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Pótusniśo, aby zachopił"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Pokazaś"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Datowe zastojanje"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox pśestanjo z wašym kontom synchronizěrowaś, ale njewulašujo pśeglědowańske daty na toś tom rěźe."; +"Settings.Disconnect.Body" = "Ecosia pśestanjo z wašym kontom synchronizěrowaś, ale njewulašujo pśeglědowańske daty na toś tom rěźe."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync źěliś"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Nastajenja synchronizacije"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Konto Firefox"; +"Settings.FxA.Title" = "Konto Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Powšykne"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Wubjeŕśo, což se ma ako startowy bok pokazaś."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Wubjeŕśo, kótary wopśimjeśe se ma na startowem boku Firefox pokazaś."; +"Settings.Home.Option.Description.v101" = "Wubjeŕśo, kótary wopśimjeśe se ma na startowem boku Ecosia pokazaś."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Slědk skócyś"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startowy bok"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Wubjeŕśo, což cośo wiźeś, gaž se k Firefox wrośaśo."; +"Settings.Home.Option.StartAtHome.Description" = "Wubjeŕśo, což cośo wiźeś, gaž se k Ecosia wrośaśo."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Slědny rejtarik"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Slězynowy wobraz"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Slězynowy wobraz Firefox, ametystowy muster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Slězynowy wobraz Ecosia, ametystowy muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Slězynowy wobraz Firefox, muster pśibrjoznych górkow."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Slězynowy wobraz Ecosia, muster pśibrjoznych górkow."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Slězynowy wobraz Firefox, njebjomódry muster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Slězynowy wobraz Ecosia, njebjomódry muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standardny transparentny slězynowy wobraz."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Slězynowy wobraz Firefox, muster zejźenja słyńca."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Slězynowy wobraz Ecosia, muster zejźenja słyńca."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Šalter za cyklus slězynowych wobrazow startowego boka"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Slězynowy wobraz Firefox, muster z górkami w změrkanju."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Slězynowy wobraz Ecosia, muster z górkami w změrkanju."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasiski %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limitěrowana edicija"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Pótusniśo logo startowego boka Firefox, aby slězynowy wobraz změnił"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Pótusniśo logo startowego boka Ecosia, aby slězynowy wobraz změnił"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Slězynowy wobraz"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Swójski"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Startowy bok Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Startowy bok Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startowy bok"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Wobraze blokěrowaś"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Gaž se Firefox wócynja"; +"Settings.OfferClipboardBar.Status" = "Gaž se Ecosia wócynja"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Pśi wócynjanju %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Gaž se Firefox wócynja"; +"Settings.OfferClipboardBar.StatusV2" = "Gaž se Ecosia wócynja"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Póbitowaś, kopěrowane wótkaze wócyniś"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Dalšne informacije."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se procujo, jano informacije gromaźiś, kótarež trjebamy, aby Firefox za kuždego póbitowaś a pólěpšyś."; +"Settings.SendUsage.Message" = "Ecosia se procujo, jano informacije gromaźiś, kótarež trjebamy, aby Ecosia za kuždego póbitowaś a pólěpšyś."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Wužywańske daty pósłaś"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Nowy rejtarik wócyniś"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Skrotconki Siri wužywaś, aby se Firefox malsnje pśez Siri wócynił"; +"Settings.Siri.SectionDescription" = "Skrotconki Siri wužywaś, aby se Ecosia malsnje pśez Siri wócynił"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Skrotconki Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Dalšne informacije."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox móžo wótergi studije instalěrowaś a pśewjasć."; +"Settings.Studies.Toggle.Message" = "Ecosia móžo wótergi studije instalěrowaś a pśewjasć."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studije"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "W slězynje zacytaś"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Zacytujo se w Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Zacytujo se w Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "W Firefox wócyniś"; +"ShareExtension.OpenInFirefoxAction.Title" = "W Ecosia wócyniś"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "W Firefox pytaś"; +"ShareExtension.SeachInFirefoxAction.Title" = "W Ecosia pytaś"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopěrowaś"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Zasajźiś a zachopiś"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Z Firefox pytaś"; +"UIMenuItem.SearchWithFirefox" = "Z Ecosia pytaś"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "njecytany"; diff --git a/firefox-ios/Shared/dsb.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/dsb.lproj/PrivateBrowsing.strings index 2675875e63bb4..7bb19d78a98ae 100644 --- a/firefox-ios/Shared/dsb.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/dsb.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "W nowem priwatnem rejtariku wócyniś"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox njebuźo se wašu historiju abo cookieje spomnjeś, ale nowe cytańske znamjenja budu se składowaś."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia njebuźo se wašu historiju abo cookieje spomnjeś, ale nowe cytańske znamjenja budu se składowaś."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Dalšne informacije"; diff --git a/firefox-ios/Shared/dsb.lproj/Today.strings b/firefox-ios/Shared/dsb.lproj/Today.strings index b5df66daba7ac..5654cc01a7418 100644 --- a/firefox-ios/Shared/dsb.lproj/Today.strings +++ b/firefox-ios/Shared/dsb.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Priwatne\nrejtariki zacyniś"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Pśidajśo swójej startowej wobrazowce zwězanja Firefox"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Pśidajśo swójej startowej wobrazowce zwězanja Ecosia"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ku kopěrowanemu wótkazoju"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Žedne wócynjone rejtariki."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox wócyniś"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia wócyniś"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Priwatne pytanje"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Pśidajśo startowej wobrazowce zwězanje Firefox. Za tym až sćo pśidał asistent, pótusniśo asistent a źaržćo jen, aby wobźěłował a wubjeŕśo druge zwězanje."; +"TodayWidget.QuickActionGalleryDescription" = "Pśidajśo startowej wobrazowce zwězanje Ecosia. Za tym až sćo pśidał asistent, pótusniśo asistent a źaržćo jen, aby wobźěłował a wubjeŕśo druge zwězanje."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Malsne akcije"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Zwězanja Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Zwězanja Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Pśidajśo swójim wócynjonym rejtarikam zwězanja."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Malsny naglěd"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "W Firefox pytaś"; +"TodayWidget.SearchInFirefoxV2" = "W Ecosia pytaś"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "W priwatnem rejtariku pytaś"; diff --git a/firefox-ios/Shared/el.lproj/Default Browser.strings b/firefox-ios/Shared/el.lproj/Default Browser.strings index a78291c3ae84a..f7b88d481b4e4 100644 --- a/firefox-ios/Shared/el.lproj/Default Browser.strings +++ b/firefox-ios/Shared/el.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Ορίζοντας το Firefox ως προεπιλεγμένο πρόγραμμα περιήγησης, υποστηρίζετε ένα ανοικτό και προσβάσιμο διαδίκτυο."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ορίζοντας το Ecosia ως προεπιλεγμένο πρόγραμμα περιήγησης, υποστηρίζετε ένα ανοικτό και προσβάσιμο διαδίκτυο."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Προεπιλογή για ένα καλύτερο διαδίκτυο"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Μάθετε πώς"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Ρύθμιση του Firefox για αυτόματο άνοιγμα συνδέσμων από ιστοτόπους, email και μηνύματα."; +"DefaultBrowserCard.Description" = "Ρύθμιση του Ecosia για αυτόματο άνοιγμα συνδέσμων από ιστοτόπους, email και μηνύματα."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Επιλέξτε το Firefox ως το προεπιλεγμένο πρόγραμμα περιήγησής σας για αυτόματη ταχύτητα, ασφάλεια και ιδιωτικότητα."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Επιλέξτε το Ecosia ως το προεπιλεγμένο πρόγραμμα περιήγησής σας για αυτόματη ταχύτητα, ασφάλεια και ιδιωτικότητα."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Βελτίωση καθημερινής περιήγησης"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Το Firefox αποκλείει κατά μέσο όρο 3.000+ ιχνηλάτες ανά χρήστη κάθε μήνα. Ορίστε το ως προεπιλογή για να μην ανησυχείτε για το απόρρητό σας."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Το Ecosia αποκλείει κατά μέσο όρο 3.000+ ιχνηλάτες ανά χρήστη κάθε μήνα. Ορίστε το ως προεπιλογή για να μην ανησυχείτε για το απόρρητό σας."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Το Firefox προστατεύει το απόρρητό σας"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Το Ecosia προστατεύει το απόρρητό σας"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Αλλαγή κύριου φυλλομετρητή"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Πατήστε \"Προεπιλεγμένο πρόγραμμα περιήγησης εφαρμογών\""; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Επιλέξτε το Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Επιλέξτε το Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Προεπιλεγμένο πρόγραμμα περιήγησης εφαρμογών"; diff --git a/firefox-ios/Shared/el.lproj/Intro.strings b/firefox-ios/Shared/el.lproj/Intro.strings index 1385519be4cf0..89fa0aece502d 100644 --- a/firefox-ios/Shared/el.lproj/Intro.strings +++ b/firefox-ios/Shared/el.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Σύνδεση"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Καλώς ορίσατε στο Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Καλώς ορίσατε στο Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Έναρξη περιήγησης"; diff --git a/firefox-ios/Shared/el.lproj/Localizable.strings b/firefox-ios/Shared/el.lproj/Localizable.strings index 189af8451b95b..3de0fa3f750c9 100644 --- a/firefox-ios/Shared/el.lproj/Localizable.strings +++ b/firefox-ios/Shared/el.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Ενδέχεται να είναι μια λάθος ρύθμιση ή αλλοίωση από τρίτους. Συνεχίστε αν αποδέχεστε τον πιθανό κίνδυνο."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Ο ιδιοκτήτης του %@ δεν έχει ρυθμίσει σωστά τον ιστότοπό του. Για την προστασία των πληροφοριών σας από κλοπή, το Firefox δεν έχει συνδεθεί σε αυτόν τον ιστότοπο."; +"ErrorPages.CertWarning.Description" = "Ο ιδιοκτήτης του %@ δεν έχει ρυθμίσει σωστά τον ιστότοπό του. Για την προστασία των πληροφοριών σας από κλοπή, το Ecosia δεν έχει συνδεθεί σε αυτόν τον ιστότοπο."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Αυτή η σύνδεση δεν είναι αξιόπιστη"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Αρχική σελίδα Firefox"; +"Firefox.HomePage.Title" = "Αρχική σελίδα Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Προσαρμογή αρχικής σελίδας"; @@ -377,7 +377,7 @@ "Forward" = "Εμπρός"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Λογαριασμός Firefox"; +"FxA.FirefoxAccount" = "Λογαριασμός Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Διαχείριση λογαριασμού και συσκευών"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Σύνδεση με την κάμερά σας"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Στον υπολογιστή σας, ανοίξτε το Firefox και μεταβείτε στο firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Στον υπολογιστή σας, ανοίξτε το Ecosia και μεταβείτε στο ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Σάρωση"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Συγχρονισμός τώρα"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Το Firefox Sync έχει συνδεθεί με τη συσκευή %@"; +"FxAPush_DeviceConnected_body" = "Το Ecosia Sync έχει συνδεθεί με τη συσκευή %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Το Sync συνδέθηκε"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Η συσκευή %@ έχει αποσυνδεθεί επιτυχώς."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Αυτή η συσκευή έχει αποσυνδεθεί επιτυχώς από το Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Αυτή η συσκευή έχει αποσυνδεθεί επιτυχώς από το Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Το Sync αποσυνδέθηκε"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Το Sync αποσυνδέθηκε"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Μια συσκευή αποσυνδέθηκε από το Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Μια συσκευή αποσυνδέθηκε από το Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Λογότυπο Firefox, αλλαγής ταπετσαρίας."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Λογότυπο Ecosia, αλλαγής ταπετσαρίας."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Βοήθεια"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Συνδεθείτε για να δείτε μια λίστα καρτελών από τις άλλες συσκευές σας."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Ιστορικό"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Μάθετε περισσότερα"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Για τη λειτουργίας αυτόματης συμπλήρωσης του Firefox, πρέπει να έχετε ενεργοποιήσει το συνθηματικό συσκευής."; +"Logins.PasscodeRequirement.Warning" = "Για τη λειτουργίας αυτόματης συμπλήρωσης του Ecosia, πρέπει να έχετε ενεργοποιήσει το συνθηματικό συσκευής."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Πάρτε τους κωδικούς πρόσβασής σας παντού"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Αυτόματη συμπλήρωση κωδικών πρόσβασης Firefox"; +"Logins.WelcomeView.Title2" = "Αυτόματη συμπλήρωση κωδικών πρόσβασης Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Ενεργοποίηση αυτόματης συμπλήρωσης"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ΑΠΟΘΗΚΕΥΜΕΝΕΣ ΣΥΝΔΕΣΕΙΣ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Φαίνεται πως το Firefox κατέρρευσε προηγουμένως. Θα θέλατε να επαναφέρετε τις καρτέλες σας;"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Φαίνεται πως το Ecosia κατέρρευσε προηγουμένως. Θα θέλατε να επαναφέρετε τις καρτέλες σας;"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Σήμανση ως αναγνωσμένο"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Έναρξη περιήγησης"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ωχ! Το Firefox κατέρρευσε"; +"Oops! Firefox crashed" = "Ωχ! Το Ecosia κατέρρευσε"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Ανοίξτε τα άρθρα στην προβολή ανάγνωσης πατώντας στο εικονίδιο-βιβλίο όταν εμφανίζεται στη γραμμή τίτλου."; @@ -905,7 +905,7 @@ "Open Tabs" = "Άνοιγμα καρτελών"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Το Firefox δεν μπορεί να ανοίξει τη σελίδα επειδή έχει μια μη έγκυρη διεύθυνση."; +"OpenURL.Error.Message" = "Το Ecosia δεν μπορεί να ανοίξει τη σελίδα επειδή έχει μια μη έγκυρη διεύθυνση."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Αδυναμία ανοίγματος σελίδας"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Σας επιτρέπει να αποθηκεύετε την εικόνα στο Άλμπουμ κάμερας."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Το Firefox θα ήθελε πρόσβαση στις φωτογραφίες σας"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Το Ecosia θα ήθελε πρόσβαση στις φωτογραφίες σας"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Κλείσιμο"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Άκυρα δεδομένα"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Μεταβείτε στις «Ρυθμίσεις» > «Firefox». Επιτρέψτε στο Firefox την πρόσβαση στην κάμερα."; +"ScanQRCode.PermissionError.Message.v100" = "Μεταβείτε στις «Ρυθμίσεις» > «Ecosia». Επιτρέψτε στο Ecosia την πρόσβαση στην κάμερα."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Σάρωση κωδικού QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Εναλλαγή σε καρτέλα"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Προτάσεις Firefox"; +"Search.SuggestSectionTitle.v102" = "Προτάσεις Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Η νέα μηχανή αναζήτησης θα εμφανιστεί στη γραμμή γρήγορης αναζήτησης."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Αποστολή αναφοράς κατάρρευσης, ώστε να μπορέσει η Mozilla να διορθώσει το πρόβλημα;"; +"Send a crash report so Mozilla can fix the problem?" = "Αποστολή αναφοράς κατάρρευσης, ώστε να μπορέσει η Ecosia να διορθώσει το πρόβλημα;"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Αποστολή σχολίων"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Αποστολή καρτέλας"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Δεν έχετε συνδέσει καμία άλλη, διαθέσιμη προς συγχρονισμό, συσκευή σε αυτό το λογαριασμό Firefox."; +"SendTo.NoDevicesFound.Message" = "Δεν έχετε συνδέσει καμία άλλη, διαθέσιμη προς συγχρονισμό, συσκευή σε αυτό το λογαριασμό Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Παρακαλούμε ανοίξτε το Firefox, μεταβείτε στις Ρυθμίσεις και συνδεθείτε για να συνεχίσετε."; +"SendTo.NotSignedIn.Message" = "Παρακαλούμε ανοίξτε το Ecosia, μεταβείτε στις Ρυθμίσεις και συνδεθείτε για να συνεχίσετε."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Δεν έχετε συνδεθεί στον λογαριασμό Firefox σας."; +"SendTo.NotSignedIn.Title" = "Δεν έχετε συνδεθεί στον λογαριασμό Ecosia σας."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Αποστολή"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Πατήστε για έναρξη"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Προβολή"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Διαχείριση δεδομένων"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Το Firefox θα σταματήσει τον συγχρονισμό με τον λογαριασμό σας, αλλά δεν θα διαγράψει τα δεδομένα περιήγησής σας από αυτήν την συσκευή."; +"Settings.Disconnect.Body" = "Το Ecosia θα σταματήσει τον συγχρονισμό με τον λογαριασμό σας, αλλά δεν θα διαγράψει τα δεδομένα περιήγησής σας από αυτήν την συσκευή."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Αποσύνδεση Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Ρυθμίσεις συγχρονισμού"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Λογαριασμός Firefox"; +"Settings.FxA.Title" = "Λογαριασμός Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Γενικά"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Επιλέξτε τι εμφανίζεται ως αρχική σελίδα."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Επιλέξτε τι περιλαμβάνεται στην αρχική σελίδα του Firefox."; +"Settings.Home.Option.Description.v101" = "Επιλέξτε τι περιλαμβάνεται στην αρχική σελίδα του Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Άμεση επιστροφή"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Αρχική σελίδα"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Επιλέξτε τι θα βλέπετε όταν επιστρέφετε στο Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Επιλέξτε τι θα βλέπετε όταν επιστρέφετε στο Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Τελευταία καρτέλα"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Ταπετσαρία"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ταπετσαρία Firefox, μοτίβο αμέθυστου."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ταπετσαρία Ecosia, μοτίβο αμέθυστου."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ταπετσαρία Firefox, μοτίβο λόφων παραλίας."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ταπετσαρία Ecosia, μοτίβο λόφων παραλίας."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ταπετσαρία Firefox, γαλανό μοτίβο."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ταπετσαρία Ecosia, γαλανό μοτίβο."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Προεπιλεγμένη διάφανη ταπετσαρία."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ταπετσαρία Firefox, μοτίβο ανατολής."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ταπετσαρία Ecosia, μοτίβο ανατολής."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Διακόπτης εναλλαγής ταπετσαρίας αρχικής οθόνης"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ταπετσαρία Firefox, μοτίβο λόφων δειλινού."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ταπετσαρία Ecosia, μοτίβο λόφων δειλινού."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Κλασικό %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Περιορισμένη έκδοση"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Αλλαγή ταπετσαρίας από το λογότυπο του Firefox στην αρχική σελίδα"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Αλλαγή ταπετσαρίας από το λογότυπο του Ecosia στην αρχική σελίδα"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Ταπετσαρία"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Προσαρμογή"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Αρχική Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Αρχική Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Αρχική σελίδα"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Φραγή εικόνων"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Κατά το άνοιγμα του Firefox"; +"Settings.OfferClipboardBar.Status" = "Κατά το άνοιγμα του Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Κατά το άνοιγμα %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Κατά το άνοιγμα του Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Κατά το άνοιγμα του Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Επιλογή για άνοιγμα αντιγραμμένων συνδέσμων"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Μάθετε περισσότερα."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Η Mozilla αγωνίζεται για να συλλέγει μόνο ό,τι χρειάζεται για να παρέχει και να βελτιώνει το Firefox για όλους."; +"Settings.SendUsage.Message" = "Η Ecosia αγωνίζεται για να συλλέγει μόνο ό,τι χρειάζεται για να παρέχει και να βελτιώνει το Ecosia για όλους."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Αποστολή δεδομένων χρήσης"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Άνοιγμα νέας καρτέλας"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Χρήση συντομεύσεων Siri για ταχύ άνοιγμα του Firefox μέσω Siri"; +"Settings.Siri.SectionDescription" = "Χρήση συντομεύσεων Siri για ταχύ άνοιγμα του Ecosia μέσω Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Συντομεύσεις Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Μάθετε περισσότερα."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Το Firefox ενδέχεται να εγκαθιστά και να εκτελεί περιστασιακά μελέτες."; +"Settings.Studies.Toggle.Message" = "Το Ecosia ενδέχεται να εγκαθιστά και να εκτελεί περιστασιακά μελέτες."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Μελέτες"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Φόρτωση στο παρασκήνιο"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Φόρτωση στο Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Φόρτωση στο Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Άνοιγμα στο Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Άνοιγμα στο Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Αναζήτηση στο Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Αναζήτηση στο Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Αντιγραφή"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Επικόλληση και μετάβαση"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Αναζήτηση με το Firefox"; +"UIMenuItem.SearchWithFirefox" = "Αναζήτηση με το Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "μη αναγνωσμένο"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Χθες"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Δεν έχετε καμία ανοικτή καρτέλα στο Firefox στις άλλες σας συσκευές."; +"You don’t have any tabs open in Firefox on your other devices." = "Δεν έχετε καμία ανοικτή καρτέλα στο Ecosia στις άλλες σας συσκευές."; /* Your Rights settings section title */ "Your Rights" = "Τα δικαιώματά σας"; diff --git a/firefox-ios/Shared/el.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/el.lproj/PrivateBrowsing.strings index c9f5ea9def358..6e53c2d32adce 100644 --- a/firefox-ios/Shared/el.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/el.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Άνοιγμα σε νέα ιδιωτική καρτέλα"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Το Firefox δεν θα διατηρεί το ιστορικό ή τα cookie σας, αλλά θα αποθηκεύει τους νέους σελιδοδείκτες."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Το Ecosia δεν θα διατηρεί το ιστορικό ή τα cookie σας, αλλά θα αποθηκεύει τους νέους σελιδοδείκτες."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Μάθετε περισσότερα"; diff --git a/firefox-ios/Shared/el.lproj/Today.strings b/firefox-ios/Shared/el.lproj/Today.strings index 26f55e6cd1cd8..d3b7799b37a12 100644 --- a/firefox-ios/Shared/el.lproj/Today.strings +++ b/firefox-ios/Shared/el.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Κλείσιμο ιδιωτικών καρτελών"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Προσθήκη συντομεύσεων Firefox στην αρχική οθόνη."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Προσθήκη συντομεύσεων Ecosia στην αρχική οθόνη."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Μετάβαση στον αντιγραμμένο σύνδεσμο"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Καμία ανοικτή καρτέλα."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Άνοιγμα Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Άνοιγμα Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Ιδιωτική αναζήτηση"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Προσθέστε μια συντόμευση Firefox στην αρχική οθόνη. Αφού προσθέσετε το γραφικό στοιχείο, πατήστε παρατεταμένα για επεξεργασία και επιλογή διαφορετικής συντόμευσης."; +"TodayWidget.QuickActionGalleryDescription" = "Προσθέστε μια συντόμευση Ecosia στην αρχική οθόνη. Αφού προσθέσετε το γραφικό στοιχείο, πατήστε παρατεταμένα για επεξεργασία και επιλογή διαφορετικής συντόμευσης."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Γρήγορες ενέργειες"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Συντομεύσεις Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Συντομεύσεις Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Προσθήκη συντομεύσεων για ανοικτές καρτέλες."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Γρήγορη προβολή"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Αναζήτηση στο Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Αναζήτηση στο Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Αναζήτηση σε ιδιωτική καρτέλα"; diff --git a/firefox-ios/Shared/en-CA.lproj/Default Browser.strings b/firefox-ios/Shared/en-CA.lproj/Default Browser.strings index d717a1aa0f10d..0652aef635934 100644 --- a/firefox-ios/Shared/en-CA.lproj/Default Browser.strings +++ b/firefox-ios/Shared/en-CA.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Firefox your default browser is a vote for an open, accessible internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Ecosia your default browser is a vote for an open, accessible internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Default to a Better Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Learn How"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Firefox."; +"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Firefox as your default browser to make speed, safety, and privacy automatic."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Ecosia as your default browser to make speed, safety, and privacy automatic."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Elevate Everyday Browsing"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox Has Privacy Covered"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia Has Privacy Covered"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Switch Your Default Browser"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tap Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Select Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Select Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/en-CA.lproj/Intro.strings b/firefox-ios/Shared/en-CA.lproj/Intro.strings index bc4663239633e..860a24bba7897 100644 --- a/firefox-ios/Shared/en-CA.lproj/Intro.strings +++ b/firefox-ios/Shared/en-CA.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Sign In"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Welcome to Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Welcome to Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Start Browsing"; diff --git a/firefox-ios/Shared/en-CA.lproj/Localizable.strings b/firefox-ios/Shared/en-CA.lproj/Localizable.strings index 2bfd2ca45c658..ac9befb2fa337 100644 --- a/firefox-ios/Shared/en-CA.lproj/Localizable.strings +++ b/firefox-ios/Shared/en-CA.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "It may be a misconfiguration or tampering by an attacker. Proceed if you accept the potential risk."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website."; +"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their website improperly. To protect your information from being stolen, Ecosia has not connected to this website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "This Connection is Untrusted"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Home Page"; +"Firefox.HomePage.Title" = "Ecosia Home Page"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Customize Homepage"; @@ -377,7 +377,7 @@ "Forward" = "Forwards"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Manage Account & Devices"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Sign In with Your Camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "On your computer open Firefox and go to firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "On your computer open Ecosia and go to ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Ready to Scan"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sync Now"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync has connected to %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync has connected to %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Connected"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ has been successfully disconnected."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync Disconnected"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync Disconnected"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logo, change the wallpaper."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logo, change the wallpaper."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Help"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Sign in to view a list of tabs from your other devices."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "History"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Learn More"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "To use the AutoFill feature for Firefox, you must have a device passcode enabled."; +"Logins.PasscodeRequirement.Warning" = "To use the AutoFill feature for Ecosia, you must have a device passcode enabled."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Take your passwords everywhere"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill Firefox Passwords"; +"Logins.WelcomeView.Title2" = "AutoFill Ecosia Passwords"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Turn on AutoFill"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Firefox crashed previously. Would you like to restore your tabs?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Ecosia crashed previously. Would you like to restore your tabs?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Mark as Read"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Start Browsing"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox crashed"; +"Oops! Firefox crashed" = "Oops! Ecosia crashed"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Open articles in Reader View by tapping the book icon when it appears in the title bar."; @@ -905,7 +905,7 @@ "Open Tabs" = "Open Tabs"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox cannot open the page because it has an invalid address."; +"OpenURL.Error.Message" = "Ecosia cannot open the page because it has an invalid address."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Cannot Open Page"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "This allows you to save the image to your Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox would like to access your Photos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia would like to access your Photos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Close"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "The data is invalid"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Go to device ‘Settings’ > ‘Firefox’. Allow Firefox to access camera."; +"ScanQRCode.PermissionError.Message.v100" = "Go to device ‘Settings’ > ‘Ecosia’. Allow Ecosia to access camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scan QR Code"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Switch to tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "The new search engine will appear in the quick search bar."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Mozilla can fix the problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Ecosia can fix the problem?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Send Feedback"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Send Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Firefox Account available to sync."; +"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Ecosia Account available to sync."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Please open Firefox, go to Settings and sign in to continue."; +"SendTo.NotSignedIn.Message" = "Please open Ecosia, go to Settings and sign in to continue."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "You are not signed in to your Firefox Account."; +"SendTo.NotSignedIn.Title" = "You are not signed in to your Ecosia Account."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tap to begin"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "View"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Data Management"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox will stop syncing with your account, but won’t delete any of your browsing data on this device."; +"Settings.Disconnect.Body" = "Ecosia will stop syncing with your account, but won’t delete any of your browsing data on this device."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Disconnect Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync Settings"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Choose what displays as the homepage."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Choose what’s included on the Firefox homepage."; +"Settings.Home.Option.Description.v101" = "Choose what’s included on the Ecosia homepage."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Jump Back In"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Homepage"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Choose what you see when you return to Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Choose what you see when you return to Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Last tab"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Wallpaper"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox wallpaper, amethyst pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia wallpaper, amethyst pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox wallpaper, beach hills pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia wallpaper, beach hills pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox wallpaper, cerulean pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia wallpaper, cerulean pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Default clear wallpaper."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox wallpaper, sunrise pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia wallpaper, sunrise pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Homepage wallpaper cycle toggle"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox wallpaper, twilight hills pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia wallpaper, twilight hills pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Classic %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limited Edition"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Change wallpaper by tapping Firefox homepage logo"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Change wallpaper by tapping Ecosia homepage logo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Wallpaper"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Custom"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Homepage"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Block Images"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "When Opening Firefox"; +"Settings.OfferClipboardBar.Status" = "When Opening Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "When opening %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "When opening Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "When opening Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Offer to Open Copied Links"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Learn More."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strives to only collect what we need to provide and improve Firefox for everyone."; +"Settings.SendUsage.Message" = "Ecosia strives to only collect what we need to provide and improve Ecosia for everyone."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send Usage Data"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Open New Tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Shortcuts"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Learn More."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox may install and run studies from time to time."; +"Settings.Studies.Toggle.Message" = "Ecosia may install and run studies from time to time."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studies"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Load in Background"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Open in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Open in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Search in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Search in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copy"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Paste & Go"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Search with Firefox"; +"UIMenuItem.SearchWithFirefox" = "Search with Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "unread"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Yesterday"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "You don’t have any tabs open in Firefox on your other devices."; +"You don’t have any tabs open in Firefox on your other devices." = "You don’t have any tabs open in Ecosia on your other devices."; /* Your Rights settings section title */ "Your Rights" = "Your Rights"; diff --git a/firefox-ios/Shared/en-CA.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/en-CA.lproj/PrivateBrowsing.strings index 1afee75ffac1b..c4986950c5450 100644 --- a/firefox-ios/Shared/en-CA.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/en-CA.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Open in New Private Tab"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox won’t remember any of your history or cookies, but new bookmarks will be saved."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia won’t remember any of your history or cookies, but new bookmarks will be saved."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Learn More"; diff --git a/firefox-ios/Shared/en-CA.lproj/Today.strings b/firefox-ios/Shared/en-CA.lproj/Today.strings index f795803bac7dd..428b5772d47ac 100644 --- a/firefox-ios/Shared/en-CA.lproj/Today.strings +++ b/firefox-ios/Shared/en-CA.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Close Private Tabs"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Firefox shortcuts to your Home screen."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Ecosia shortcuts to your Home screen."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Go to copied link"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No open tabs."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Open Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Open Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Private Search"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Add a Firefox shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; +"TodayWidget.QuickActionGalleryDescription" = "Add a Ecosia shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Quick Actions"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox Shortcuts"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia Shortcuts"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Add shortcuts to your open tabs."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Quick View"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Search in Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Search in Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Search in Private Tab"; diff --git a/firefox-ios/Shared/en-GB.lproj/Default Browser.strings b/firefox-ios/Shared/en-GB.lproj/Default Browser.strings index 44ff7114c0478..e77d7d8075207 100644 --- a/firefox-ios/Shared/en-GB.lproj/Default Browser.strings +++ b/firefox-ios/Shared/en-GB.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Firefox your default browser is a vote for an open, accessible internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Ecosia your default browser is a vote for an open, accessible internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Default to a Better Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Learn How"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Set links from web sites, emails, and Messages to open automatically in Firefox."; +"DefaultBrowserCard.Description" = "Set links from web sites, emails, and Messages to open automatically in Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Firefox as your default browser to make speed, safety, and privacy automatic."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Ecosia as your default browser to make speed, safety, and privacy automatic."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Elevate Everyday Browsing"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox Has Privacy Covered"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia Has Privacy Covered"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Switch Your Default Browser"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tap Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Select Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Select Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/en-GB.lproj/Intro.strings b/firefox-ios/Shared/en-GB.lproj/Intro.strings index bc4663239633e..860a24bba7897 100644 --- a/firefox-ios/Shared/en-GB.lproj/Intro.strings +++ b/firefox-ios/Shared/en-GB.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Sign In"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Welcome to Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Welcome to Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Start Browsing"; diff --git a/firefox-ios/Shared/en-GB.lproj/Localizable.strings b/firefox-ios/Shared/en-GB.lproj/Localizable.strings index fa607ebc9b368..32dad9da8e46c 100644 --- a/firefox-ios/Shared/en-GB.lproj/Localizable.strings +++ b/firefox-ios/Shared/en-GB.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "It may be a misconfiguration or tampering by an attacker. Proceed if you accept the potential risk."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their web site improperly. To protect your information from being stolen, Firefox has not connected to this web site."; +"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their web site improperly. To protect your information from being stolen, Ecosia has not connected to this web site."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "This Connection is Untrusted"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Home Page"; +"Firefox.HomePage.Title" = "Ecosia Home Page"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Customise Homepage"; @@ -377,7 +377,7 @@ "Forward" = "Forwards"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Manage Account & Devices"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Sign In with Your Camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "On your computer open Firefox and go to firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "On your computer open Ecosia and go to ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Ready to Scan"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sync Now"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync has connected to %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync has connected to %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Connected"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ has been successfully disconnected."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync Disconnected"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync Disconnected"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logo, change the wallpaper."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logo, change the wallpaper."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Help"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Sign in to view a list of tabs from your other devices."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "History"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Learn More"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "To use the AutoFill feature for Firefox, you must have a device passcode enabled."; +"Logins.PasscodeRequirement.Warning" = "To use the AutoFill feature for Ecosia, you must have a device passcode enabled."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Take your passwords everywhere"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill Firefox Passwords"; +"Logins.WelcomeView.Title2" = "AutoFill Ecosia Passwords"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Turn on AutoFill"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Firefox crashed previously. Would you like to restore your tabs?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Ecosia crashed previously. Would you like to restore your tabs?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Mark as Read"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Start Browsing"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox crashed"; +"Oops! Firefox crashed" = "Oops! Ecosia crashed"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Open articles in Reader View by tapping the book icon when it appears in the title bar."; @@ -905,7 +905,7 @@ "Open Tabs" = "Open Tabs"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox cannot open the page because it has an invalid address."; +"OpenURL.Error.Message" = "Ecosia cannot open the page because it has an invalid address."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Cannot Open Page"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "This allows you to save the image to your Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox would like to access your Photos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia would like to access your Photos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Close"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "The data is invalid"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Go to device ‘Settings’ > ‘Firefox’. Allow Firefox to access camera."; +"ScanQRCode.PermissionError.Message.v100" = "Go to device ‘Settings’ > ‘Ecosia’. Allow Ecosia to access camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scan QR Code"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Switch to tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "The new search engine will appear in the quick search bar."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Mozilla can fix the problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Ecosia can fix the problem?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Send Feedback"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Send Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Firefox Account available to sync."; +"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Ecosia Account available to sync."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Please open Firefox, go to Settings and sign in to continue."; +"SendTo.NotSignedIn.Message" = "Please open Ecosia, go to Settings and sign in to continue."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "You are not signed in to your Firefox Account."; +"SendTo.NotSignedIn.Title" = "You are not signed in to your Ecosia Account."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tap to begin"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "View"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Data Management"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox will stop syncing with your account, but won’t delete any of your browsing data on this device."; +"Settings.Disconnect.Body" = "Ecosia will stop syncing with your account, but won’t delete any of your browsing data on this device."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Disconnect Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync Settings"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Choose what displays as the homepage."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Choose what’s included on the Firefox homepage."; +"Settings.Home.Option.Description.v101" = "Choose what’s included on the Ecosia homepage."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Jump Back In"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Homepage"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Choose what you see when you return to Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Choose what you see when you return to Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Last tab"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Wallpaper"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox wallpaper, amethyst pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia wallpaper, amethyst pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox wallpaper, beach hills pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia wallpaper, beach hills pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox wallpaper, cerulean pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia wallpaper, cerulean pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Default clear wallpaper."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox wallpaper, sunrise pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia wallpaper, sunrise pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Homepage wallpaper cycle toggle"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox wallpaper, twilight hills pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia wallpaper, twilight hills pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Classic %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limited Edition"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Change wallpaper by tapping Firefox homepage logo"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Change wallpaper by tapping Ecosia homepage logo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Wallpaper"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Custom"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Homepage"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Block Images"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "When Opening Firefox"; +"Settings.OfferClipboardBar.Status" = "When Opening Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "When opening %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "When opening Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "When opening Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Offer to Open Copied Links"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Learn More."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strives to only collect what we need to provide and improve Firefox for everyone."; +"Settings.SendUsage.Message" = "Ecosia strives to only collect what we need to provide and improve Ecosia for everyone."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send Usage Data"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Open New Tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Shortcuts"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Learn More."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox may install and run studies from time to time."; +"Settings.Studies.Toggle.Message" = "Ecosia may install and run studies from time to time."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studies"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Load in Background"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Open in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Open in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Search in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Search in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copy"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Paste & Go"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Search with Firefox"; +"UIMenuItem.SearchWithFirefox" = "Search with Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "unread"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Yesterday"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "You don't have any tabs open in Firefox on your other devices."; +"You don’t have any tabs open in Firefox on your other devices." = "You don't have any tabs open in Ecosia on your other devices."; /* Your Rights settings section title */ "Your Rights" = "Your Rights"; diff --git a/firefox-ios/Shared/en-GB.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/en-GB.lproj/PrivateBrowsing.strings index ff9695fbf6da3..e972f94851e43 100644 --- a/firefox-ios/Shared/en-GB.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/en-GB.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Open in New Private Tab"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox won't remember any of your history or cookies, but new bookmarks will be saved."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia won't remember any of your history or cookies, but new bookmarks will be saved."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Learn More"; diff --git a/firefox-ios/Shared/en-GB.lproj/Today.strings b/firefox-ios/Shared/en-GB.lproj/Today.strings index c9a039841930d..a918e85de9fc2 100644 --- a/firefox-ios/Shared/en-GB.lproj/Today.strings +++ b/firefox-ios/Shared/en-GB.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Close Private Tabs"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Firefox shortcuts to your Home screen."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Ecosia shortcuts to your Home screen."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Go to copied link"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No open tabs."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Open Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Open Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Private Search"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Add a Firefox shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; +"TodayWidget.QuickActionGalleryDescription" = "Add a Ecosia shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Quick Actions"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox Shortcuts"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia Shortcuts"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Add shortcuts to your open tabs."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Quick View"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Search in Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Search in Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Search in Private Tab"; diff --git a/firefox-ios/Shared/en-US.lproj/Default Browser.strings b/firefox-ios/Shared/en-US.lproj/Default Browser.strings index d717a1aa0f10d..0652aef635934 100644 --- a/firefox-ios/Shared/en-US.lproj/Default Browser.strings +++ b/firefox-ios/Shared/en-US.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Firefox your default browser is a vote for an open, accessible internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Ecosia your default browser is a vote for an open, accessible internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Default to a Better Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Learn How"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Firefox."; +"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Firefox as your default browser to make speed, safety, and privacy automatic."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Ecosia as your default browser to make speed, safety, and privacy automatic."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Elevate Everyday Browsing"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox Has Privacy Covered"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia Has Privacy Covered"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Switch Your Default Browser"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tap Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Select Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Select Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/en-US.lproj/Intro.strings b/firefox-ios/Shared/en-US.lproj/Intro.strings index bc4663239633e..860a24bba7897 100644 --- a/firefox-ios/Shared/en-US.lproj/Intro.strings +++ b/firefox-ios/Shared/en-US.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Sign In"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Welcome to Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Welcome to Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Start Browsing"; diff --git a/firefox-ios/Shared/en-US.lproj/Localizable.strings b/firefox-ios/Shared/en-US.lproj/Localizable.strings index 161af25ac28b8..be8be13ab1de4 100644 --- a/firefox-ios/Shared/en-US.lproj/Localizable.strings +++ b/firefox-ios/Shared/en-US.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "It may be a misconfiguration or tampering by an attacker. Proceed if you accept the potential risk."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website."; +"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their website improperly. To protect your information from being stolen, Ecosia has not connected to this website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "This Connection is Untrusted"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Home Page"; +"Firefox.HomePage.Title" = "Ecosia Home Page"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Customize Homepage"; @@ -377,7 +377,7 @@ "Forward" = "Forward"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Manage Account & Devices"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Sign In with Your Camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "On your computer open Firefox and go to firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "On your computer open Ecosia and go to ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Ready to Scan"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sync Now"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync has connected to %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync has connected to %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Connected"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ has been successfully disconnected."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync Disconnected"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync Disconnected"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logo, change the wallpaper."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logo, change the wallpaper."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Help"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Sign in to view a list of tabs from your other devices."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "History"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Learn More"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "To use the AutoFill feature for Firefox, you must have a device passcode enabled."; +"Logins.PasscodeRequirement.Warning" = "To use the AutoFill feature for Ecosia, you must have a device passcode enabled."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Take your passwords everywhere"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill Firefox Passwords"; +"Logins.WelcomeView.Title2" = "AutoFill Ecosia Passwords"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Turn on AutoFill"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Firefox crashed previously. Would you like to restore your tabs?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Ecosia crashed previously. Would you like to restore your tabs?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Mark as Read"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Start Browsing"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox crashed"; +"Oops! Firefox crashed" = "Oops! Ecosia crashed"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Open articles in Reader View by tapping the book icon when it appears in the title bar."; @@ -905,7 +905,7 @@ "Open Tabs" = "Open Tabs"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox cannot open the page because it has an invalid address."; +"OpenURL.Error.Message" = "Ecosia cannot open the page because it has an invalid address."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Cannot Open Page"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "This allows you to save the image to your Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox would like to access your Photos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia would like to access your Photos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Close"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "The data is invalid"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Go to device ‘Settings’ > ‘Firefox’. Allow Firefox to access camera."; +"ScanQRCode.PermissionError.Message.v100" = "Go to device ‘Settings’ > ‘Ecosia’. Allow Ecosia to access camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scan QR Code"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Switch to tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "The new search engine will appear in the quick search bar."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Mozilla can fix the problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Ecosia can fix the problem?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Send Feedback"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Send Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Firefox Account available to sync."; +"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Ecosia Account available to sync."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Please open Firefox, go to Settings and sign in to continue."; +"SendTo.NotSignedIn.Message" = "Please open Ecosia, go to Settings and sign in to continue."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "You are not signed in to your Firefox Account."; +"SendTo.NotSignedIn.Title" = "You are not signed in to your Ecosia Account."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tap to begin"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "View"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Data Management"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox will stop syncing with your account, but won’t delete any of your browsing data on this device."; +"Settings.Disconnect.Body" = "Ecosia will stop syncing with your account, but won’t delete any of your browsing data on this device."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Disconnect Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync Settings"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Choose what displays as the homepage."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Choose what’s included on the Firefox homepage."; +"Settings.Home.Option.Description.v101" = "Choose what’s included on the Ecosia homepage."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Jump Back In"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Homepage"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Choose what you see when you return to Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Choose what you see when you return to Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Last tab"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Wallpaper"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox wallpaper, amethyst pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia wallpaper, amethyst pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox wallpaper, beach hills pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia wallpaper, beach hills pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox wallpaper, cerulean pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia wallpaper, cerulean pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Default clear wallpaper."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox wallpaper, sunrise pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia wallpaper, sunrise pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Homepage wallpaper cycle toggle"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox wallpaper, twilight hills pattern."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia wallpaper, twilight hills pattern."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Classic %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limited Edition"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Change wallpaper by tapping Firefox homepage logo"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Change wallpaper by tapping Ecosia homepage logo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Wallpaper"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Custom"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Homepage"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Block Images"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "When Opening Firefox"; +"Settings.OfferClipboardBar.Status" = "When Opening Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "When opening %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "When opening Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "When opening Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Offer to Open Copied Links"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Learn More."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strives to only collect what we need to provide and improve Firefox for everyone."; +"Settings.SendUsage.Message" = "Ecosia strives to only collect what we need to provide and improve Ecosia for everyone."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send Usage Data"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Open New Tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Shortcuts"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Learn More."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox may install and run studies from time to time."; +"Settings.Studies.Toggle.Message" = "Ecosia may install and run studies from time to time."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studies"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Load in Background"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Open in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Open in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Search in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Search in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copy"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Paste & Go"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Search with Firefox"; +"UIMenuItem.SearchWithFirefox" = "Search with Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "unread"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Yesterday"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "You don’t have any tabs open in Firefox on your other devices."; +"You don’t have any tabs open in Firefox on your other devices." = "You don’t have any tabs open in Ecosia on your other devices."; /* Your Rights settings section title */ "Your Rights" = "Your Rights"; diff --git a/firefox-ios/Shared/en-US.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/en-US.lproj/PrivateBrowsing.strings index 1afee75ffac1b..c4986950c5450 100644 --- a/firefox-ios/Shared/en-US.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/en-US.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Open in New Private Tab"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox won’t remember any of your history or cookies, but new bookmarks will be saved."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia won’t remember any of your history or cookies, but new bookmarks will be saved."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Learn More"; diff --git a/firefox-ios/Shared/en-US.lproj/Today.strings b/firefox-ios/Shared/en-US.lproj/Today.strings index de41f6f505735..70e89ce819652 100644 --- a/firefox-ios/Shared/en-US.lproj/Today.strings +++ b/firefox-ios/Shared/en-US.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Close\nPrivate Tabs"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Firefox shortcuts to your Home screen."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Ecosia shortcuts to your Home screen."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Go to copied link"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No open tabs."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Open Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Open Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Private Search"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Add a Firefox shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; +"TodayWidget.QuickActionGalleryDescription" = "Add a Ecosia shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Quick Actions"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox Shortcuts"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia Shortcuts"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Add shortcuts to your open tabs."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Quick View"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Search in\nFirefox"; +"TodayWidget.SearchInFirefoxV2" = "Search in\nEcosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Search in\nPrivate Tab"; diff --git a/firefox-ios/Shared/en.lproj/Default Browser.strings b/firefox-ios/Shared/en.lproj/Default Browser.strings index d717a1aa0f10d..0652aef635934 100644 --- a/firefox-ios/Shared/en.lproj/Default Browser.strings +++ b/firefox-ios/Shared/en.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Firefox your default browser is a vote for an open, accessible internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Making Ecosia your default browser is a vote for an open, accessible internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Default to a Better Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Learn How"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Firefox."; +"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Firefox as your default browser to make speed, safety, and privacy automatic."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Choose Ecosia as your default browser to make speed, safety, and privacy automatic."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Elevate Everyday Browsing"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blocks 3,000+ trackers per user each month on average. Make us your default browser for privacy peace of mind."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox Has Privacy Covered"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia Has Privacy Covered"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Switch Your Default Browser"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tap Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Select Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Select Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/en.lproj/Intro.strings b/firefox-ios/Shared/en.lproj/Intro.strings index bc4663239633e..860a24bba7897 100644 --- a/firefox-ios/Shared/en.lproj/Intro.strings +++ b/firefox-ios/Shared/en.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Sign In"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Welcome to Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Welcome to Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Start Browsing"; diff --git a/firefox-ios/Shared/en.lproj/Localizable.strings b/firefox-ios/Shared/en.lproj/Localizable.strings index 77cb40a38fb6e..2420d34f7ad19 100644 --- a/firefox-ios/Shared/en.lproj/Localizable.strings +++ b/firefox-ios/Shared/en.lproj/Localizable.strings @@ -260,7 +260,7 @@ "ErrorPages.AdvancedWarning2.Text" = "It may be a misconfiguration or tampering by an attacker. Proceed if you accept the potential risk."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website."; +"ErrorPages.CertWarning.Description" = "The owner of %@ has configured their website improperly. To protect your information from being stolen, Ecosia has not connected to this website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "This Connection is Untrusted"; @@ -281,13 +281,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Home Page"; +"Firefox.HomePage.Title" = "Ecosia Home Page"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Forward"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Manage Account & Devices"; @@ -299,19 +299,19 @@ "FxA.SyncNow" = "Sync Now"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync has connected to %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync has connected to %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Connected"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "This device has been successfully disconnected from Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync Disconnected"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "A device has disconnected from Ecosia Sync"; /* Body of a notification displayed when named device has been disconnected from FxA. %@ refers to the name of the disconnected device. */ "FxAPush_DeviceDisconnected_body" = "%@ has been successfully disconnected."; @@ -350,7 +350,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Sign in to view a list of tabs from your other devices."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "History"; @@ -470,7 +470,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Firefox crashed previously. Would you like to restore your tabs?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Looks like Ecosia crashed previously. Would you like to restore your tabs?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Mark as Read"; @@ -566,7 +566,7 @@ "Okay" = "Okay"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox crashed"; +"Oops! Firefox crashed" = "Oops! Ecosia crashed"; /* See http://mzl.la/1G7uHo7 */ "Open Settings" = "Open Settings"; @@ -578,7 +578,7 @@ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Open articles in Reader View by tapping the book icon when it appears in the title bar."; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox cannot open the page because it has an invalid address."; +"OpenURL.Error.Message" = "Ecosia cannot open the page because it has an invalid address."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Cannot Open Page"; @@ -590,7 +590,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "This allows you to save the image to your Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox would like to access your Photos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia would like to access your Photos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Close"; @@ -704,7 +704,7 @@ "Send Report" = "Send Report"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Mozilla can fix the problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Send a crash report so Ecosia can fix the problem?"; /* Button title for cancelling share screen */ "SendTo.Cancel.Button" = "Cancel"; @@ -725,13 +725,13 @@ "SendTo.NavBar.Title" = "Send Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Firefox Account available to sync."; +"SendTo.NoDevicesFound.Message" = "You don’t have any other devices connected to this Ecosia Account available to sync."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Please open Firefox, go to Settings and sign in to continue."; +"SendTo.NotSignedIn.Message" = "Please open Ecosia, go to Settings and sign in to continue."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "You are not signed in to your Firefox Account."; +"SendTo.NotSignedIn.Title" = "You are not signed in to your Ecosia Account."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -740,7 +740,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tap to begin"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "View"; @@ -809,7 +809,7 @@ "Settings.DataManagement.Title" = "Data Management"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox will stop syncing with your account, but won’t delete any of your browsing data on this device."; +"Settings.Disconnect.Body" = "Ecosia will stop syncing with your account, but won’t delete any of your browsing data on this device."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Disconnect Sync"; @@ -872,7 +872,7 @@ "Settings.FxA.Sync.SectionName" = "Sync Settings"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -896,7 +896,7 @@ "Settings.NewTab.Option.BlankPage" = "Blank Page"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Homepage"; @@ -914,7 +914,7 @@ "Settings.NewTab.TopSectionNameFooter" = "Choose what to load when opening a new tab"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "When Opening Firefox"; +"Settings.OfferClipboardBar.Status" = "When Opening Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Offer to Open Copied Links"; @@ -938,7 +938,7 @@ "Settings.SendUsage.Link" = "Learn More."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strives to only collect what we need to provide and improve Firefox for everyone."; +"Settings.SendUsage.Message" = "Ecosia strives to only collect what we need to provide and improve Ecosia for everyone."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send Usage Data"; @@ -956,7 +956,7 @@ "Settings.Siri.OpenTabShortcut" = "Open New Tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Use Siri shortcuts to quickly open Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Shortcuts"; @@ -1031,13 +1031,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Load in Background"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Loading in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Open in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Open in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Search in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Search in Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Show Search Suggestions"; @@ -1139,7 +1139,7 @@ "UIMenuItem.PasteGo" = "Paste & Go"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Search with Firefox"; +"UIMenuItem.SearchWithFirefox" = "Search with Ecosia"; /* Debug option */ "Use stage servers" = "Use stage servers"; @@ -1163,7 +1163,7 @@ "Yesterday" = "Yesterday"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "You don’t have any tabs open in Firefox on your other devices."; +"You don’t have any tabs open in Firefox on your other devices." = "You don’t have any tabs open in Ecosia on your other devices."; /* Tile title for YouTube */ "YouTube" = "YouTube"; @@ -1175,7 +1175,7 @@ "fxa.signin.camera-signin" = "Sign In with Your Camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "On your computer open Firefox and go to firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "On your computer open Ecosia and go to ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Ready to Scan"; diff --git a/firefox-ios/Shared/en.lproj/Today.strings b/firefox-ios/Shared/en.lproj/Today.strings index 842fc605fe64b..4351e24f51364 100644 --- a/firefox-ios/Shared/en.lproj/Today.strings +++ b/firefox-ios/Shared/en.lproj/Today.strings @@ -6,7 +6,7 @@ Private Tabs"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Firefox shortcuts to your Home screen."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Add Ecosia shortcuts to your Home screen."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Go to copied link"; @@ -25,13 +25,13 @@ Copied Link"; "TodayWidget.NoOpenTabsLabel" = "No open tabs."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Open Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Open Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Private Search"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Add a Firefox shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; +"TodayWidget.QuickActionGalleryDescription" = "Add a Ecosia shortcut to your Home screen. After adding the widget, touch and hold to edit it and select a different shortcut."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Quick Actions"; @@ -44,7 +44,7 @@ Copied Link"; /* Search in Firefox. Do not translate the word Firefox */ "TodayWidget.SearchInFirefoxV2" = "Search in -Firefox"; +Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Search in diff --git a/firefox-ios/Shared/eo.lproj/Default Browser.strings b/firefox-ios/Shared/eo.lproj/Default Browser.strings index b83772bd86304..9e84405d2edaa 100644 --- a/firefox-ios/Shared/eo.lproj/Default Browser.strings +++ b/firefox-ios/Shared/eo.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Igi Firefox via norma retumilo egalas al voĉdono por malfermita, alirebla interreto."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Igi Ecosia via norma retumilo egalas al voĉdono por malfermita, alirebla interreto."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Norme iri al pli bona interreto"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Lernu kiel fari"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Aŭtomate malfermi ligilon en retejoj, retpoŝtoj kaj mesaĝoj per Firefox."; +"DefaultBrowserCard.Description" = "Aŭtomate malfermi ligilon en retejoj, retpoŝtoj kaj mesaĝoj per Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Elektu Firefox kiel vian norman retumilon por aŭtomate havi rapidecon, sekurecon kaj privatecon."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Elektu Ecosia kiel vian norman retumilon por aŭtomate havi rapidecon, sekurecon kaj privatecon."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Plibonigu vian ĉiutagan retumon"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokas 3,000+ spurilojn per uzanto, mezume, ĉiumonate. Igu nin via norma retumilo por privateca trankvilo."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokas 3,000+ spurilojn per uzanto, mezume, ĉiumonate. Igu nin via norma retumilo por privateca trankvilo."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox protektas vian privatecon"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia protektas vian privatecon"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Ŝanĝi vian norman retumilon"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tuŝetu Norma retumila programo"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Elektu Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Elektu Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Norma retumila programo"; diff --git a/firefox-ios/Shared/eo.lproj/Intro.strings b/firefox-ios/Shared/eo.lproj/Intro.strings index 27a1fa30a28c5..16765832b5356 100644 --- a/firefox-ios/Shared/eo.lproj/Intro.strings +++ b/firefox-ios/Shared/eo.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Komenci seancon"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bonvenon al Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bonvenon al Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Komenci retumi"; diff --git a/firefox-ios/Shared/eo.lproj/Localizable.strings b/firefox-ios/Shared/eo.lproj/Localizable.strings index a58f8e4d31324..ad3f6450d12d6 100644 --- a/firefox-ios/Shared/eo.lproj/Localizable.strings +++ b/firefox-ios/Shared/eo.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Tion povas okazigi misagordo aŭ atakanto. Daŭrigu se vi akceptas la eblan riskon."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "La posedanto de %@ malĝuste agordis sian retejon. Por protekti vian informon de ŝtelo, Firefox ne konektiĝis al tiu ĉi retejo."; +"ErrorPages.CertWarning.Description" = "La posedanto de %@ malĝuste agordis sian retejon. Por protekti vian informon de ŝtelo, Ecosia ne konektiĝis al tiu ĉi retejo."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Tiu ĉi konekto ne estas fidata"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Eka paĝo de Firefox"; +"Firefox.HomePage.Title" = "Eka paĝo de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personecigi la ekan paĝon"; @@ -377,7 +377,7 @@ "Forward" = "Antaŭen"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Konto de Firefox"; +"FxA.FirefoxAccount" = "Konto de Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administri kontojn kaj aparatojn"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Komencu seancon per via filmilo"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "En via komputilo malfermu Firefox kaj vizitu firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "En via komputilo malfermu Ecosia kaj vizitu ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Preta skani"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Speguli nun"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Spegulado de Firefox konektiĝis al %@"; +"FxAPush_DeviceConnected_body" = "Spegulado de Ecosia konektiĝis al %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Spegulado konektiĝis"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ estis sukcese malkonektita."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Tiu ĉi aparato estis sukcese malkonektita de la Spegulado de Firefox."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Tiu ĉi aparato estis sukcese malkonektita de la Spegulado de Ecosia."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Spegulado malkonektiĝis"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Spegulado malkonektiĝis"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Aparato malkonektiĝis de Spegulado de Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Aparato malkonektiĝis de Spegulado de Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Emblemo de Firefox, ŝanĝi ekranfonon."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Emblemo de Ecosia, ŝanĝi ekranfonon."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Helpo"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Komencu seancon por vidi liston de langetoj de viaj aliaj aparatoj."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Spegulado de Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Spegulado de Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historio"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Pli da informo"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Por uzi la aŭtomata pleniga trajto en Firefox, vi devas unue havi aparatan paskodon aktivita."; +"Logins.PasscodeRequirement.Warning" = "Por uzi la aŭtomata pleniga trajto en Ecosia, vi devas unue havi aparatan paskodon aktivita."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Kunportu viajn pasvortojn ĉien"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Aŭtomate plenigi pasvortojn de Firefox"; +"Logins.WelcomeView.Title2" = "Aŭtomate plenigi pasvortojn de Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Ŝalti aŭtomatan plenigon"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "KONSERVITAJ AKREDITILOJ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ŝajne Firefox antaŭe paneis. Ĉu vi ŝatus restarigi viajn langetojn?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ŝajne Ecosia antaŭe paneis. Ĉu vi ŝatus restarigi viajn langetojn?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marki kiel legita"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Komenci retumi"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Fuŝ! Firefox paneis"; +"Oops! Firefox crashed" = "Fuŝ! Ecosia paneis"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Malfermu artikolojn en legila vido per tuŝeto sur la emblemo de libro, kiam ĝi aperas en la titolstrio."; @@ -905,7 +905,7 @@ "Open Tabs" = "Malfermitaj langetoj"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ne povas malfermi la paĝon ĉar ĝi havas nevalidan adreson."; +"OpenURL.Error.Message" = "Ecosia ne povas malfermi la paĝon ĉar ĝi havas nevalidan adreson."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Ne eblas malfermi la paĝon"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Tio permesas al vi konservi la bildon en via filmila rulo."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ŝatus aliri viajn fotojn"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ŝatus aliri viajn fotojn"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Fermi"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Nevalidaj datumoj"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Iru al ‘Agordoj‘ > ‘Firefox‘ en la aparato. Permesu al Firefox aliri la filmilon."; +"ScanQRCode.PermissionError.Message.v100" = "Iru al ‘Agordoj‘ > ‘Ecosia‘ en la aparato. Permesu al Ecosia aliri la filmilon."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skani kodon QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Iri al langeto"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugestoj de Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugestoj de Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "La nova serĉilo aperos en la rapida serĉostrio."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Akcepti"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Ĉu sendi raporton pri paneo, tiel ke Mozilla povas solvi la problemon?"; +"Send a crash report so Mozilla can fix the problem?" = "Ĉu sendi raporton pri paneo, tiel ke Ecosia povas solvi la problemon?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Sendi komentojn"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Sendi langeton"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Vi ne havas alian aparaton preta speguli kaj konektita al tiu ĉi konto de Firefox."; +"SendTo.NoDevicesFound.Message" = "Vi ne havas alian aparaton preta speguli kaj konektita al tiu ĉi konto de Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Bonvolu malfermi Firefox, iru al Agordoj kaj komencu seancon por daŭrigi."; +"SendTo.NotSignedIn.Message" = "Bonvolu malfermi Ecosia, iru al Agordoj kaj komencu seancon por daŭrigi."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Vi ne komencis seancon en via konto de Firefox."; +"SendTo.NotSignedIn.Title" = "Vi ne komencis seancon en via konto de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Sendi"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tuŝetu por komenci"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Spegulado de Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Spegulado de Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vidi"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Administro de datumoj"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ne plu spegulos vian konton, sed ĝi ne forigos iun ajn el viaj retumaj datumoj en tiu ĉi aparato."; +"Settings.Disconnect.Body" = "Ecosia ne plu spegulos vian konton, sed ĝi ne forigos iun ajn el viaj retumaj datumoj en tiu ĉi aparato."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Malkonekti de Spegulado"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Spegulaj agordoj"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Konto de Firefox"; +"Settings.FxA.Title" = "Konto de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Ĝenerala"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Elektu kion montri kiel ekan paĝon."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Elektu kion inkluzivi en la eka paĝo de Firefox."; +"Settings.Home.Option.Description.v101" = "Elektu kion inkluzivi en la eka paĝo de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Reiri"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Eka paĝo"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Elektu kion vi vidas kiam vi reiras al Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Elektu kion vi vidas kiam vi reiras al Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Lasta langeto"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Ekranfono"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ekranfono de Firefox, modelo de ametisto."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ekranfono de Ecosia, modelo de ametisto."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ekranfono de Firefox, modelo de montoj ĉe strando."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ekranfono de Ecosia, modelo de montoj ĉe strando."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ekranfono de Firefox, modelo ĉielbula."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ekranfono de Ecosia, modelo ĉielbula."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Norma malplena ekranfono."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ekranfono de Firefox, modelo de eliranta suno."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ekranfono de Ecosia, modelo de eliranta suno."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Ŝaltilo por la bukla ŝanĝiĝo de ekranfono de la eka paĝo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ekranfono de Firefox, modelo de monton je sunsubiro."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ekranfono de Ecosia, modelo de monton je sunsubiro."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasika %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limigita eldono"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ŝanĝi ekranfonon per tuŝeto de la emblemo de Firefox en la eka paĝo"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ŝanĝi ekranfonon per tuŝeto de la emblemo de Ecosia en la eka paĝo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Ekranfono"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personecigita"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Eka paĝo de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Eka paĝo de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Hejmpaĝo"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloki bildojn"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Dum malfermo de Firefox"; +"Settings.OfferClipboardBar.Status" = "Dum malfermo de Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Je malfermo de %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Dum malfermo de Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Dum malfermo de Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Proponi malfermi kopiitajn ligilojn"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Pli da informo."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla penas kolekti nur la datumojn nepre bezonatajn por provizi kaj plibonigi Firefox por ĉiuj."; +"Settings.SendUsage.Message" = "Ecosia penas kolekti nur la datumojn nepre bezonatajn por provizi kaj plibonigi Ecosia por ĉiuj."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Sendi datumojn pri uzo"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Malfermi novan langeton"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Uzi ŝparvojojn de Siri por rapide malfermi Firefox per Siri"; +"Settings.Siri.SectionDescription" = "Uzi ŝparvojojn de Siri por rapide malfermi Ecosia per Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Ŝparvojoj de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Pli da informo."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox povas de tempo al tempo instali kaj fari studojn."; +"Settings.Studies.Toggle.Message" = "Ecosia povas de tempo al tempo instali kaj fari studojn."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studoj"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Fone ŝargi"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Ŝargata en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ŝargata en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Malfermi en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Malfermi en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Serĉi en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Serĉi en Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopii"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Alglui kaj iri"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Serĉi per Firefox"; +"UIMenuItem.SearchWithFirefox" = "Serĉi per Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ne legita"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Hieraŭ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Vi ne havas langetojn malfermitajn en Firefox en viaj aliaj aparatoj."; +"You don’t have any tabs open in Firefox on your other devices." = "Vi ne havas langetojn malfermitajn en Ecosia en viaj aliaj aparatoj."; /* Your Rights settings section title */ "Your Rights" = "Viaj rajtoj"; diff --git a/firefox-ios/Shared/eo.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/eo.lproj/PrivateBrowsing.strings index c7a658930c43d..12a9b56153bce 100644 --- a/firefox-ios/Shared/eo.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/eo.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Malfermi en nova privata langeto"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ne memoros vian historion aŭ kuketojn, sed novaj legosignoj estos konservitaj."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ne memoros vian historion aŭ kuketojn, sed novaj legosignoj estos konservitaj."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Pli da informo"; diff --git a/firefox-ios/Shared/eo.lproj/Today.strings b/firefox-ios/Shared/eo.lproj/Today.strings index 85df1c8b5e0c0..8f711064ad829 100644 --- a/firefox-ios/Shared/eo.lproj/Today.strings +++ b/firefox-ios/Shared/eo.lproj/Today.strings @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Neniu malfermita langeto."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Malfermi Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Malfermi Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privata serĉo"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Aldonu ŝparvojon de Firefox al via hejmekrano. Post la aldono de la elemento, tuŝu longe por modifi ĝin kaj elekti alian ŝparvojon."; +"TodayWidget.QuickActionGalleryDescription" = "Aldonu ŝparvojon de Ecosia al via hejmekrano. Post la aldono de la elemento, tuŝu longe por modifi ĝin kaj elekti alian ŝparvojon."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Rapidaj agoj"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Ŝparvojoj de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ŝparvojoj de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Aldoni ŝparvojojn al viaj malfermitaj langetoj."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Rapida vido"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Serĉi en\nFirefox"; +"TodayWidget.SearchInFirefoxV2" = "Serĉi en\nEcosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Serĉi en\nprivata langeto"; diff --git a/firefox-ios/Shared/es-AR.lproj/Default Browser.strings b/firefox-ios/Shared/es-AR.lproj/Default Browser.strings index 9755a4b07b82e..2d1e07b1b4c55 100644 --- a/firefox-ios/Shared/es-AR.lproj/Default Browser.strings +++ b/firefox-ios/Shared/es-AR.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Firefox tu navegador predeterminado es votar por una Internet abierta y accesible."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Ecosia tu navegador predeterminado es votar por una Internet abierta y accesible."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Pasá a una mejor Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Aprender cómo"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Configurar enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox."; +"DefaultBrowserCard.Description" = "Configurar enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Elegí Firefox como tu navegador predeterminado para que la velocidad, la seguridad y la privacidad sean automáticas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Elegí Ecosia como tu navegador predeterminado para que la velocidad, la seguridad y la privacidad sean automáticas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Mejorá la navegación diaria"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloquea en promedio más de 3000 rastreadores por usuario cada mes. Hacenos tu navegador predeterminado para tu privacidad y tranquilidad."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloquea en promedio más de 3000 rastreadores por usuario cada mes. Hacenos tu navegador predeterminado para tu privacidad y tranquilidad."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox tiene la privacidad cubierta"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia tiene la privacidad cubierta"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambiá tu navegador predeterminado"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tocá la aplicación del navegador predeterminado"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleccioná Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Seleccioná Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplicación del navegador predeterminado"; diff --git a/firefox-ios/Shared/es-AR.lproj/Intro.strings b/firefox-ios/Shared/es-AR.lproj/Intro.strings index d41af54e96e02..3c8b56a5fd73e 100644 --- a/firefox-ios/Shared/es-AR.lproj/Intro.strings +++ b/firefox-ios/Shared/es-AR.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Iniciar sesión"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bienvenido a Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bienvenido a Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Empezar a navegar"; diff --git a/firefox-ios/Shared/es-AR.lproj/Localizable.strings b/firefox-ios/Shared/es-AR.lproj/Localizable.strings index 3279d6e88fd6b..3605ef2acf218 100644 --- a/firefox-ios/Shared/es-AR.lproj/Localizable.strings +++ b/firefox-ios/Shared/es-AR.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Puede ser una mala configuración o una falsificación de un atacante. Seguí adelante si aceptás el riesgo potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "EL dueño de %@ ha configurado incorrectamente el sitio. Para proteger tu información contra robo, Firefox no se ha conectado a este sitio web."; +"ErrorPages.CertWarning.Description" = "EL dueño de %@ ha configurado incorrectamente el sitio. Para proteger tu información contra robo, Ecosia no se ha conectado a este sitio web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Esta conexión no es confiable"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Página de inicio de Firefox"; +"Firefox.HomePage.Title" = "Página de inicio de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar inicio"; @@ -377,7 +377,7 @@ "Forward" = "Adelante"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cuenta de Firefox"; +"FxA.FirefoxAccount" = "Cuenta de Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administrar cuenta y dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Iniciá sesión con tu cámara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "En tu computadora, abrí Firefox y andá a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "En tu computadora, abrí Ecosia y andá a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Listo para escanear"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar ahora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync se ha conectado a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync se ha conectado a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ha sido desconectado exitosamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo se desconectó con éxito de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo se desconectó con éxito de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo fue desconectado de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo fue desconectado de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Firefox, cambiar el fondo de pantalla."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Ecosia, cambiar el fondo de pantalla."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ayuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Ingresá para ver una lista de pestañas de los otros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Conocer más"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para utilizar AutoFill en Firefox, tenés que tener habilitada una contraseña para el dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Para utilizar AutoFill en Ecosia, tenés que tener habilitada una contraseña para el dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Llevá tus contraseñas a todas partes"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autocompletar contraseñas de Firefox"; +"Logins.WelcomeView.Title2" = "Autocompletar contraseñas de Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Habilitar Autocompletar"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Firefox se cerró inesperadamente. ¿Te gustaría restaurar tus pestañas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Ecosia se cerró inesperadamente. ¿Te gustaría restaurar tus pestañas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como leído"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Empezar a navegar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "¡Ups! Firefox falló"; +"Oops! Firefox crashed" = "¡Ups! Ecosia falló"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abre artículos en la Vista de lectura tocando el ícono del libro cuando aparece en la barra de título."; @@ -905,7 +905,7 @@ "Open Tabs" = "Abrir pestañas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox no puede abrir la página porque tiene una dirección inválida."; +"OpenURL.Error.Message" = "Ecosia no puede abrir la página porque tiene una dirección inválida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "No se pudo abrir la página"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Esto le permite guardar la imagen en la galería."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Firefox le gustaría acceder a sus Fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Ecosia le gustaría acceder a sus Fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Cerrar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Los datos no son válidos"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ir a ‘Configuración’ > ‘Firefox’. Permitir que Firefox acceda a la cámara."; +"ScanQRCode.PermissionError.Message.v100" = "Ir a ‘Configuración’ > ‘Ecosia’. Permitir que Ecosia acceda a la cámara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Escanear código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Cambiar a pestaña"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugerencia de Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugerencia de Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "El nuevo buscador aparecerá en la barra de búsquedas rápidas."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Aceptar"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un informe de fallos para que Mozilla pueda arreglar el problema?"; +"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un informe de fallos para que Ecosia pueda arreglar el problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar opinión"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar pestaña"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "No tiene otros dispositivos conectados a esta cuenta de Firefox disponible para sincronizar."; +"SendTo.NoDevicesFound.Message" = "No tiene otros dispositivos conectados a esta cuenta de Ecosia disponible para sincronizar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Por favor abra Firefox, vaya a Configuración e inicie la sesión para continuar."; +"SendTo.NotSignedIn.Message" = "Por favor abra Ecosia, vaya a Configuración e inicie la sesión para continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "No inició la sesión en su cuenta de Firefox."; +"SendTo.NotSignedIn.Title" = "No inició la sesión en su cuenta de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tocá para comenzar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Administrador de datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox dejará de sincronizar su cuenta pero no eliminará ningún dato de navegación en este dispositivo."; +"Settings.Disconnect.Body" = "Ecosia dejará de sincronizar su cuenta pero no eliminará ningún dato de navegación en este dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectar Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Opciones de Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cuenta de Firefox"; +"Settings.FxA.Title" = "Cuenta de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Elegí lo que se mostrará como página de inicio."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Elegí lo que se incluirá en la página de inicio de Firefox."; +"Settings.Home.Option.Description.v101" = "Elegí lo que se incluirá en la página de inicio de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Volver a ver"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Página de inicio"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Seleccionar lo que se verá al volver a Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Seleccionar lo que se verá al volver a Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Última pestaña"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fondo de pantalla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Firefox, patrón de amatista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón de amatista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Firefox, diseño de colinas en la playa."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, diseño de colinas en la playa."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Firefox, patrón cerúleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón cerúleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fondo de pantalla claro predeterminado."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Firefox, patrón de amanecer."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón de amanecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Cambio de ciclo de fondo de pantalla del inicio"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Firefox, diseño de colinas al atardecer."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, diseño de colinas al atardecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ Clásico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edición limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambiar el fondo de pantalla tocando el logotipo de la página de inicio de Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambiar el fondo de pantalla tocando el logotipo de la página de inicio de Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fondo de pantalla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizar"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Página de Inicio de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Página de Inicio de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Página de inicio"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imágenes"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.Status" = "Al abrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Al abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Al abrir Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Oferta para abrir enlaces copiados"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Conocer más."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se esfuerza por recolectar solamente lo que necesitamos para proveer y mejorar Firefox para todos."; +"Settings.SendUsage.Message" = "Ecosia se esfuerza por recolectar solamente lo que necesitamos para proveer y mejorar Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar datos de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir nueva pestaña"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilizar los atajos de teclado de Siri para abrir Firefox rápidamente vía Siri"; +"Settings.Siri.SectionDescription" = "Utilizar los atajos de teclado de Siri para abrir Ecosia rápidamente vía Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atajos de teclado de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Conocer más."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox puede instalar y ejecutar estudios de vez en cuando."; +"Settings.Studies.Toggle.Message" = "Ecosia puede instalar y ejecutar estudios de vez en cuando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudios"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Pegar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Buscar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Buscar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "no leído"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ayer"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "No hay ninguna pestaña abierta en Firefox en los otros dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "No hay ninguna pestaña abierta en Ecosia en los otros dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Tus derechos"; diff --git a/firefox-ios/Shared/es-AR.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/es-AR.lproj/PrivateBrowsing.strings index f4754205908f8..d0d3ad1eda8b7 100644 --- a/firefox-ios/Shared/es-AR.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/es-AR.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir en nueva pestaña privada"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox no recordará tu historial ni tus cookies, pero se guardarán los nuevos marcadores."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia no recordará tu historial ni tus cookies, pero se guardarán los nuevos marcadores."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Conocer más"; diff --git a/firefox-ios/Shared/es-AR.lproj/Today.strings b/firefox-ios/Shared/es-AR.lproj/Today.strings index 058370ca1bd86..09febfd58b5e6 100644 --- a/firefox-ios/Shared/es-AR.lproj/Today.strings +++ b/firefox-ios/Shared/es-AR.lproj/Today.strings @@ -23,7 +23,7 @@ "TodayWidget.NoOpenTabsLabel" = "No hay pestañas abiertas."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Búsqueda privada"; @@ -35,7 +35,7 @@ "TodayWidget.QuickActionsGalleryTitle" = "Acciones rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Accesos directos de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Accesos directos de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Agregar atajos a las pestañas abiertas."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Buscar en Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Buscar en Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Buscar en pestaña privada"; diff --git a/firefox-ios/Shared/es-CL.lproj/Default Browser.strings b/firefox-ios/Shared/es-CL.lproj/Default Browser.strings index 18e73b6244d7f..b9d66a19631d2 100644 --- a/firefox-ios/Shared/es-CL.lproj/Default Browser.strings +++ b/firefox-ios/Shared/es-CL.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Firefox tu navegador predeterminado es votar por un Internet abierto y accesible."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Ecosia tu navegador predeterminado es votar por un Internet abierto y accesible."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Dale la prioridad a un mejor Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Aprender cómo"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox."; +"DefaultBrowserCard.Description" = "Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Firefox como tu navegador predeterminado para que la velocidad, la seguridad y la privacidad sean automáticas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Ecosia como tu navegador predeterminado para que la velocidad, la seguridad y la privacidad sean automáticas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Navegación diaria mejorada"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloquea en promedio más de 3.000 rastreadores por usuario al mes. Haznos tu navegador predeterminado pensando en tu privacidad y tranquilidad."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloquea en promedio más de 3.000 rastreadores por usuario al mes. Haznos tu navegador predeterminado pensando en tu privacidad y tranquilidad."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox tiene la privacidad cubierta"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia tiene la privacidad cubierta"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambia tu navegador predeterminado"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tocar aplicación de navegador predeterminada"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleccionar Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Seleccionar Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplicación de navegador predeterminada"; diff --git a/firefox-ios/Shared/es-CL.lproj/Intro.strings b/firefox-ios/Shared/es-CL.lproj/Intro.strings index 370bc1a2bb00a..0513085ee8bae 100644 --- a/firefox-ios/Shared/es-CL.lproj/Intro.strings +++ b/firefox-ios/Shared/es-CL.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Conectarse"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bienvenido a Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bienvenido a Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Empezar a navegar"; diff --git a/firefox-ios/Shared/es-CL.lproj/Localizable.strings b/firefox-ios/Shared/es-CL.lproj/Localizable.strings index 27279e1b52fe6..40fbf8cac817b 100644 --- a/firefox-ios/Shared/es-CL.lproj/Localizable.strings +++ b/firefox-ios/Shared/es-CL.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Puede ser una mala configuración o una modificación de atacante. Procede si aceptas el riesgo potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "El dueño de %@ ha configurado su sitio de forma inadecuada. Para proteger tu información de ser robada, Firefox no se ha conectado a este sitio."; +"ErrorPages.CertWarning.Description" = "El dueño de %@ ha configurado su sitio de forma inadecuada. Para proteger tu información de ser robada, Ecosia no se ha conectado a este sitio."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Esta conexión no es confiable"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Página de inicio de Firefox"; +"Firefox.HomePage.Title" = "Página de inicio de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar página de inicio"; @@ -377,7 +377,7 @@ "Forward" = "Siguiente"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cuenta de Firefox"; +"FxA.FirefoxAccount" = "Cuenta de Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administrar cuenta y dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Conéctate con tu cámara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Abre Firefox en tu computador y ve a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Abre Ecosia en tu computador y ve a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Listo para escanear"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar ahora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync se ha conectado a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync se ha conectado a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ha sido desconectado exitosamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo ha sido desconectado exitosamente de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo ha sido desconectado exitosamente de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo ha sido desconectado de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo ha sido desconectado de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Firefox, cambiar el fondo de pantalla."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Ecosia, cambiar el fondo de pantalla."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ayuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Conéctate para ver una lista de pestañas de tus otros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Aprender más"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para utilizar la función de autocompletar en Firefox, debes tener habilitada una contraseña para el dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Para utilizar la función de autocompletar en Ecosia, debes tener habilitada una contraseña para el dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Lleva tus contraseñas a todas partes"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autocompletar contraseñas en Firefox"; +"Logins.WelcomeView.Title2" = "Autocompletar contraseñas en Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activar autocompletado"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "CREDENCIALES GUARDADAS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Firefox se cayó antes. ¿Te gustaría restaurar tus pestañas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Ecosia se cayó antes. ¿Te gustaría restaurar tus pestañas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como leído"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Empezar a navegar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "¡Chuta! Firefox falló"; +"Oops! Firefox crashed" = "¡Chuta! Ecosia falló"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Para abrir artículos en la Vista del lector se debe tocar el icono del libro cuando aparece en la barra de título."; @@ -905,7 +905,7 @@ "Open Tabs" = "Pestañas abiertas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox no puede abrir la página porque tiene una dirección inválida."; +"OpenURL.Error.Message" = "Ecosia no puede abrir la página porque tiene una dirección inválida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "No se puede abrir la página"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Esto te permite guardar la imagen en el carrete."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox desea acceder a tus Fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia desea acceder a tus Fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Cerrar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Los datos no son válidos"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ve a ‘Ajustes’ > ‘Firefox’. Permitir que Firefox acceda a la cámara."; +"ScanQRCode.PermissionError.Message.v100" = "Ve a ‘Ajustes’ > ‘Ecosia’. Permitir que Ecosia acceda a la cámara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Escanear código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Cambiar a la pestaña"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugerencias de Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugerencias de Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "El nuevo motor de búsqueda aparecerá en la barra de búsqueda rápida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Aceptar"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un reporte de fallos a Mozilla para que pueda arreglar el problema?"; +"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un reporte de fallos a Ecosia para que pueda arreglar el problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar comentarios"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar pestaña"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "No tienes ningún otro dispositivo conectado a esta cuenta de Firefox disponible para sincronizar."; +"SendTo.NoDevicesFound.Message" = "No tienes ningún otro dispositivo conectado a esta cuenta de Ecosia disponible para sincronizar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Por favor, abre Firefox y ve a Ajustes para conectarte y continuar."; +"SendTo.NotSignedIn.Message" = "Por favor, abre Ecosia y ve a Ajustes para conectarte y continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "No estás conectado a tu cuenta de Firefox."; +"SendTo.NotSignedIn.Title" = "No estás conectado a tu cuenta de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Toca para empezar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gestión de datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox dejará de sincronizarse con tu cuenta, pero no se borrarán los datos de navegación del dispositivo."; +"Settings.Disconnect.Body" = "Ecosia dejará de sincronizarse con tu cuenta, pero no se borrarán los datos de navegación del dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectarse de Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Ajustes de Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cuenta de Firefox"; +"Settings.FxA.Title" = "Cuenta de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Elige lo que se mostrará como página de inicio."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Elige lo que se incluirá en la página de inicio de Firefox."; +"Settings.Home.Option.Description.v101" = "Elige lo que se incluirá en la página de inicio de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Regresar a"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Página de inicio"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Elige que ver cuando regreses a Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Elige que ver cuando regreses a Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Última pestaña"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fondo de pantalla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Firefox, patrón de amatista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón de amatista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Firefox, patrón de colinas de playa."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, patrón de colinas de playa."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Firefox, patrón cerúleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón cerúleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fondo de pantalla claro predeterminado."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Firefox, patrón de amanecer."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón de amanecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Botón de cambio cíclico del fondo de pantalla de la página de inicio"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Firefox, patrón de colinas crepusculares."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, patrón de colinas crepusculares."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clásico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edición limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia el fondo de pantalla tocando el logo de Firefox en la página de inicio"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia el fondo de pantalla tocando el logo de Ecosia en la página de inicio"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fondo de pantalla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizado"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Inicio de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Inicio de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Página de inicio"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imagenes"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.Status" = "Al abrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Al abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Al abrir Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ofrecer abrir enlaces copiados"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Aprender más."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se esfuerza por recolectar solo lo que necesita para proporcionar y mejorar Firefox para todos."; +"Settings.SendUsage.Message" = "Ecosia se esfuerza por recolectar solo lo que necesita para proporcionar y mejorar Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar datos de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir nueva pestaña"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilizar atajos de Siri para abrir rápidamente Firefox a través de Siri"; +"Settings.Siri.SectionDescription" = "Utilizar atajos de Siri para abrir rápidamente Ecosia a través de Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atajos de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Aprender más."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox podría instalar y realizar experimentos de vez en cuando."; +"Settings.Studies.Toggle.Message" = "Ecosia podría instalar y realizar experimentos de vez en cuando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudios"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Pegar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Buscar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Buscar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "sin leer"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ayer"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "No tienes ninguna pestaña abierta en Firefox en tus otros dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "No tienes ninguna pestaña abierta en Ecosia en tus otros dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Tus derechos"; diff --git a/firefox-ios/Shared/es-CL.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/es-CL.lproj/PrivateBrowsing.strings index 4a70c88617571..ae7a1983e0566 100644 --- a/firefox-ios/Shared/es-CL.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/es-CL.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir en una nueva pestaña privada"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox no recordará su historial de navegación o cookies, pero los nuevos marcadores serán guardados."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia no recordará su historial de navegación o cookies, pero los nuevos marcadores serán guardados."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Aprender más"; diff --git a/firefox-ios/Shared/es-CL.lproj/Today.strings b/firefox-ios/Shared/es-CL.lproj/Today.strings index 85b6c51d97387..e6ba4034e3097 100644 --- a/firefox-ios/Shared/es-CL.lproj/Today.strings +++ b/firefox-ios/Shared/es-CL.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Cerrar pestañas privadas"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Añade atajos de Firefox a tu pantalla de inicio."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Añade atajos de Ecosia a tu pantalla de inicio."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir al enlace copiado"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No hay pestañas abiertas."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Búsqueda privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Agrega un atajo de Firefox a tu pantalla de inicio. Después de añadir el widget, tocalo y mantenlo presionado para editarlo y seleccionar un acceso directo diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Agrega un atajo de Ecosia a tu pantalla de inicio. Después de añadir el widget, tocalo y mantenlo presionado para editarlo y seleccionar un acceso directo diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Acciones rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Atajos de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Atajos de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Añade atajos a tus pestañas abiertas."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Buscar en Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Buscar en Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Buscar en nueva pestaña privada"; diff --git a/firefox-ios/Shared/es-MX.lproj/Default Browser.strings b/firefox-ios/Shared/es-MX.lproj/Default Browser.strings index 9b91827b51046..e773298f1a5f5 100644 --- a/firefox-ios/Shared/es-MX.lproj/Default Browser.strings +++ b/firefox-ios/Shared/es-MX.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Firefox tu navegador predeterminado es votar por una Internet abierta y accesible."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Ecosia tu navegador predeterminado es votar por una Internet abierta y accesible."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Predeterminado a una mejor Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Aprende a hacerlo"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox."; +"DefaultBrowserCard.Description" = "Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Firefox como tu navegador predeterminado para que la velocidad, la seguridad y la privacidad sean automáticas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Ecosia como tu navegador predeterminado para que la velocidad, la seguridad y la privacidad sean automáticas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Mejora tu navegación diaria"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloquea en promedio más de 3,000 rastreadores por usuario cada mes. Elígenos como tu navegador predeterminado para tu privacidad y tranquilidad."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloquea en promedio más de 3,000 rastreadores por usuario cada mes. Elígenos como tu navegador predeterminado para tu privacidad y tranquilidad."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox tiene la privacidad cubierta"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia tiene la privacidad cubierta"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambia tu navegador predeterminado"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Pulsar la Aplicación del navegador por defecto"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleccionar Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Seleccionar Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplicación del navegador por defecto"; diff --git a/firefox-ios/Shared/es-MX.lproj/Intro.strings b/firefox-ios/Shared/es-MX.lproj/Intro.strings index 9a2fd6ae833d6..ec386dd1b547b 100644 --- a/firefox-ios/Shared/es-MX.lproj/Intro.strings +++ b/firefox-ios/Shared/es-MX.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Iniciar sesión"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bienvenido a Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bienvenido a Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Empieza a navegar"; diff --git a/firefox-ios/Shared/es-MX.lproj/Localizable.strings b/firefox-ios/Shared/es-MX.lproj/Localizable.strings index a5fd8c91103ab..70881c31dad0f 100644 --- a/firefox-ios/Shared/es-MX.lproj/Localizable.strings +++ b/firefox-ios/Shared/es-MX.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Puede ser una mala configuración o una falsificación de un atacante. Sigue adelante si aceptas el riesgo potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "El dueño de %@ ha configurado su sitio web incorrectamente. Para evitar que roben tu información, Firefox no se ha conectado a este sitio web."; +"ErrorPages.CertWarning.Description" = "El dueño de %@ ha configurado su sitio web incorrectamente. Para evitar que roben tu información, Ecosia no se ha conectado a este sitio web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Esta conexión no es confiable"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Página de inicio de Firefox"; +"Firefox.HomePage.Title" = "Página de inicio de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar página de inicio"; @@ -377,7 +377,7 @@ "Forward" = "Adelante"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cuenta de Firefox"; +"FxA.FirefoxAccount" = "Cuenta de Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administrar cuenta y dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Inicia sesión con tu cámara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "En tu computadora abre Firefox y ve a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "En tu computadora abre Ecosia y ve a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Listo para escanear"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar ahora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync se ha conectado a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync se ha conectado a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ha sido desconectado exitosamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo ha sido desconectado exitosamente de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo ha sido desconectado exitosamente de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo ha sido desconectado de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo ha sido desconectado de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Firefox, cambia el fondo de pantalla."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Ecosia, cambia el fondo de pantalla."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ayuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Inicia sesión para ver una lista de pestañas de tus otros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Saber más"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para usar la función de AutoFill en Firefox, debes habilitar un código de acceso del dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Para usar la función de AutoFill en Ecosia, debes habilitar un código de acceso del dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Llévate las contraseñas a todas partes"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill contraseñas de Firefox"; +"Logins.WelcomeView.Title2" = "AutoFill contraseñas de Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Habilitar AutoFill"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "INICIOS DE SESIÓN GUARDADOS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Firefox falló. ¿Te gustaría restablecer tus pestañas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Ecosia falló. ¿Te gustaría restablecer tus pestañas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como leído"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Empezar a navegar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "¡Ups! Firefox falló"; +"Oops! Firefox crashed" = "¡Ups! Ecosia falló"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abrir artículos en la vista de lectura al tocar el ícono del libro cuando aparece en la barra titular."; @@ -905,7 +905,7 @@ "Open Tabs" = "Abrir pestañas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox no puede abrir la página porque tiene una dirección inválida."; +"OpenURL.Error.Message" = "Ecosia no puede abrir la página porque tiene una dirección inválida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "No se puede abrir la página"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Esto te permite guardar la imagen en su rollo de cámara."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Firefox le gustaría acceder a tus fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Ecosia le gustaría acceder a tus fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Cerrar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Datos no válidos"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ir a ‘Configuración’ > ‘Firefox’. Permitir a Firefox el acceso de la cámara."; +"ScanQRCode.PermissionError.Message.v100" = "Ir a ‘Configuración’ > ‘Ecosia’. Permitir a Ecosia el acceso de la cámara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Escanear código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Cambiar a pestaña"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugerencia de Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugerencia de Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "El nuevo motor de búsqueda aparecerá en la barra de búsqueda rápida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Aceptar"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un reporte de fallo para que Mozilla pueda arreglar el problema?"; +"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un reporte de fallo para que Ecosia pueda arreglar el problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar comentarios"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar pestaña"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "No tienes otro dispositivo conectado a esta cuenta de Firefox disponible para sincronizar."; +"SendTo.NoDevicesFound.Message" = "No tienes otro dispositivo conectado a esta cuenta de Ecosia disponible para sincronizar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Por favor, abre Firefox, ve a los Ajustes e inicia sesión para continuar."; +"SendTo.NotSignedIn.Message" = "Por favor, abre Ecosia, ve a los Ajustes e inicia sesión para continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "No has iniciado sesión a tu cuenta de Firefox."; +"SendTo.NotSignedIn.Title" = "No has iniciado sesión a tu cuenta de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Presiona para iniciar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Administración de datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox detendrá la sincronización con tu cuenta, pero no eliminará nada en tu historial de navegación en este dispositivo."; +"Settings.Disconnect.Body" = "Ecosia detendrá la sincronización con tu cuenta, pero no eliminará nada en tu historial de navegación en este dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectar Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sincronizar configuraciones"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cuenta de Firefox"; +"Settings.FxA.Title" = "Cuenta de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Elige que mostrar como página de inicio."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Elige que incluir en tu página de inicio de Firefox."; +"Settings.Home.Option.Description.v101" = "Elige que incluir en tu página de inicio de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Saltar hacia atrás"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Página de inicio"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Escoge lo que ves cuando regresas a Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Escoge lo que ves cuando regresas a Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Última pestaña"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fondo de pantalla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Firefox, patrón amatista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón amatista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Firefox, patrón de colinas de playa."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, patrón de colinas de playa."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Firefox, patrón cerúleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón cerúleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fondo de pantalla claro predeterminado."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Firefox, patrón de amanecer."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Ecosia, patrón de amanecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Botón de cambio cíclico del fondo de pantalla de la página de inicio"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Firefox, patrón de colinas crepusculares."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, patrón de colinas crepusculares."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clásico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edición limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia el fondo de pantalla tocando el logotipo de la página de inicio de Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia el fondo de pantalla tocando el logotipo de la página de inicio de Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fondo de pantalla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizado"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Inicio de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Inicio de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Página de inicio"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imágenes"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Cuando esté abriendo Firefox"; +"Settings.OfferClipboardBar.Status" = "Cuando esté abriendo Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Al abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Cuando esté abriendo Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Cuando esté abriendo Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ofrecer para abrir enlaces copiados"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Aprender más."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla lucha por recolectar solo lo necesario para brindar y mejorar Firefox para todos."; +"Settings.SendUsage.Message" = "Ecosia lucha por recolectar solo lo necesario para brindar y mejorar Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar datos de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir nueva pestaña"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Usar atajos de Siri para abrir rápidamente Firefox vía Siri"; +"Settings.Siri.SectionDescription" = "Usar atajos de Siri para abrir rápidamente Ecosia vía Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atajos de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Saber más"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox puede instalar y ejecutar estudios de vez en cuando."; +"Settings.Studies.Toggle.Message" = "Ecosia puede instalar y ejecutar estudios de vez en cuando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudios"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Pegar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Buscar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Buscar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "no leído"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ayer"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "No hay otras pestañas abiertas de Firefox en tus otros dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "No hay otras pestañas abiertas de Ecosia en tus otros dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Tus derechos"; diff --git a/firefox-ios/Shared/es-MX.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/es-MX.lproj/PrivateBrowsing.strings index c75828ecdf3b3..c37c26c50733d 100644 --- a/firefox-ios/Shared/es-MX.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/es-MX.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir en una nueva pestaña privada"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox no recordará nada de tu historial o cookies, pero los nuevos marcadores se guardarán."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia no recordará nada de tu historial o cookies, pero los nuevos marcadores se guardarán."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saber más"; diff --git a/firefox-ios/Shared/es-MX.lproj/Today.strings b/firefox-ios/Shared/es-MX.lproj/Today.strings index 479eca6bfa91f..7cad1bb73bd52 100644 --- a/firefox-ios/Shared/es-MX.lproj/Today.strings +++ b/firefox-ios/Shared/es-MX.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Cerrar pestañas privadas"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Agrega atajos de Firefox a tu pantalla de inicio."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Agrega atajos de Ecosia a tu pantalla de inicio."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir al enlace copiado"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No hay pestañas abiertas."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Búsqueda privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Agrega un atajo de Firefox a tu pantalla de inicio. Después de agregar el widget, tócalo y mantenlo presionado para editarlo y seleccionar un acceso directo diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Agrega un atajo de Ecosia a tu pantalla de inicio. Después de agregar el widget, tócalo y mantenlo presionado para editarlo y seleccionar un acceso directo diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Acciones rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Atajos de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Atajos de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Agrega atajos a tus pestañas abiertas."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Buscar en Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Buscar en Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Buscar en pestaña privada"; diff --git a/firefox-ios/Shared/es.lproj/Default Browser.strings b/firefox-ios/Shared/es.lproj/Default Browser.strings index eeecddc7aa2e8..2d794430389e9 100644 --- a/firefox-ios/Shared/es.lproj/Default Browser.strings +++ b/firefox-ios/Shared/es.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Firefox tu navegador predeterminado es votar por una Internet abierta y accesible."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Hacer de Ecosia tu navegador predeterminado es votar por una Internet abierta y accesible."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Un mejor Internet de forma predeterminada"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Aprender cómo"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Firefox."; +"DefaultBrowserCard.Description" = "Configura enlaces de sitios web, correos electrónicos y mensajes para que se abran automáticamente en Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Firefox como tu navegador predeterminado para que la velocidad, seguridad y privacidad sean automáticas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Ecosia como tu navegador predeterminado para que la velocidad, seguridad y privacidad sean automáticas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Mejora la navegación cotidiana"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloquea de promedio más de 3.000 rastreadores por usuario al mes. Haznos tu navegador predeterminado para tu privacidad y tranquilidad."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloquea de promedio más de 3.000 rastreadores por usuario al mes. Haznos tu navegador predeterminado para tu privacidad y tranquilidad."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox tiene la privacidad cubierta"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia tiene la privacidad cubierta"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambia tu navegador predeterminado"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tocar aplicación de navegador predeterminada"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleccionar Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Seleccionar Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplicación de navegador predeterminado"; diff --git a/firefox-ios/Shared/es.lproj/Intro.strings b/firefox-ios/Shared/es.lproj/Intro.strings index d06c7fda1bfdc..a5808412db14b 100644 --- a/firefox-ios/Shared/es.lproj/Intro.strings +++ b/firefox-ios/Shared/es.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Iniciar sesión"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Te damos la bienvenida a Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Te damos la bienvenida a Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Empieza a navegar"; diff --git a/firefox-ios/Shared/es.lproj/Localizable.strings b/firefox-ios/Shared/es.lproj/Localizable.strings index 598fbf90c7948..6d03c243efdf5 100644 --- a/firefox-ios/Shared/es.lproj/Localizable.strings +++ b/firefox-ios/Shared/es.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Puede ser una mala configuración o una falsificación de un atacante. Sigue adelante si aceptas el riesgo potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "El dueño de %@ ha configurado su sitio web incorrectamente. Para evitar que roben tu información, Firefox no se ha conectado a este sitio web."; +"ErrorPages.CertWarning.Description" = "El dueño de %@ ha configurado su sitio web incorrectamente. Para evitar que roben tu información, Ecosia no se ha conectado a este sitio web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Tu conexión no está verificada"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Página de inicio de Firefox"; +"Firefox.HomePage.Title" = "Página de inicio de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar la página de inicio"; @@ -377,7 +377,7 @@ "Forward" = "Adelante"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cuenta de Firefox"; +"FxA.FirefoxAccount" = "Cuenta de Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administrar cuenta y dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Inicia sesión con tu cámara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Abre Firefox en tu computador y ve a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Abre Ecosia en tu computador y ve a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Listo para escanear"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar ahora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync se ha conectado a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync se ha conectado a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ se ha desconectado correctamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo se ha desconectado correctamente de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo se ha desconectado correctamente de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Se ha desconectado un dispositivo de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Se ha desconectado un dispositivo de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Firefox, cambiar el fondo de pantalla."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Ecosia, cambiar el fondo de pantalla."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ayuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Inicia sesión para ver la lista de pestañas de tus otros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Aprender más"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para utilizar la función de autocompletar en Firefox, debes tener habilitada una contraseña para el dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Para utilizar la función de autocompletar en Ecosia, debes tener habilitada una contraseña para el dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Lleva tus contraseñas siempre contigo"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autocompletar contraseñas en Firefox"; +"Logins.WelcomeView.Title2" = "Autocompletar contraseñas en Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activar autocompletado"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "INICIOS DE SESIÓN GUARDADOS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Firefox se cerró inesperadamente. ¿Te gustaría restaurar tus pestañas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que Ecosia se cerró inesperadamente. ¿Te gustaría restaurar tus pestañas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como leído"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Empieza a navegar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "¡Vaya! Firefox se bloqueó"; +"Oops! Firefox crashed" = "¡Vaya! Ecosia se bloqueó"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abre artículos en la vista de lectura pulsando el icono de libro cuando aparezca en la barra de títulos."; @@ -905,7 +905,7 @@ "Open Tabs" = "Abrir pestañas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox no pudo encontrar la página porque la dirección no es válida."; +"OpenURL.Error.Message" = "Ecosia no pudo encontrar la página porque la dirección no es válida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "No se pudo abrir la página"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Esto te permite guardar la imagen en tu Galería."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Firefox le gustaría acceder a tus fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Ecosia le gustaría acceder a tus fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Cerrar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "La información no es válida"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Vete a ‘Ajustes’ > ‘Firefox’. Permitir que Firefox acceda a la cámara."; +"ScanQRCode.PermissionError.Message.v100" = "Vete a ‘Ajustes’ > ‘Ecosia’. Permitir que Ecosia acceda a la cámara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Escanear código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Cambiar a pestaña"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugerencia de Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugerencia de Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "El nuevo buscador aparecerá en la barra de búsqueda rápida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Aceptar"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un informe del fallo para que Mozilla resuelva el problema?"; +"Send a crash report so Mozilla can fix the problem?" = "¿Enviar un informe del fallo para que Ecosia resuelva el problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar comentarios"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar pestaña"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "No tienes otros dispositivos conectados a esta cuenta de Firefox que estén disponibles para sincronizarse."; +"SendTo.NoDevicesFound.Message" = "No tienes otros dispositivos conectados a esta cuenta de Ecosia que estén disponibles para sincronizarse."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Abre Firefox, ve a Ajustes e inicia sesión para continuar."; +"SendTo.NotSignedIn.Message" = "Abre Ecosia, ve a Ajustes e inicia sesión para continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "No has iniciado sesión en tu cuenta de Firefox."; +"SendTo.NotSignedIn.Title" = "No has iniciado sesión en tu cuenta de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Selecciona para empezar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Administración de datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox dejará de sincronizarse con tu cuenta, pero no eliminará la información de navegación en este dispositivo."; +"Settings.Disconnect.Body" = "Ecosia dejará de sincronizarse con tu cuenta, pero no eliminará la información de navegación en este dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectar Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Ajustes de sincronización"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cuenta de Firefox"; +"Settings.FxA.Title" = "Cuenta de Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Elige lo que se mostrará como página de inicio."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Elige lo que se incluirá en la página de inicio de Firefox."; +"Settings.Home.Option.Description.v101" = "Elige lo que se incluirá en la página de inicio de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Saltar hacia atrás"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Página de inicio"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Elige que ver cuando regreses a Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Elige que ver cuando regreses a Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Última pestaña"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fondo de pantalla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Firefox, motivo de amatista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Ecosia, motivo de amatista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Firefox, motivo de colinas de playa."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, motivo de colinas de playa."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Firefox, motivo cerúleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Ecosia, motivo cerúleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fondo de pantalla claro predeterminado."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Firefox, motivo de amanecer."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Ecosia, motivo de amanecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Botón de cambio cíclico del fondo de pantalla de la página de inicio"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Firefox, motivo de colinas al atardecer."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, motivo de colinas al atardecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clásico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edición limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia el fondo de pantalla tocando el logotipo de la página de inicio de Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia el fondo de pantalla tocando el logotipo de la página de inicio de Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fondo de pantalla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizado"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Inicio de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Inicio de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Página de inicio"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imágenes"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.Status" = "Al abrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Al abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Al abrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Al abrir Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Proponer abrir enlaces copiados"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Descubrir más."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se esfuerza para recopilar solo lo que necesita para poder ejecutar y mejorar Firefox para todos."; +"Settings.SendUsage.Message" = "Ecosia se esfuerza para recopilar solo lo que necesita para poder ejecutar y mejorar Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar datos de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir pestaña nueva"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilizar los atajos de Siri para abrir Firefox rápidamente vía Siri"; +"Settings.Siri.SectionDescription" = "Utilizar los atajos de Siri para abrir Ecosia rápidamente vía Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atajos de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Aprender más."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox podría instalar y ejecutar estudios de vez en cuando."; +"Settings.Studies.Toggle.Message" = "Ecosia podría instalar y ejecutar estudios de vez en cuando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudios"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Buscar en Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Pegar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Buscar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Buscar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "no leído"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ayer"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "No tienes ninguna pestaña abierta en Firefox en tus demás dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "No tienes ninguna pestaña abierta en Ecosia en tus demás dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Tus derechos"; diff --git a/firefox-ios/Shared/es.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/es.lproj/PrivateBrowsing.strings index 0c63ae26423a8..a19618edf547e 100644 --- a/firefox-ios/Shared/es.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/es.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir en una nueva pestaña privada"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox no recordará tu historial ni tus cookies, pero guardará los marcadores nuevos."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia no recordará tu historial ni tus cookies, pero guardará los marcadores nuevos."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Más información"; diff --git a/firefox-ios/Shared/es.lproj/Today.strings b/firefox-ios/Shared/es.lproj/Today.strings index d4f4bd13777bf..4f0d341f7b932 100644 --- a/firefox-ios/Shared/es.lproj/Today.strings +++ b/firefox-ios/Shared/es.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Cerrar pestañas privadas"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Añade atajos de Firefox a tu pantalla de inicio."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Añade atajos de Ecosia a tu pantalla de inicio."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir al enlace copiado"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "No hay pestañas abiertas."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Búsqueda privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Agrega un acceso directo de Firefox a tu pantalla de inicio. Después de agregar el widget, selecciona y mantén pulsado para editarlo y seleccionar un acceso directo diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Agrega un acceso directo de Ecosia a tu pantalla de inicio. Después de agregar el widget, selecciona y mantén pulsado para editarlo y seleccionar un acceso directo diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Acciones rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Accesos directos de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Accesos directos de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Añade atajos a tus pestañas abiertas."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Buscar en Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Buscar en Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Buscar en pestaña privada"; diff --git a/firefox-ios/Shared/et.lproj/Default Browser.strings b/firefox-ios/Shared/et.lproj/Default Browser.strings index 1955795192ed0..915b7a10e5f75 100644 --- a/firefox-ios/Shared/et.lproj/Default Browser.strings +++ b/firefox-ios/Shared/et.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "Vaata juhendit"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Määra Firefox automaatselt avama linke, e-posti ja sõnumeid."; +"DefaultBrowserCard.Description" = "Määra Ecosia automaatselt avama linke, e-posti ja sõnumeid."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Vaikebrauseri vahetamine"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Puuduta valikut Default Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Vali Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Vali Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/et.lproj/Intro.strings b/firefox-ios/Shared/et.lproj/Intro.strings index 9e927a6974e39..a25947ddf78bd 100644 --- a/firefox-ios/Shared/et.lproj/Intro.strings +++ b/firefox-ios/Shared/et.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Logi sisse"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Tere tulemast Firefoxi"; +"Intro.Slides.Welcome.Title.v2" = "Tere tulemast Ecosiai"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Alusta veebilehitsemist"; diff --git a/firefox-ios/Shared/et.lproj/Localizable.strings b/firefox-ios/Shared/et.lproj/Localizable.strings index eab63c4968398..a536a31d03872 100644 --- a/firefox-ios/Shared/et.lproj/Localizable.strings +++ b/firefox-ios/Shared/et.lproj/Localizable.strings @@ -296,7 +296,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Tegemist võib olla häälestusveaga või ründaja rikkumisega. Jätka ainult siis, kui nõustud võimaliku riskiga."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Saidi %@ omanik on oma veebilehe valesti häälestanud. Sinu andmete kaitsmiseks varastamise eest ei loonud Firefox selle veebilehega ühendust."; +"ErrorPages.CertWarning.Description" = "Saidi %@ omanik on oma veebilehe valesti häälestanud. Sinu andmete kaitsmiseks varastamise eest ei loonud Ecosia selle veebilehega ühendust."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Seda ühendust ei usaldata"; @@ -317,7 +317,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefoxi avaleht"; +"Firefox.HomePage.Title" = "Ecosiai avaleht"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Kohanda avalehte"; @@ -347,7 +347,7 @@ "Forward" = "Edasi"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefoxi konto"; +"FxA.FirefoxAccount" = "Ecosiai konto"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Halda kontot ja seadmeid"; @@ -359,7 +359,7 @@ "fxa.signin.camera-signin" = "Logi sisse oma kaameraga"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Ava Firefox oma arvutis ja mine aadressile firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Ava Ecosia oma arvutis ja mine aadressile ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Skannimiseks valmis"; @@ -371,7 +371,7 @@ "FxA.SyncNow" = "Sünkroniseeri kohe"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync on ühendatud seadmega %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync on ühendatud seadmega %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync on ühendatud"; @@ -380,7 +380,7 @@ "FxAPush_DeviceDisconnected_body" = "Seade %@ on edukalt lahti ühendatud."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "See seade ühendati edukalt Firefox Syncist lahti."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "See seade ühendati edukalt Ecosia Syncist lahti."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync ühendati lahti"; @@ -389,10 +389,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync ühendati lahti"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Seade on Firefox Syncist lahti ühendatud"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Seade on Ecosia Syncist lahti ühendatud"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefoxi logo - taustapildi muutmise nupp."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosiai logo - taustapildi muutmise nupp."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Abi"; @@ -425,7 +425,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Teistes seadmetes avatud kaartide nimekirja nägemiseks logi sisse."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Ajalugu"; @@ -617,7 +617,7 @@ "Logins.WelcomeView.Tagline" = "Võta oma paroolid kõikjale kaasa"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Täida Firefoxi paroolid automaatselt"; +"Logins.WelcomeView.Title2" = "Täida Ecosiai paroolid automaatselt"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Lülita automaatne täitmine sisse"; @@ -662,7 +662,7 @@ "LoginsList.Title" = "SALVESTATUD KASUTAJAKONTOD"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Paistab, et Firefox jooksis varem kokku. Kas soovid oma kaardid taastada?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Paistab, et Ecosia jooksis varem kokku. Kas soovid oma kaardid taastada?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Märgi loetuks"; @@ -836,7 +836,7 @@ "Onboarding.WhatsNew.Button.Title" = "Alusta veebilehitsemist"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oih! Firefox jooksis kokku"; +"Oops! Firefox crashed" = "Oih! Ecosia jooksis kokku"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Artiklite avamiseks lugemisrežiimis puuduta raamatu ikooni, kui see ilmub aadressiribale."; @@ -848,7 +848,7 @@ "Open Tabs" = "Avatud kaardid"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ei saa seda lehte avada, sest sellel on vigane aadress."; +"OpenURL.Error.Message" = "Ecosia ei saa seda lehte avada, sest sellel on vigane aadress."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Lehe avamine pole võimalik"; @@ -860,7 +860,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "See võimaldab salvestada pildi Camera Rolli."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox soovib ligipääsu sinu piltidele"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia soovib ligipääsu sinu piltidele"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Sulge"; @@ -941,7 +941,7 @@ "ScanQRCode.InvalidDataError.Message" = "Andmed on vigased"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ava oma seadmes ‘Settings’ > ‘Firefox’. Seejärel luba kaamera ligipääs."; +"ScanQRCode.PermissionError.Message.v100" = "Ava oma seadmes ‘Settings’ > ‘Ecosia’. Seejärel luba kaamera ligipääs."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR-koodi skannimine"; @@ -989,7 +989,7 @@ "Search.ThirdPartyEngines.OK" = "Olgu"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Kas saata vearaport, et Mozilla saaks probleemi lahendada?"; +"Send a crash report so Mozilla can fix the problem?" = "Kas saata vearaport, et Ecosia saaks probleemi lahendada?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Saada tagasisidet"; @@ -1016,13 +1016,13 @@ "SendTo.NavBar.Title" = "Kaardi saatmine"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Sul ei ole selle Firefoxi kontoga teisi sünkroniseeritavaid seadmeid ühendatud."; +"SendTo.NoDevicesFound.Message" = "Sul ei ole selle Ecosiai kontoga teisi sünkroniseeritavaid seadmeid ühendatud."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Jätkamiseks ava Firefox, mine sätetesse ja logi sisse."; +"SendTo.NotSignedIn.Message" = "Jätkamiseks ava Ecosia, mine sätetesse ja logi sisse."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Sa pole oma Firefoxi kontosse sisse logitud."; +"SendTo.NotSignedIn.Title" = "Sa pole oma Ecosiai kontosse sisse logitud."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Saada"; @@ -1043,7 +1043,7 @@ "SentTab.NoTabArrivingNotification.body" = "Alustamiseks puuduta"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vaata"; @@ -1103,7 +1103,7 @@ "Settings.DataManagement.Title" = "Andmehaldus"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox lõpetab sinu kontoga sünkroniseerimise, aga ei kustuta lehitsemise andmeid sellest seadmest."; +"Settings.Disconnect.Body" = "Ecosia lõpetab sinu kontoga sünkroniseerimise, aga ei kustuta lehitsemise andmeid sellest seadmest."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Katkesta ühendus Synciga"; @@ -1166,7 +1166,7 @@ "Settings.FxA.Sync.SectionName" = "Synci sätted"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefoxi konto"; +"Settings.FxA.Title" = "Ecosiai konto"; /* General settings section title */ "Settings.General.SectionName" = "Üldine"; @@ -1175,7 +1175,7 @@ "Settings.Home.Current.Description.v101" = "Vali, mida kuvatakse avalehena."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Vali Firefoxi avalehel sisalduv."; +"Settings.Home.Option.Description.v101" = "Vali Ecosiai avalehel sisalduv."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Hiljutised kaardid"; @@ -1199,7 +1199,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Avaleht"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Vali, mida näed Firefoxi naastes."; +"Settings.Home.Option.StartAtHome.Description" = "Vali, mida näed Ecosiai naastes."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Viimane kaart"; @@ -1214,31 +1214,31 @@ "Settings.Home.Option.Wallpaper" = "Taustapilt"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefoxi taustapilt, ametüsti muster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosiai taustapilt, ametüsti muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefoxi taustapilt, rannamägede muster."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosiai taustapilt, rannamägede muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefoxi taustapilt, helesinine muster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosiai taustapilt, helesinine muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Vaikimis selge taustapilt."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefoxi taustapilt, päikesetõusu muster."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosiai taustapilt, päikesetõusu muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Avalehe taustapilti ringlemise lüliti"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefoxi taustapilt, hämariku mägede muster."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosiai taustapilt, hämariku mägede muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the section that allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.CollectionTitle" = "AVAKUVA"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Taustapilti saab muuta Firefoxi logo puudutades"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Taustapilti saab muuta Ecosiai logo puudutades"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Taustapilt"; @@ -1289,7 +1289,7 @@ "Settings.NewTab.Option.BlankPage" = "Tühi leht"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefoxi avaleht"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosiai avaleht"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Avaleht"; @@ -1310,10 +1310,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Pildid blokitakse"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefoxi avamisel"; +"Settings.OfferClipboardBar.Status" = "Ecosiai avamisel"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefoxi avamisel"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosiai avamisel"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Pakutakse kopeeritud linkide avamist"; @@ -1337,7 +1337,7 @@ "Settings.SendUsage.Link" = "Rohkem teavet."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla püüab koguda ainult seda, mida vajame, et teha Firefox kõigi jaoks paremaks."; +"Settings.SendUsage.Message" = "Ecosia püüab koguda ainult seda, mida vajame, et teha Ecosia kõigi jaoks paremaks."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Saadetakse kasutusandmeid"; @@ -1358,7 +1358,7 @@ "Settings.Siri.OpenTabShortcut" = "Ava uus kaart"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Kasuta Siri otseteid, et avada Firefox kiiresti"; +"Settings.Siri.SectionDescription" = "Kasuta Siri otseteid, et avada Ecosia kiiresti"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri otseteed"; @@ -1367,7 +1367,7 @@ "Settings.Studies.Toggle.Link" = "Rohkem teavet."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox võib aegajalt paigaldada ja käivitada uuringuid."; +"Settings.Studies.Toggle.Message" = "Ecosia võib aegajalt paigaldada ja käivitada uuringuid."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Uuringud"; @@ -1463,13 +1463,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Laadi taustal"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Laaditakse Firefoxis"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Laaditakse Ecosiais"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Ava Firefoxis"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ava Ecosiais"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Otsi Firefoxis"; +"ShareExtension.SeachInFirefoxAction.Title" = "Otsi Ecosiais"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Kuvatakse otsingusoovitusi"; @@ -1595,7 +1595,7 @@ "UIMenuItem.PasteGo" = "Aseta ja mine"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Otsi Firefoxiga"; +"UIMenuItem.SearchWithFirefox" = "Otsi Ecosiaiga"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "lugemata"; @@ -1625,7 +1625,7 @@ "Yesterday" = "Eile"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Sul pole teistes seadmetes olevates Firefoxides kaarte avatud."; +"You don’t have any tabs open in Firefox on your other devices." = "Sul pole teistes seadmetes olevates Ecosiaides kaarte avatud."; /* Your Rights settings section title */ "Your Rights" = "Sinu õigused"; diff --git a/firefox-ios/Shared/et.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/et.lproj/PrivateBrowsing.strings index 7a22214bb4da0..5f2ba90039463 100644 --- a/firefox-ios/Shared/et.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/et.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Ava uuel privaatsel kaardil"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ei mäleta ajalugu või küpsiseid, aga uued järjehoidjad salvestatakse."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ei mäleta ajalugu või küpsiseid, aga uued järjehoidjad salvestatakse."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Rohkem teavet"; diff --git a/firefox-ios/Shared/et.lproj/Today.strings b/firefox-ios/Shared/et.lproj/Today.strings index e19cc64b11d9a..a8dd9bf48c63b 100644 --- a/firefox-ios/Shared/et.lproj/Today.strings +++ b/firefox-ios/Shared/et.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Sulge\nprivaatsed kaardid"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Lisa Firefoxi otsetee oma avaekraanile."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Lisa Ecosiai otsetee oma avaekraanile."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Mine kopeeritud lingile"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Avatud kaarte pole."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Ava Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Ava Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privaatne otsing"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Lisa Firefoxi otsetee oma avaekraanile. Pärast lisamist saad seda pikalt puudutades muuta ning soovi korral valida teise otsetee."; +"TodayWidget.QuickActionGalleryDescription" = "Lisa Ecosiai otsetee oma avaekraanile. Pärast lisamist saad seda pikalt puudutades muuta ning soovi korral valida teise otsetee."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Kiired tegevused"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefoxi otseteed"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosiai otseteed"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Lisa otsetee oma avatud kaartideni."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Kiire vaade"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Otsi\nFirefoxis"; +"TodayWidget.SearchInFirefoxV2" = "Otsi\nEcosiais"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Otsi\nprivaatsel kaardil"; diff --git a/firefox-ios/Shared/eu.lproj/Default Browser.strings b/firefox-ios/Shared/eu.lproj/Default Browser.strings index e5bd1bbb89aa9..38ddd60a05608 100644 --- a/firefox-ios/Shared/eu.lproj/Default Browser.strings +++ b/firefox-ios/Shared/eu.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox zure nabigatzaile lehenetsia egitea Internet irekia eta denon eskurakoa bozkatzea da."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia zure nabigatzaile lehenetsia egitea Internet irekia eta denon eskurakoa bozkatzea da."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Lehenetsi Internet hobeago bat"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Ikasi nola"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Ireki webgune, mezu elektroniko eta Messages-eko loturak Firefoxen automatikoki."; +"DefaultBrowserCard.Description" = "Ireki webgune, mezu elektroniko eta Messages-eko loturak Ecosiaen automatikoki."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Aukeratu Firefox zure nabigatzaile lehenetsi gisa, abiadura, segurtasuna eta pribatutasuna automatikoak izan daitezen."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Aukeratu Ecosia zure nabigatzaile lehenetsi gisa, abiadura, segurtasuna eta pribatutasuna automatikoak izan daitezen."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Goratu eguneroko nabigazioa"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefoxek 3.000 jarraipen-elementu blokeatzen ditu batez bestean erabiltzaileko hilabetero. Egiguzu zure nabigatzaile lehenetsia pribatutasunaren lasaitasunerako."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosiak 3.000 jarraipen-elementu blokeatzen ditu batez bestean erabiltzaileko hilabetero. Egiguzu zure nabigatzaile lehenetsia pribatutasunaren lasaitasunerako."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefoxek pribatutasuna kontrolpean du"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosiak pribatutasuna kontrolpean du"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Aldatu zure nabigatzaile lehenetsia"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Sakatu nabigatzaile lehenetsiaren aplikazioa"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Hautatu Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Hautatu Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Nabigatzaile lehenetsiaren aplikazioa"; diff --git a/firefox-ios/Shared/eu.lproj/Intro.strings b/firefox-ios/Shared/eu.lproj/Intro.strings index 2687813836abd..3455df570e7f3 100644 --- a/firefox-ios/Shared/eu.lproj/Intro.strings +++ b/firefox-ios/Shared/eu.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Hasi saioa"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Ongi etorri Firefoxera"; +"Intro.Slides.Welcome.Title.v2" = "Ongi etorri Ecosiaera"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Hasi nabigatzen"; diff --git a/firefox-ios/Shared/eu.lproj/Localizable.strings b/firefox-ios/Shared/eu.lproj/Localizable.strings index b4633acd8e140..b730663ec9a26 100644 --- a/firefox-ios/Shared/eu.lproj/Localizable.strings +++ b/firefox-ios/Shared/eu.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Konfigurazio okerra edo erasotzaile baten faltsutze-saiakera izan daiteke. Jarraitu balizko arriskua onartzen baduzu."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ gunearen jabeak webgunea gaizki konfiguratu du. Zure informazioa lapurtzea saihesteko, Firefox ez da webgune honetara konektatu."; +"ErrorPages.CertWarning.Description" = "%@ gunearen jabeak webgunea gaizki konfiguratu du. Zure informazioa lapurtzea saihesteko, Ecosia ez da webgune honetara konektatu."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Konexio hau ez da fidagarria"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefoxen hasiera-orria"; +"Firefox.HomePage.Title" = "Ecosiaen hasiera-orria"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Pertsonalizatu hasiera-orria"; @@ -377,7 +377,7 @@ "Forward" = "Aurrera"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox kontua"; +"FxA.FirefoxAccount" = "Ecosia kontua"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Kudeatu kontua eta gailuak"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Hasi saioa zure kamerarekin"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Ireki Firefox zure ordenagailuan eta zoaz firefox.com/pair helbidera"; +"fxa.signin.qr-link-instruction" = "Ireki Ecosia zure ordenagailuan eta zoaz ecosia.org/pair helbidera"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Eskaneatzeko prest"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sinkronizatu orain"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync %@ gailura konektatu da"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync %@ gailura konektatu da"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync konektatuta"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ gailua ondo deskonektatu da."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Gailu hau Firefox Sync-etik ondo deskonektatu da."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Gailu hau Ecosia Sync-etik ondo deskonektatu da."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync deskonektatuta"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync deskonektatuta"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Gailu bat Firefox Sync-etik deskonektatu da"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Gailu bat Ecosia Sync-etik deskonektatu da"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logoa, aldatu horma-papera."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logoa, aldatu horma-papera."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Laguntza"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Hasi saioa zure beste gailuetako fitxak ikusteko."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historia"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Argibide gehiago"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefoxen osatze automatikoa erabiltzeko, gailuaren pasakodea gaituta izan behar duzu."; +"Logins.PasscodeRequirement.Warning" = "Ecosia osatze automatikoa erabiltzeko, gailuaren pasakodea gaituta izan behar duzu."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Eraman pasahitzak alboan"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Osatu automatikoki Firefoxen pasahitzak"; +"Logins.WelcomeView.Title2" = "Osatu automatikoki Ecosiaen pasahitzak"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Aktibatu osatze automatikoa"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "GORDETAKO SAIO-HASIERAK"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Badirudi Firefoxek aurretik huts egin duela. Zure fitxak berreskuratu nahi dituzu?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Badirudi Ecosiaek aurretik huts egin duela. Zure fitxak berreskuratu nahi dituzu?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Markatu irakurritako gisa"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Hasi nabigatzen"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ene! Firefoxek huts egin du"; +"Oops! Firefox crashed" = "Ene! Ecosiaek huts egin du"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Ireki artikuluak irakurtzeko ikuspegian liburuaren ikonoa sakatuz izenburu-barran agertzen denean."; @@ -905,7 +905,7 @@ "Open Tabs" = "Irekitako fitxak"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefoxek ezin du orria ireki helbide baliogabea duelako."; +"OpenURL.Error.Message" = "Ecosiak ezin du orria ireki helbide baliogabea duelako."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Ezin da orria ireki"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Honekin kameraren karretean gorde dezakezu irudia."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefoxek zure argazkietarako sarbidea nahi du"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosiak zure argazkietarako sarbidea nahi du"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Itxi"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Datuak baliogabeak dira"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Zoaz 'Ezarpenak' > 'Firefox' menu-aukerara eta baimendu Firefoxi zure gailuaren kamerarako sarbidea."; +"ScanQRCode.PermissionError.Message.v100" = "Zoaz 'Ezarpenak' > 'Ecosia' menu-aukerara eta baimendu Ecosiai zure gailuaren kamerarako sarbidea."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Eskaneatu QR kodea"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Aldatu fitxara"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefoxen iradokizunak"; +"Search.SuggestSectionTitle.v102" = "Ecosia iradokizunak"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Bilaketa-motor berria bilaketa-barran agertuko da."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Ados"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Bidali hutsegite-txostena Mozillari arazoa konpondu ahal izateko?"; +"Send a crash report so Mozilla can fix the problem?" = "Bidali hutsegite-txostena Ecosiari arazoa konpondu ahal izateko?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Bidali iritzia"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Bidali fitxa"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Ez daukazu Firefox kontu honetara sinkronizatzeko konektatutako beste gailurik."; +"SendTo.NoDevicesFound.Message" = "Ez daukazu Ecosia kontu honetara sinkronizatzeko konektatutako beste gailurik."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Jarraitzeko, ireki Firefox, joan ezarpenetara eta hasi saioa."; +"SendTo.NotSignedIn.Message" = "Jarraitzeko, ireki Ecosia, joan ezarpenetara eta hasi saioa."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Ez duzu zure Firefox kontuan saiorik hasi."; +"SendTo.NotSignedIn.Title" = "Ez duzu zure Ecosia kontuan saiorik hasi."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Bidali"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Sakatu hasteko"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ikusi"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Datuen kudeaketa"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefoxek zure kontuarekin sinkronizatzeari utziko dio baina ez du gailu honetako zure nabigatze-daturik ezabatuko."; +"Settings.Disconnect.Body" = "Ecosiak zure kontuarekin sinkronizatzeari utziko dio baina ez du gailu honetako zure nabigatze-daturik ezabatuko."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Deskonektatu Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sinkronizazio-ezarpenak"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox kontua"; +"Settings.FxA.Title" = "Ecosia kontua"; /* General settings section title */ "Settings.General.SectionName" = "Orokorra"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Aukeratu zer bistaratzen den hasierako orri gisa."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Aukeratu zer sartzen den Firefoxen hasierako orrian."; +"Settings.Home.Option.Description.v101" = "Aukeratu zer sartzen den Ecosiaen hasierako orrian."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Itzuli zeudenera"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Hasiera-orria"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Aukeratu zer ikusi Firefoxera itzultzen zarenean."; +"Settings.Home.Option.StartAtHome.Description" = "Aukeratu zer ikusi Ecosiaera itzultzen zarenean."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Azken fitxa"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Horma-papera"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox horma-papera, amethyst patroia."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia horma-papera, amethyst patroia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox horma-papera, hondartzako muinoen patroia."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia horma-papera, hondartzako muinoen patroia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox horma-papera, cerulean patroia."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia horma-papera, cerulean patroia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Horma-paper garbi lehenetsia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox horma-papera, sunrise patroia."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia horma-papera, sunrise patroia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Hasiera-orriko horma-papera txandakatzekoa"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox horma-papera, ilunabarreko muinoen patroia."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia horma-papera, ilunabarreko muinoen patroia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ klasikoa"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edizio mugatua"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Aldatu horma-papera Firefoxen hasiera-orriko logoa sakatuz"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Aldatu horma-papera Ecosiaen hasiera-orriko logoa sakatuz"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Horma-papera"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Pertsonalizatua"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefoxen hasiera"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia hasiera"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Hasiera-orria"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokeatu irudiak"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox irekitzean"; +"Settings.OfferClipboardBar.Status" = "Ecosia irekitzean"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@ irekitzean"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox irekitzean"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia irekitzean"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Eskaini kopiatutako loturak irekitzea"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Argibide gehiago."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Firefox denontzat eskaini eta hobetzeko beharrezkoa dena soilik biltzen ahalegintzen da Mozilla."; +"Settings.SendUsage.Message" = "Ecosia denontzat eskaini eta hobetzeko beharrezkoa dena soilik biltzen ahalegintzen da Ecosia."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Bidali erabilera-datuak"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Ireki fitxa berria"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Erabili Siri lasterbideak Firefox Siri bidez irekitzeko"; +"Settings.Siri.SectionDescription" = "Erabili Siri lasterbideak Ecosia Siri bidez irekitzeko"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri lasterbideak"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Argibide gehiago."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Noizean behin esperimentuak instala eta exekuta litzake Firefoxek."; +"Settings.Studies.Toggle.Message" = "Noizean behin esperimentuak instala eta exekuta litzake Ecosiaek."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Esperimentuak"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Kargatu atzeko planoan"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefoxen kargatzen"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia kargatzen"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Ireki Firefoxen"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ireki Ecosiaen"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Bilatu Firefoxen"; +"ShareExtension.SeachInFirefoxAction.Title" = "Bilatu Ecosiaen"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiatu"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Itsatsi eta joan"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Bilatu Firefox erabiliz"; +"UIMenuItem.SearchWithFirefox" = "Bilatu Ecosia erabiliz"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "irakurri gabe"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Atzo"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Ez daukazu fitxarik irekita beste gailuetako Firefoxetan."; +"You don’t have any tabs open in Firefox on your other devices." = "Ez daukazu fitxarik irekita beste gailuetako Ecosiaetan."; /* Your Rights settings section title */ "Your Rights" = "Zure eskubideak"; diff --git a/firefox-ios/Shared/eu.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/eu.lproj/PrivateBrowsing.strings index 00823eb3282cd..68b95ff8c68d8 100644 --- a/firefox-ios/Shared/eu.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/eu.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Ireki fitxa pribatu berrian"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefoxek ez du historia edo cookierik gogoratuko baina laster-markak gorde egingo dira."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosiak ez du historia edo cookierik gogoratuko baina laster-markak gorde egingo dira."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Argibide gehiago"; diff --git a/firefox-ios/Shared/eu.lproj/Today.strings b/firefox-ios/Shared/eu.lproj/Today.strings index 7dbc787969fa9..955d4d8447a07 100644 --- a/firefox-ios/Shared/eu.lproj/Today.strings +++ b/firefox-ios/Shared/eu.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Itxi fitxa pribatuak"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Gehitu Firefoxen lasterbideak zure hasierako pantailan."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Gehitu Ecosiaen lasterbideak zure hasierako pantailan."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Joan kopiatutako loturara"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Irekitako fitxarik ez."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Ireki Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Ireki Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Bilaketa pribatua"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Gehitu Firefoxerako lasterbidea zure hasierako pantailan. Widget-a gehitu bezain laster, sakatu eta mantendu lasterbidea editatu eta beste bat hautatzeko."; +"TodayWidget.QuickActionGalleryDescription" = "Gehitu Ecosiaerako lasterbidea zure hasierako pantailan. Widget-a gehitu bezain laster, sakatu eta mantendu lasterbidea editatu eta beste bat hautatzeko."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Ekintza bizkorrak"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefoxen lasterbideak"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia lasterbideak"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Gehitu irekitako zure fitxetarako lasterbideak."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Ikuspegi azkarra"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Bilatu Firefoxen"; +"TodayWidget.SearchInFirefoxV2" = "Bilatu Ecosiaen"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Bilatu fitxa pribatuan"; diff --git a/firefox-ios/Shared/fa.lproj/Default Browser.strings b/firefox-ios/Shared/fa.lproj/Default Browser.strings index 2d3b8f180bea6..badece1f3ba4e 100644 --- a/firefox-ios/Shared/fa.lproj/Default Browser.strings +++ b/firefox-ios/Shared/fa.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "چگونگی کار را یاد بگیرید"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "پیوندهای وب سایت ها ، ایمیل ها و پیام ها را تنظیم کنید تا به طور خودکار در Firefox باز شوند."; +"DefaultBrowserCard.Description" = "پیوندهای وب سایت ها ، ایمیل ها و پیام ها را تنظیم کنید تا به طور خودکار در Ecosia باز شوند."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "مرورگر یش‌فرض خود تغییر دهید"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "۲. تبدیل به مرورگر پیش‌فرض"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox را انتخاب کنید"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia را انتخاب کنید"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "مرورگر پیش‌فرض"; diff --git a/firefox-ios/Shared/fa.lproj/Localizable.strings b/firefox-ios/Shared/fa.lproj/Localizable.strings index 00959e2127f38..42fd914b08c64 100644 --- a/firefox-ios/Shared/fa.lproj/Localizable.strings +++ b/firefox-ios/Shared/fa.lproj/Localizable.strings @@ -320,7 +320,7 @@ "fxa.signin.camera-signin" = "با دوربین خود وارد شوید"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "بر روی کامپیوتر خود فایرفاکس را باز کرده و به firefox.com/pair بروید"; +"fxa.signin.qr-link-instruction" = "بر روی کامپیوتر خود فایرفاکس را باز کرده و به ecosia.org/pair بروید"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "آماده برای اسکن"; diff --git a/firefox-ios/Shared/fa.lproj/Today.strings b/firefox-ios/Shared/fa.lproj/Today.strings index e4ff4246f71b1..7fca62ac7d2e2 100644 --- a/firefox-ios/Shared/fa.lproj/Today.strings +++ b/firefox-ios/Shared/fa.lproj/Today.strings @@ -29,7 +29,7 @@ "TodayWidget.PrivateTabButtonLabelV1" = "جست‌و‌جوی خصوصی"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "یک میانبر Firefox به صفحه اصلی خود اضافه کنید. پس از افزودن ابزارک ، آن را لمس کنید و نگه دارید تا ویرایش شود و یک میانبر دیگر انتخاب کنید."; +"TodayWidget.QuickActionGalleryDescription" = "یک میانبر Ecosia به صفحه اصلی خود اضافه کنید. پس از افزودن ابزارک ، آن را لمس کنید و نگه دارید تا ویرایش شود و یک میانبر دیگر انتخاب کنید."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "اقدامات سریع"; diff --git a/firefox-ios/Shared/fi.lproj/Default Browser.strings b/firefox-ios/Shared/fi.lproj/Default Browser.strings index bba7e4c4d1e75..30d8d4f755e88 100644 --- a/firefox-ios/Shared/fi.lproj/Default Browser.strings +++ b/firefox-ios/Shared/fi.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefoxin asettaminen oletusselaimeksi on äänestys avoimen ja saavutettavan internetin puolesta."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosiain asettaminen oletusselaimeksi on äänestys avoimen ja saavutettavan internetin puolesta."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Oletuksena parempaan internetiin"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Lue lisää"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Aseta verkkosivustojen, sähköpostien ja viestien linkit avautumaan automaattisesti Firefoxissa."; +"DefaultBrowserCard.Description" = "Aseta verkkosivustojen, sähköpostien ja viestien linkit avautumaan automaattisesti Ecosiaissa."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Valitse Firefox oletusselaimeksi, jotta saat automaatisesti nopeutta, turvallisuutta ja yksityisyyttä."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Valitse Ecosia oletusselaimeksi, jotta saat automaatisesti nopeutta, turvallisuutta ja yksityisyyttä."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Paranna päivittäistä selailua"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox estää keskimäärin yli 3000 seurainta käyttäjää kohden kuukaudessa. Aseta Firefox oletusselaimeksi yksityisyyden turvaamiseksi."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia estää keskimäärin yli 3000 seurainta käyttäjää kohden kuukaudessa. Aseta Ecosia oletusselaimeksi yksityisyyden turvaamiseksi."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox kattaa yksityisyyden"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia kattaa yksityisyyden"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Vaihda oletusselain"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Napauta Oletusselainappi"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Valitse Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Valitse Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Oletusselain"; diff --git a/firefox-ios/Shared/fi.lproj/Intro.strings b/firefox-ios/Shared/fi.lproj/Intro.strings index fe069f6eb0244..c7c6c7d5b8ec6 100644 --- a/firefox-ios/Shared/fi.lproj/Intro.strings +++ b/firefox-ios/Shared/fi.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Kirjaudu sisään"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Tervetuloa Firefoxiin"; +"Intro.Slides.Welcome.Title.v2" = "Tervetuloa Ecosiaiin"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Aloita selaaminen"; diff --git a/firefox-ios/Shared/fi.lproj/Localizable.strings b/firefox-ios/Shared/fi.lproj/Localizable.strings index 863e68e17837d..3a3d8af84630d 100644 --- a/firefox-ios/Shared/fi.lproj/Localizable.strings +++ b/firefox-ios/Shared/fi.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Kyseessä saattaa olla ongelma palvelimen asetuksissa tai hyökkääjän suorittama toimenpide. Jatka, jos hyväksyt mahdollisen riskin."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Sivuston %@ omistaja on määrittänyt sivustonsa asetukset väärin. Firefox ei muodostanut yhteyttä sivustoon suojellakseen tietojasi varkaudelta."; +"ErrorPages.CertWarning.Description" = "Sivuston %@ omistaja on määrittänyt sivustonsa asetukset väärin. Ecosia ei muodostanut yhteyttä sivustoon suojellakseen tietojasi varkaudelta."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Tämä yhteys ei ole luotettu"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefoxin aloitussivu"; +"Firefox.HomePage.Title" = "Ecosiain aloitussivu"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Mukauta kotisivua"; @@ -377,7 +377,7 @@ "Forward" = "Seuraava"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-tili"; +"FxA.FirefoxAccount" = "Ecosia-tili"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Hallitse tiliä ja laitteita"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Kirjaudu sisään kamerallasi"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Avaa tietokoneellasi Firefox ja siirry osoitteeseen firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Avaa tietokoneellasi Ecosia ja siirry osoitteeseen ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Valmiina skannaamaan"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synkronoi"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync on yhdistänyt laitteeseen %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync on yhdistänyt laitteeseen %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync yhdistetty"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ on irrotettu onnistuneesti."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Tämä laite on irrotettu onnistuneesti Firefox Sync -palvelusta."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Tämä laite on irrotettu onnistuneesti Ecosia Sync -palvelusta."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Synkronointi katkaistu"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Synkronointi poistettu käytöstä"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Laite on irrotettu Firefox Sync -palvelusta"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Laite on irrotettu Ecosia Sync -palvelusta"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-logo, vaihda taustakuva."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-logo, vaihda taustakuva."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ohje"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Kirjaudu sisään nähdäksesi listan muiden laitteidesi välilehdistä."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historia"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Lue lisää"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Automaattinen täyttö -ominaisuuden käyttäminen Firefoxissa edellyttää, että laitteessa on pääsykoodi käytössä."; +"Logins.PasscodeRequirement.Warning" = "Automaattinen täyttö -ominaisuuden käyttäminen Ecosiaissa edellyttää, että laitteessa on pääsykoodi käytössä."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ota salasanasi mukaan kaikkialle"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Täytä Firefox-salasanat automaattisesti"; +"Logins.WelcomeView.Title2" = "Täytä Ecosia-salasanat automaattisesti"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Ota automaattinen täyttö käyttöön"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "TALLENNETUT KIRJAUTUMISTIEDOT"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Näyttää siltä, että Firefox kaatui aiemmin. Haluatko palauttaa välilehdet?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Näyttää siltä, että Ecosia kaatui aiemmin. Haluatko palauttaa välilehdet?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Merkitse luetuksi"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Aloita selaaminen"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hups! Firefox kaatui"; +"Oops! Firefox crashed" = "Hups! Ecosia kaatui"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Avaa artikkelit lukunäkymässä napauttamalla kirjakuvaketta, kun se ilmestyy hakuriville."; @@ -905,7 +905,7 @@ "Open Tabs" = "Avoimet välilehdet"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ei voi avata sivustoa, sillä sen osoite on virheellinen."; +"OpenURL.Error.Message" = "Ecosia ei voi avata sivustoa, sillä sen osoite on virheellinen."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Sivua ei voi avata"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Tämän antaa tallentaa kuvan Kameran rullaan."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox haluaisi käyttää valokuviasi"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia haluaisi käyttää valokuviasi"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Sulje"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Tiedot ovat virheellisiä"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Siirry \"Asetukset\" > \"Firefox\". Salli Firefoxin käyttää kameraa."; +"ScanQRCode.PermissionError.Message.v100" = "Siirry \"Asetukset\" > \"Ecosia\". Salli Ecosiain käyttää kameraa."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skannaa QR-koodi"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Vaihda välilehteen"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-ehdotukset"; +"Search.SuggestSectionTitle.v102" = "Ecosia-ehdotukset"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Uusi hakukone ilmestyy pkahakupalkkiin."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Lähetätkö virheraportin, jotta Mozilla voi korjata ongelman?"; +"Send a crash report so Mozilla can fix the problem?" = "Lähetätkö virheraportin, jotta Ecosia voi korjata ongelman?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Lähetä palautetta"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Lähetä välilehti"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Sinulla ei ole muita tähän Firefox-tiliin kytkettyjä laitteita synkronointia varten."; +"SendTo.NoDevicesFound.Message" = "Sinulla ei ole muita tähän Ecosia-tiliin kytkettyjä laitteita synkronointia varten."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Avaa Firefox, siirry asetuksiin ja kirjaudu sisään jatkaaksesi."; +"SendTo.NotSignedIn.Message" = "Avaa Ecosia, siirry asetuksiin ja kirjaudu sisään jatkaaksesi."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Et ole kirjautuneena Firefox-tilillesi."; +"SendTo.NotSignedIn.Title" = "Et ole kirjautuneena Ecosia-tilillesi."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Lähetä"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Napauta aloittaaksesi"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Näytä"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Tietojen hallinta"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox lopettaa synkronoinnin tilisi kanssa, mutta ei poista mitään selaustietojasi tällä laitteella."; +"Settings.Disconnect.Body" = "Ecosia lopettaa synkronoinnin tilisi kanssa, mutta ei poista mitään selaustietojasi tällä laitteella."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Ota synkronointi pois käytöstä"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync-asetukset"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-tili"; +"Settings.FxA.Title" = "Ecosia-tili"; /* General settings section title */ "Settings.General.SectionName" = "Yleiset"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Valitse mitä näytetään kotisivuna."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Valitse mitä Firefoxin kotisivulla on."; +"Settings.Home.Option.Description.v101" = "Valitse mitä Ecosiain kotisivulla on."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Palaa takaisin"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Kotisivu"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Valitse, mitä näet, kun palaat Firefoxiin."; +"Settings.Home.Option.StartAtHome.Description" = "Valitse, mitä näet, kun palaat Ecosiaiin."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Viimeisin välilehti"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Taustakuva"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-taustakuva, ametistikuvio."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-taustakuva, ametistikuvio."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-taustakuva, rantakukkulakuvio."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-taustakuva, rantakukkulakuvio."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-taustakuva, seruleenikuvio."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-taustakuva, seruleenikuvio."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Oletusarvoinen tyhjä taustakuva."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox-taustakuva, auringonnousukuvio."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia-taustakuva, auringonnousukuvio."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Kotisivun taustakuvan vaihtokytkin"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-taustakuva, iltahämärän kukkulakuviot."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-taustakuva, iltahämärän kukkulakuviot."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassinen %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Rajoitettu erä"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Vaihda taustakuvaa napauttamalla Firefoxin kotisivun logoa"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Vaihda taustakuvaa napauttamalla Ecosiain kotisivun logoa"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Taustakuva"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Mukautettu"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefoxin aloitussivu"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosiain aloitussivu"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Kotisivu"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Estä kuvat"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Kun avaat Firefoxin"; +"Settings.OfferClipboardBar.Status" = "Kun avaat Ecosiain"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Avattaessa %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Kun avaat Firefoxin"; +"Settings.OfferClipboardBar.StatusV2" = "Kun avaat Ecosiain"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Tarjoa kopioitujen linkkien avaamista"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Lue lisää."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla pyrkii keräämään vain tietoja, jotka ovat tarpeellisia ja auttavat Firefoxin kehittämisessä."; +"Settings.SendUsage.Message" = "Ecosia pyrkii keräämään vain tietoja, jotka ovat tarpeellisia ja auttavat Ecosiain kehittämisessä."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Lähetä käyttötilastoja"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Avaa uusi välilehti"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Käytä Siri-oikoteitä avataksesi Firefoxin nopeasti Sirin kautta"; +"Settings.Siri.SectionDescription" = "Käytä Siri-oikoteitä avataksesi Ecosiain nopeasti Sirin kautta"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-oikotiet"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Lue lisää."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox saattaa asentaa ja suorittaa tutkimuksia silloin tällöin."; +"Settings.Studies.Toggle.Message" = "Ecosia saattaa asentaa ja suorittaa tutkimuksia silloin tällöin."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Tutkimukset"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Lataa taustalla"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Ladataan Firefoxissa"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ladataan Ecosiaissa"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Avaa Firefoxissa"; +"ShareExtension.OpenInFirefoxAction.Title" = "Avaa Ecosiaissa"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Hae Firefoxissa"; +"ShareExtension.SeachInFirefoxAction.Title" = "Hae Ecosiaissa"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopioi"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Liitä ja avaa"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Etsi Firefoxilla"; +"UIMenuItem.SearchWithFirefox" = "Etsi Ecosiailla"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "lukematon"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Eilen"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Sinulla ei ole Firefoxissa avoimia välilehtiä muilla laitteilla."; +"You don’t have any tabs open in Firefox on your other devices." = "Sinulla ei ole Ecosiaissa avoimia välilehtiä muilla laitteilla."; /* Your Rights settings section title */ "Your Rights" = "Oikeutesi"; diff --git a/firefox-ios/Shared/fi.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/fi.lproj/PrivateBrowsing.strings index ec1ad8acd738d..9d7f1987756e7 100644 --- a/firefox-ios/Shared/fi.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/fi.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Avaa yksityisessä välilehdessä"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ei tallenna historiaa tai evästeitä, mutta uudet kirjanmerkit tallennetaan."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ei tallenna historiaa tai evästeitä, mutta uudet kirjanmerkit tallennetaan."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Lue lisää"; diff --git a/firefox-ios/Shared/fi.lproj/Today.strings b/firefox-ios/Shared/fi.lproj/Today.strings index 7344680984ac0..2fef04e644617 100644 --- a/firefox-ios/Shared/fi.lproj/Today.strings +++ b/firefox-ios/Shared/fi.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Sulje yksityiset välilehdet"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Lisää Firefoxin oikotiet kotinäyttöön."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Lisää Ecosiain oikotiet kotinäyttöön."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Siirry kopioituun linkkiin"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ei avoimia välilehtiä."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Avaa Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Avaa Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Yksityinen haku"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Lisää Firefox-oikotie kotinäyttöön. Kun olet lisännyt widgetin, kosketa pitkään muokataksesi sitä ja valitse toinen oikotie."; +"TodayWidget.QuickActionGalleryDescription" = "Lisää Ecosia-oikotie kotinäyttöön. Kun olet lisännyt widgetin, kosketa pitkään muokataksesi sitä ja valitse toinen oikotie."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Pikatoiminnot"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-oikotiet"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-oikotiet"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Lisää oikoteitä avoimiin välilehtiisi."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Pikanäkymä"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Hae\nFirefoxissa"; +"TodayWidget.SearchInFirefoxV2" = "Hae\nEcosiaissa"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Hae\nyksityisessä välilehdessä"; diff --git a/firefox-ios/Shared/fil.lproj/Default Browser.strings b/firefox-ios/Shared/fil.lproj/Default Browser.strings index 899e81507d913..2b57f8dc610df 100644 --- a/firefox-ios/Shared/fil.lproj/Default Browser.strings +++ b/firefox-ios/Shared/fil.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "Alamin kung paano"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Firefox."; +"DefaultBrowserCard.Description" = "Set links from websites, emails, and Messages to open automatically in Ecosia."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Palitan ang iyong pangunahin na Browser"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. i-Tap ang Default na Browser App"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Piliin ang Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Piliin ang Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Default Browser App"; diff --git a/firefox-ios/Shared/fil.lproj/Intro.strings b/firefox-ios/Shared/fil.lproj/Intro.strings index 26554e42ee543..6475d5bfd7dd8 100644 --- a/firefox-ios/Shared/fil.lproj/Intro.strings +++ b/firefox-ios/Shared/fil.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Mag sign-in"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Maligayang pagdating sa Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Maligayang pagdating sa Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Simulang mag-Browse"; diff --git a/firefox-ios/Shared/fil.lproj/Localizable.strings b/firefox-ios/Shared/fil.lproj/Localizable.strings index 30db63a353376..3f9f595eec9c8 100644 --- a/firefox-ios/Shared/fil.lproj/Localizable.strings +++ b/firefox-ios/Shared/fil.lproj/Localizable.strings @@ -299,7 +299,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Maaari ito ay isang maling pag-configure o pakikialam ng isang nang-aatake. Magpatuloy kung tatanggapin mo ang mga potensyal na panganib."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Ang may-ari ng %@ ay maling nai-configure ang kanilang website. Upang maprotektahan and iyong impormasyon at hindi manakaw, Hindi kumonekta ang Firefox sa website."; +"ErrorPages.CertWarning.Description" = "Ang may-ari ng %@ ay maling nai-configure ang kanilang website. Upang maprotektahan and iyong impormasyon at hindi manakaw, Hindi kumonekta ang Ecosia sa website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ang iyong koneksyon ay hindi pribado"; @@ -320,7 +320,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Home Page ng Firefox"; +"Firefox.HomePage.Title" = "Home Page ng Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Ipasadya ang Homepage"; @@ -350,7 +350,7 @@ "Forward" = "Sulong"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Pamahalaan ang Account"; @@ -371,7 +371,7 @@ "FxA.SyncNow" = "I-sync Ngayon"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Konektado ang Firefox Sync sa %@"; +"FxAPush_DeviceConnected_body" = "Konektado ang Ecosia Sync sa %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "I-sync ang Konektado"; @@ -380,7 +380,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ay matagumpay na naalis."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Matagumpay na na-disconnect ang device na ito mula sa Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Matagumpay na na-disconnect ang device na ito mula sa Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Nakakonekta ang Sync"; @@ -389,7 +389,7 @@ "FxAPush_DeviceDisconnected_title" = "Hindi nakakonekta ang Sync"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ang isang aparato ay naka-disconnect mula sa Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ang isang aparato ay naka-disconnect mula sa Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Help"; @@ -422,7 +422,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Mag-sign in upang tingnan ang isang listahan ng mga tab mula sa iyong iba pang mga device."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Kasaysayan"; @@ -617,7 +617,7 @@ "Logins.WelcomeView.Tagline" = "Dalhin kahit saan ang mga password mo"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "i-AutoFill ang mga Firefox Password mo"; +"Logins.WelcomeView.Title2" = "i-AutoFill ang mga Ecosia Password mo"; /* Button to not save the user's password */ "LoginsHelper.DontSave.Button" = "Huwag I-save"; @@ -656,7 +656,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Mukhang bumagsak ang Firefox. Gusto mo bang ibalik ang iyong mga tab?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Mukhang bumagsak ang Ecosia. Gusto mo bang ibalik ang iyong mga tab?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Markahan bilang Nabasa"; @@ -806,7 +806,7 @@ "Okay" = "Okay"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Bumagsak ang Firefox"; +"Oops! Firefox crashed" = "Oops! Bumagsak ang Ecosia"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Buksan ang basahin sa Reader View gamit ang book icon paglabas nito sa title bar."; @@ -818,7 +818,7 @@ "Open Tabs" = "Buksan ang mga Tab"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Hindi mabuksan ng Firefox ang pahina dahil ang address ay inbalido."; +"OpenURL.Error.Message" = "Hindi mabuksan ng Ecosia ang pahina dahil ang address ay inbalido."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Hindi maaaring buksan ang pahina"; @@ -830,7 +830,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Ito ay pumapayag na i-save ang imahe sa iyong Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Gusto ng Firefox na mag-access ng iyong mga Larawan"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Gusto ng Ecosia na mag-access ng iyong mga Larawan"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Isarado"; @@ -947,7 +947,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Magpadala ng crash report sa Mozilla upang ayusin ang problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Magpadala ng crash report sa Ecosia upang ayusin ang problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Ipadala ang Feedback"; @@ -974,13 +974,13 @@ "SendTo.NavBar.Title" = "Ipadala ang Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Wala kang ibang mga aparato na konektado sa Firefox Account na magagamit upang mai-sync."; +"SendTo.NoDevicesFound.Message" = "Wala kang ibang mga aparato na konektado sa Ecosia Account na magagamit upang mai-sync."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Mangyaring i-open ang Firefox, pumunta ng Settings at mag sign in para ipagpatuloy."; +"SendTo.NotSignedIn.Message" = "Mangyaring i-open ang Ecosia, pumunta ng Settings at mag sign in para ipagpatuloy."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Hindi ka naka-sign in sa iyong Firefox Account."; +"SendTo.NotSignedIn.Title" = "Hindi ka naka-sign in sa iyong Ecosia Account."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Ipadala"; @@ -1001,7 +1001,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tapikin upang magsimula"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Tingnan"; @@ -1058,7 +1058,7 @@ "Settings.DataManagement.Title" = "PAgsasaayos ng Data"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Hihinto ang Firefox sa pag-sync sa iyong account, ngunit hindi tatanggalin ang alinman sa iyong data sa pagba-browse sa device na ito."; +"Settings.Disconnect.Body" = "Hihinto ang Ecosia sa pag-sync sa iyong account, ngunit hindi tatanggalin ang alinman sa iyong data sa pagba-browse sa device na ito."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Idiskonekta ang Pag-sync"; @@ -1121,7 +1121,7 @@ "Settings.FxA.Sync.SectionName" = "Mga sync setting"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "Pangkalahatan"; @@ -1145,7 +1145,7 @@ "Settings.NewTab.Option.BlankPage" = "Magpakita ng Blank na Pahina"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Ipakita ang Iyong Homepage"; @@ -1163,10 +1163,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Piliin kung ano ang mag-load kapag binubuksan ang isang bagong tab"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Kapag Binubuksan ang Firefox"; +"Settings.OfferClipboardBar.Status" = "Kapag Binubuksan ang Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Kapag Binubuksan ang Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Kapag Binubuksan ang Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Mag-alok sa Buksan ang Mga Nakopya"; @@ -1190,7 +1190,7 @@ "Settings.SendUsage.Link" = "Matuto ng higit pa."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Nagsusumikap ang Mozilla na kolektahin lamang kung ano ang kailangan naming ibigay at pagbutihin ang Firefox para sa lahat."; +"Settings.SendUsage.Message" = "Nagsusumikap ang Ecosia na kolektahin lamang kung ano ang kailangan naming ibigay at pagbutihin ang Ecosia para sa lahat."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Ipadala ang Data ng Paggamit"; @@ -1205,7 +1205,7 @@ "Settings.Siri.OpenTabShortcut" = "Magbukas ng Bagong Tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Gumamit ng mga shortcut sa Siri upang mabilis na buksan ang Firefox sa pamamagitan ng Siri"; +"Settings.Siri.SectionDescription" = "Gumamit ng mga shortcut sa Siri upang mabilis na buksan ang Ecosia sa pamamagitan ng Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Shortcuts"; @@ -1259,13 +1259,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "I-load sa Background"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Nagloload sa Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Nagloload sa Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Buksan sa Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Buksan sa Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Hanapin sa Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Hanapin sa Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Ipakita ang Search Suggestions"; @@ -1382,7 +1382,7 @@ "UIMenuItem.PasteGo" = "I-paste at Pumunta"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Maghanap gamit ang Firefox"; +"UIMenuItem.SearchWithFirefox" = "Maghanap gamit ang Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "Hindi pa nabasa"; @@ -1412,7 +1412,7 @@ "Yesterday" = "Kahapon"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Walang ibang bukas na mga tab sa Firefox sa ibang device."; +"You don’t have any tabs open in Firefox on your other devices." = "Walang ibang bukas na mga tab sa Ecosia sa ibang device."; /* Your Rights settings section title */ "Your Rights" = "Ang iyong mga Karapatan"; diff --git a/firefox-ios/Shared/fil.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/fil.lproj/PrivateBrowsing.strings index 03415d0545ff1..30dddcd134023 100644 --- a/firefox-ios/Shared/fil.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/fil.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Buksan sa bagong Private Tab"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Hindi maaalala ng Firefox ang alin man sa iyong history o cookies, pero ang mga bagong bookmark ay tatandaan."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Hindi maaalala ng Ecosia ang alin man sa iyong history o cookies, pero ang mga bagong bookmark ay tatandaan."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Learn More"; diff --git a/firefox-ios/Shared/fr.lproj/Default Browser.strings b/firefox-ios/Shared/fr.lproj/Default Browser.strings index c04690f1f1299..17255cf5a2b8a 100644 --- a/firefox-ios/Shared/fr.lproj/Default Browser.strings +++ b/firefox-ios/Shared/fr.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Faire de Firefox votre navigateur par défaut, c’est voter pour un Internet ouvert et accessible."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Faire de Ecosia votre navigateur par défaut, c’est voter pour un Internet ouvert et accessible."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Par défaut vers un meilleur Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Me montrer comment"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Faites en sorte que les liens des sites web, des e-mails et des messages s’ouvrent automatiquement dans Firefox."; +"DefaultBrowserCard.Description" = "Faites en sorte que les liens des sites web, des e-mails et des messages s’ouvrent automatiquement dans Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Choisissez Firefox comme navigateur par défaut pour rendre la vitesse, la sécurité et la confidentialité automatiques."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Choisissez Ecosia comme navigateur par défaut pour rendre la vitesse, la sécurité et la confidentialité automatiques."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Améliorez votre navigation ordinaire"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloque en moyenne plus de 3 000 traqueurs par utilisateur et par mois. Choisissez-nous comme navigateur par défaut pour la tranquillité d’esprit quant au respect de votre vie privée."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloque en moyenne plus de 3 000 traqueurs par utilisateur et par mois. Choisissez-nous comme navigateur par défaut pour la tranquillité d’esprit quant au respect de votre vie privée."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox protège votre vie privée"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia protège votre vie privée"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Changez de navigateur par défaut"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Appuyez sur App du navigateur par défaut"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Sélectionnez Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Sélectionnez Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "App du navigateur par défaut"; diff --git a/firefox-ios/Shared/fr.lproj/Intro.strings b/firefox-ios/Shared/fr.lproj/Intro.strings index 0d6cdeccfb4ba..54e6e743871bd 100644 --- a/firefox-ios/Shared/fr.lproj/Intro.strings +++ b/firefox-ios/Shared/fr.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Se connecter"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bienvenue dans Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bienvenue dans Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Commencer la navigation"; diff --git a/firefox-ios/Shared/fr.lproj/Localizable.strings b/firefox-ios/Shared/fr.lproj/Localizable.strings index 44e9ee0b71229..d7a1ecdc60dac 100644 --- a/firefox-ios/Shared/fr.lproj/Localizable.strings +++ b/firefox-ios/Shared/fr.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Il peut s’agir d’une erreur de configuration ou d’une tentative d’attaque. Poursuivez en ayant conscience du risque encouru."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Les propriétaires de %@ ont mal configuré leur site. Pour éviter que vos données ne soient dérobées, Firefox ne s’est pas connecté à ce site."; +"ErrorPages.CertWarning.Description" = "Les propriétaires de %@ ont mal configuré leur site. Pour éviter que vos données ne soient dérobées, Ecosia ne s’est pas connecté à ce site."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Cette connexion n’est pas certifiée"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Page d’accueil de Firefox"; +"Firefox.HomePage.Title" = "Page d’accueil de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personnaliser la page d’accueil"; @@ -377,7 +377,7 @@ "Forward" = "Page suivante"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Compte Firefox"; +"FxA.FirefoxAccount" = "Compte Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gérer le compte et les appareils"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Connectez-vous avec votre appareil photo"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Sur votre ordinateur, ouvrez Firefox et accédez à firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Sur votre ordinateur, ouvrez Ecosia et accédez à ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Prêt·e à scanner"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synchroniser maintenant"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync s’est connecté à %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync s’est connecté à %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync connecté"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ a été déconnecté."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Cet appareil a été correctement déconnecté de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Cet appareil a été correctement déconnecté de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync déconnecté"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync déconnecté"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un appareil a été déconnecté de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un appareil a été déconnecté de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, changer le fond d’écran."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, changer le fond d’écran."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Aide"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Connectez-vous pour afficher la liste des onglets de vos autres appareils."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historique"; @@ -659,7 +659,7 @@ "Logins.WelcomeView.Tagline" = "Emportez vos mots de passe partout avec vous"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Remplissage automatique des mots de passe par Firefox"; +"Logins.WelcomeView.Title2" = "Remplissage automatique des mots de passe par Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activer le remplissage automatique"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "IDENTIFIANTS ENREGISTRÉS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Il semblerait que Firefox ait planté. Voulez-vous restaurer vos onglets ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Il semblerait que Ecosia ait planté. Voulez-vous restaurer vos onglets ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marquer comme lu"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Commencer la navigation"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oups, Firefox a planté"; +"Oops! Firefox crashed" = "Oups, Ecosia a planté"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Ouvrez des articles en mode lecture en appuyant sur l’icône de livre lorsqu’elle apparaît dans la barre de titre."; @@ -905,7 +905,7 @@ "Open Tabs" = "Onglets ouverts"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ne parvient pas à ouvrir cette page, car l’adresse est invalide."; +"OpenURL.Error.Message" = "Ecosia ne parvient pas à ouvrir cette page, car l’adresse est invalide."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Impossible d’ouvrir la page"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Ceci vous permet d’enregistrer l’image dans Pellicule."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox souhaite accéder à vos photos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia souhaite accéder à vos photos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Fermer"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Données non valides"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Accédez à « Réglages » > « Firefox » et autorisez Firefox à accéder à l’appareil photo."; +"ScanQRCode.PermissionError.Message.v100" = "Accédez à « Réglages » > « Ecosia » et autorisez Ecosia à accéder à l’appareil photo."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scanner un code QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Aller à l’onglet"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox suggère"; +"Search.SuggestSectionTitle.v102" = "Ecosia suggère"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Le nouveau moteur de recherche apparaîtra dans la barre de recherche rapide."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Envoyer un rapport de plantage afin que Mozilla puisse corriger le problème ?"; +"Send a crash report so Mozilla can fix the problem?" = "Envoyer un rapport de plantage afin que Ecosia puisse corriger le problème ?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Donner mon avis"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Envoyer l’onglet"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Vous n’avez aucun autre appareil prêt à être synchronisé et connecté à ce compte Firefox."; +"SendTo.NoDevicesFound.Message" = "Vous n’avez aucun autre appareil prêt à être synchronisé et connecté à ce compte Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Veuillez ouvrir Firefox, vous rendre dans les paramètres et vous connecter pour continuer."; +"SendTo.NotSignedIn.Message" = "Veuillez ouvrir Ecosia, vous rendre dans les paramètres et vous connecter pour continuer."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Vous n’êtes pas connecté·e à votre compte Firefox."; +"SendTo.NotSignedIn.Title" = "Vous n’êtes pas connecté·e à votre compte Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Envoyer"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Appuyer pour commencer"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Afficher"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gestion des données"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ne se synchronisera plus avec votre compte, mais ne supprimera aucune donnée de navigation sur cet appareil."; +"Settings.Disconnect.Body" = "Ecosia ne se synchronisera plus avec votre compte, mais ne supprimera aucune donnée de navigation sur cet appareil."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Déconnecter Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Paramètres de synchronisation"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Compte Firefox"; +"Settings.FxA.Title" = "Compte Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Général"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Choisissez ce qui s’affiche comme page d’accueil."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Choisissez le contenu de la page d’accueil de Firefox."; +"Settings.Home.Option.Description.v101" = "Choisissez le contenu de la page d’accueil de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Revenir à cet onglet"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Page d’accueil"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Choisissez ce qui s’affiche lorsque vous rouvrez Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Choisissez ce qui s’affiche lorsque vous rouvrez Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Dernier onglet"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fond d’écran"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fond d’écran de Firefox, motif améthyste."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fond d’écran de Ecosia, motif améthyste."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fond d’écran Firefox, motif collines de plage."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fond d’écran Ecosia, motif collines de plage."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fond d’écran de Firefox, motif bleu céruléen."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fond d’écran de Ecosia, motif bleu céruléen."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fond d’écran clair par défaut."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fond d’écran de Firefox, motif de lever de soleil."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fond d’écran de Ecosia, motif de lever de soleil."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Commutateur du diaporama du fond d’écran de la page d’accueil"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fond d’écran Firefox, motif collines au crépuscule."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fond d’écran Ecosia, motif collines au crépuscule."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ classique"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Édition limitée"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Changer le fond d’écran en appuyant sur le logo Firefox de la page d’accueil"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Changer le fond d’écran en appuyant sur le logo Ecosia de la page d’accueil"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fond d’écran"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personnalisé"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Page d’accueil de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Page d’accueil de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Page d’accueil"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquer les images"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "À l’ouverture de Firefox"; +"Settings.OfferClipboardBar.Status" = "À l’ouverture de Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Lors de l’ouverture de %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "À l’ouverture de Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "À l’ouverture de Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Proposer d’ouvrir les liens copiés"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "En savoir plus."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla veille à collecter uniquement les données nécessaires afin d’offrir et d’améliorer Firefox pour tout le monde."; +"Settings.SendUsage.Message" = "Ecosia veille à collecter uniquement les données nécessaires afin d’offrir et d’améliorer Ecosia pour tout le monde."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Envoyer des données d’utilisation"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Ouvrir un nouvel onglet"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilisez les raccourcis Siri pour ouvrir rapidement Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Utilisez les raccourcis Siri pour ouvrir rapidement Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Raccourcis Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "En savoir plus."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox peut installer et lancer des études de temps en temps."; +"Settings.Studies.Toggle.Message" = "Ecosia peut installer et lancer des études de temps en temps."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Études"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Charger en arrière-plan"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Chargement dans Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Chargement dans Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Ouvrir dans Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ouvrir dans Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Rechercher dans Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Rechercher dans Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copier"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Coller et ouvrir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Rechercher avec Firefox"; +"UIMenuItem.SearchWithFirefox" = "Rechercher avec Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "Non lu"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Hier"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Vous n’avez aucun onglet ouvert dans Firefox sur vos autres appareils."; +"You don’t have any tabs open in Firefox on your other devices." = "Vous n’avez aucun onglet ouvert dans Ecosia sur vos autres appareils."; /* Your Rights settings section title */ "Your Rights" = "Droits de l’utilisateur"; diff --git a/firefox-ios/Shared/fr.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/fr.lproj/PrivateBrowsing.strings index 0434def634d8a..84c6d65c5c282 100644 --- a/firefox-ios/Shared/fr.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/fr.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Ouvrir dans un nouvel onglet privé"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ne conservera aucun historique ni aucun cookie, cependant les nouveaux marque-pages seront conservés."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ne conservera aucun historique ni aucun cookie, cependant les nouveaux marque-pages seront conservés."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "En savoir plus"; diff --git a/firefox-ios/Shared/fr.lproj/Today.strings b/firefox-ios/Shared/fr.lproj/Today.strings index 8522459a4b2c8..0534ec4c10e9c 100644 --- a/firefox-ios/Shared/fr.lproj/Today.strings +++ b/firefox-ios/Shared/fr.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Fermer les onglets privés"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Ajoutez des raccourcis Firefox à votre écran d’accueil."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ajoutez des raccourcis Ecosia à votre écran d’accueil."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ouvrir le lien copié"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Aucun onglet ouvert."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Ouvrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Ouvrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Recherche privée"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Ajoutez un raccourci Firefox à votre écran d’accueil. Après avoir ajouté le widget, appuyez de manière prolongée pour le modifier et sélectionnez un autre raccourci."; +"TodayWidget.QuickActionGalleryDescription" = "Ajoutez un raccourci Ecosia à votre écran d’accueil. Après avoir ajouté le widget, appuyez de manière prolongée pour le modifier et sélectionnez un autre raccourci."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Actions rapides"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Raccourcis Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Raccourcis Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Ajoutez des raccourcis vers vos onglets ouverts."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Aperçu rapide"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Rechercher dans Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Rechercher dans Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Rechercher dans un onglet privé"; diff --git a/firefox-ios/Shared/ga.lproj/Localizable.strings b/firefox-ios/Shared/ga.lproj/Localizable.strings index 275b92558c8f1..7b67ba04657ef 100644 --- a/firefox-ios/Shared/ga.lproj/Localizable.strings +++ b/firefox-ios/Shared/ga.lproj/Localizable.strings @@ -137,7 +137,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Seans go bhfuil cumraíocht mhícheart ar an suíomh, nó go bhfuil duine éigin ag déanamh ionsaí air. Lean ar aghaidh má tá tú sásta dul sa bhearna bhaoil."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Níor chumraigh úinéir %@ an suíomh mar is ceart. Níor cheangail Firefox leis an suíomh seo, chun do chuid faisnéise a chosaint."; +"ErrorPages.CertWarning.Description" = "Níor chumraigh úinéir %@ an suíomh mar is ceart. Níor cheangail Ecosia leis an suíomh seo, chun do chuid faisnéise a chosaint."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ceangal Neamhiontaofa é seo"; @@ -152,7 +152,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Leathanach Baile Firefox"; +"Firefox.HomePage.Title" = "Leathanach Baile Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Ar Aghaidh"; @@ -170,7 +170,7 @@ "HistoryPanel.EmptySyncedTabsNullState.Description" = "Taispeánfar do chuid cluaisíní ó ghléasanna eile anseo."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Stair"; @@ -272,7 +272,7 @@ "LoginsHelper.Update.Button" = "Nuashonraigh"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Dealraíonn sé gur thuairteáil Firefox roimhe seo. An bhfuil fonn ort do chuid cluaisíní a athchóiriú?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Dealraíonn sé gur thuairteáil Ecosia roimhe seo. An bhfuil fonn ort do chuid cluaisíní a athchóiriú?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcáil “Léite”"; @@ -308,7 +308,7 @@ "Okay" = "Tá"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Úps! Thuairteáil Firefox"; +"Oops! Firefox crashed" = "Úps! Thuairteáil Ecosia"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Tapáil an deilbhín leabhair sa bharra teidil chun alt a oscailt san Amharc Léitheoireachta."; @@ -317,7 +317,7 @@ "Open Settings" = "Oscail na Socruithe"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Ní féidir le Firefox an leathanach a oscailt mar gheall ar sheoladh neamhbhailí."; +"OpenURL.Error.Message" = "Ní féidir le Ecosia an leathanach a oscailt mar gheall ar sheoladh neamhbhailí."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Ní féidir an leathanach a oscailt"; @@ -413,7 +413,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "An bhfuil fonn ort tuairisc tuairteála a sheoladh chuig Mozilla leis an bhfadhb a réiteach?"; +"Send a crash report so Mozilla can fix the problem?" = "An bhfuil fonn ort tuairisc tuairteála a sheoladh chuig Ecosia leis an bhfadhb a réiteach?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Seol Aiseolas"; @@ -449,7 +449,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tapáil le tosú"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Sepia theme setting in Reading View settings */ "Sepia" = "Donnrua"; @@ -599,7 +599,7 @@ "Yesterday" = "Inné"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Níl aon chluaisíní oscailte agat i bhFirefox ar do ghléasanna eile."; +"You don’t have any tabs open in Firefox on your other devices." = "Níl aon chluaisíní oscailte agat i bhEcosia ar do ghléasanna eile."; /* Your Rights settings section title */ "Your Rights" = "Do Chearta"; diff --git a/firefox-ios/Shared/ga.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ga.lproj/PrivateBrowsing.strings index af98eef7d5a85..4992bfaf943a1 100644 --- a/firefox-ios/Shared/ga.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ga.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Dún Cluaisíní Príobháideacha"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ní choinneoidh Firefox do stair bhrabhsála ná fianáin, ach sábhálfar leabharmharcanna nua."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ní choinneoidh Ecosia do stair bhrabhsála ná fianáin, ach sábhálfar leabharmharcanna nua."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Tuilleadh Eolais"; diff --git a/firefox-ios/Shared/gd.lproj/Default Browser.strings b/firefox-ios/Shared/gd.lproj/Default Browser.strings index 614d4c3f0380a..7a13e44703ba3 100644 --- a/firefox-ios/Shared/gd.lproj/Default Browser.strings +++ b/firefox-ios/Shared/gd.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Cleachd Firefox mar am brabhsair bunaiteach agad airson taic a chumail ri eadar-lìon fosgailte agus so-ruigsinneach."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Cleachd Ecosia mar am brabhsair bunaiteach agad airson taic a chumail ri eadar-lìon fosgailte agus so-ruigsinneach."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Cleachd mar bhun-roghainn airson eadar-lìon nas fheàrr"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Barrachd fiosrachaidh"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Suidhich ceanglaichean o làraichean-lìn, puist-d agus teachdaireachdan airson ’s gun tèid am fosgladh gu fèin-obrachail ann am Firefox."; +"DefaultBrowserCard.Description" = "Suidhich ceanglaichean o làraichean-lìn, puist-d agus teachdaireachdan airson ’s gun tèid am fosgladh gu fèin-obrachail ann am Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Tagh Firefox mar am brabhsair bunaiteach agad airson luaths, sàbhailteachd is prìobhaideachd a ghnàth."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Tagh Ecosia mar am brabhsair bunaiteach agad airson luaths, sàbhailteachd is prìobhaideachd a ghnàth."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Brabhsadh làitheil nas fheàrr"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Bacaidh Firefox barrachd air 3,000 tracaice an cleachdaiche gach mìor sa chuibheas. Cleachd am fear againne mar am brabhsair bunaiteach agad is seachain draghan mu phrìobhaideachd."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Bacaidh Ecosia barrachd air 3,000 tracaice an cleachdaiche gach mìor sa chuibheas. Cleachd am fear againne mar am brabhsair bunaiteach agad is seachain draghan mu phrìobhaideachd."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Tha prìobhaideachd aig teas-meadhan Firefox"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Tha prìobhaideachd aig teas-meadhan Ecosia"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Leum dhan bhrabhsair bhunaiteach agad"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Thoir gnogag air “Aplacaid brabhsair bhunaiteach”"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Tagh Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Tagh Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplacaid brabhsair bhunaiteach"; diff --git a/firefox-ios/Shared/gd.lproj/Intro.strings b/firefox-ios/Shared/gd.lproj/Intro.strings index f42697cd2f73a..dd9b9cacdcc1e 100644 --- a/firefox-ios/Shared/gd.lproj/Intro.strings +++ b/firefox-ios/Shared/gd.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Clàraich a-steach"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Fàilte gu Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Fàilte gu Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Tòisich air brabhsadh"; diff --git a/firefox-ios/Shared/gd.lproj/Localizable.strings b/firefox-ios/Shared/gd.lproj/Localizable.strings index 3de0e00fb563e..016167611cf70 100644 --- a/firefox-ios/Shared/gd.lproj/Localizable.strings +++ b/firefox-ios/Shared/gd.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Dh’fhaoidte gu bheil e air a dhroch-rèiteachadh no gu bheil cuideigin a’ toirt ionnsaigh air. Lean air d’ adhart ma tha thu leagte ris a leithid a chunnart."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Rinn an neach aig a bheil %@ droch-rèiteachadh air an làrach-lìn aca. Cha do rinn Firefox ceangal ris an làrach-lìn seo air eagal ’s gun rachadh dàta a ghoid ort."; +"ErrorPages.CertWarning.Description" = "Rinn an neach aig a bheil %@ droch-rèiteachadh air an làrach-lìn aca. Cha do rinn Ecosia ceangal ris an làrach-lìn seo air eagal ’s gun rachadh dàta a ghoid ort."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Chan eil earbsa sa cheangal"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Duilleag mhòr Firefox"; +"Firefox.HomePage.Title" = "Duilleag mhòr Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Gnàthaich an duilleag-dhachaigh"; @@ -377,7 +377,7 @@ "Forward" = "Air adhart"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cunntas Firefox"; +"FxA.FirefoxAccount" = "Cunntas Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Stiùirich an cunntas ⁊ na h-uidheaman"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Clàraich a-steach leis a’ chamara agad"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Fosgail Firefox air a’ choimpiutair agad is tadhail air firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Fosgail Ecosia air a’ choimpiutair agad is tadhail air ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Deiseil airson sganadh"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sioncronaich an-dràsta"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Cheangail gleus sioncronachadh Firefox ri %@"; +"FxAPush_DeviceConnected_body" = "Cheangail gleus sioncronachadh Ecosia ri %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Rinn gleus an t-sioncronachaidh ceangal"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Chaidh %@ a dhì-cheangal."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Chaidh an t-uidheam seo a dhì-cheangal o ghleus sioncronachadh Firefox."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Chaidh an t-uidheam seo a dhì-cheangal o ghleus sioncronachadh Ecosia."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Chaidh gleus an t-sioncronachaidh a dhì-cheangal"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Chaidh gleus an t-sioncronachaidh a dhì-cheangal"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Dhì-cheangail uidheam o ghleus sioncronachadh Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Dhì-cheangail uidheam o ghleus sioncronachadh Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Suaicheantas Firefox, atharraich am pàipear-balla"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Suaicheantas Ecosia, atharraich am pàipear-balla"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Cobhair"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Clàraich a-steach airson liosta de thabaichean a tha agad air uidheaman eile."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Sioncronachadh Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Sioncronachadh Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "An eachdraidh"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Barrachd fiosrachaidh"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Feumaidh còd-faire uidheim a bhith an comas agad mus urrainn dhut gleus an fhèin-lìonaidh aig Firefox a chleachdadh."; +"Logins.PasscodeRequirement.Warning" = "Feumaidh còd-faire uidheim a bhith an comas agad mus urrainn dhut gleus an fhèin-lìonaidh aig Ecosia a chleachdadh."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Thoir leat na faclan-faire agad ge be càit an tèid thu"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Fèin-lìon faclan-faire Firefox"; +"Logins.WelcomeView.Title2" = "Fèin-lìon faclan-faire Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Cuir gleus an fhèin-lìonaidh air"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "CLÀRAIDHEAN A-STEACH A SHÀBHAILEADH"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Tha coltas gun do thuislich Firefox roimhe. A bheil thu ag iarraidh nan tabaichean air ais?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Tha coltas gun do thuislich Ecosia roimhe. A bheil thu ag iarraidh nan tabaichean air ais?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Comharraich gun deach a leughadh"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Tòisich air brabhsadh"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ìoc! Thuislich Firefox"; +"Oops! Firefox crashed" = "Ìoc! Thuislich Ecosia"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Fosgail artaigilean san t-sealladh leughaidh le gnogag air ìomhaigheag an leabhair nuair a nochdas e ann am bàr an tiotail."; @@ -905,7 +905,7 @@ "Open Tabs" = "Tabaichean fosgailte"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Chan urrainn dha Firefox an duilleag fhosgladh a chionn ’s gu bheul an seòladh cearr."; +"OpenURL.Error.Message" = "Chan urrainn dha Ecosia an duilleag fhosgladh a chionn ’s gu bheul an seòladh cearr."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Cha ghabh an duilleag fhosgladh"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Bheir seo comas dhut an dealbh a shàbhaladh ann an albam a’ chamara agad."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Bu mhath le Firefox cothrom air na dealbhan agad"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Bu mhath le Ecosia cothrom air na dealbhan agad"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Dùin"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Tha an dàta mì-dhligheach"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Tadhail air “Roghainnean” > “Firefox” is thoir cead-inntrigidh dha Firefox dhan chamara."; +"ScanQRCode.PermissionError.Message.v100" = "Tadhail air “Roghainnean” > “Ecosia” is thoir cead-inntrigidh dha Ecosia dhan chamara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Sganaich an còd QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Geàrr leum gun taba"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nochdaidh an t-einnsean-luirg ùr ann am bàr nan lorg gu luath."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Ceart ma-thà"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "A bheil thu deònach aithisg a chur gu Mozilla mun tuisleadh ach an urrainn dhuinn a chàradh?"; +"Send a crash report so Mozilla can fix the problem?" = "A bheil thu deònach aithisg a chur gu Ecosia mun tuisleadh ach an urrainn dhuinn a chàradh?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Cuir thugainn do bheachdan"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Cuir an taba"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Cha do cheangail thu uidheam sam bith eile ris a’ chunntas Firefox seo as urrainn dhut sioncronachadh."; +"SendTo.NoDevicesFound.Message" = "Cha do cheangail thu uidheam sam bith eile ris a’ chunntas Ecosia seo as urrainn dhut sioncronachadh."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Fosgail na roghainnean ann am Firefox is clàraich a-steach a leantainn air adhart."; +"SendTo.NotSignedIn.Message" = "Fosgail na roghainnean ann am Ecosia is clàraich a-steach a leantainn air adhart."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Cha do rinn thu clàradh a-steach dhan chunntas Firefox agad."; +"SendTo.NotSignedIn.Title" = "Cha do rinn thu clàradh a-steach dhan chunntas Ecosia agad."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Cuir"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Thoir gnogag airson tòiseachadh"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Sioncronachadh Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Sioncronachadh Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Sealladh"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Stiùireadh dàta"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Sguiridh Firefox de shioncronachadh a’ chunntais agad ach cha sguab e às gin dhen dàta brabhsaidh agad air an uidheam seo."; +"Settings.Disconnect.Body" = "Sguiridh Ecosia de shioncronachadh a’ chunntais agad ach cha sguab e às gin dhen dàta brabhsaidh agad air an uidheam seo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Dì-cheangail gleus an t-sioncronachaidh"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Roghainnean sioncronachaidh"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cunntas Firefox"; +"Settings.FxA.Title" = "Cunntas Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Coitcheann"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Tagh na chì thu air an duilleag-dhachaigh."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Tagh na chì thu air duilleag-dhachaigh Firefox."; +"Settings.Home.Option.Description.v101" = "Tagh na chì thu air duilleag-dhachaigh Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Till gu far an robh thu"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Duilleag-dhachaigh"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Tagh na chì thu nuair a thilleas tu gu Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Tagh na chì thu nuair a thilleas tu gu Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "An taba mu dheireadh"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Pàipear-balla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Pàipear-balla Firefox, pàtran amaitist."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Pàipear-balla Ecosia, pàtran amaitist."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Pàipear-balla Firefox, pàtran chnoc ri taobh na mara."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Pàipear-balla Ecosia, pàtran chnoc ri taobh na mara."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Pàipear-balla Firefox, pàtran nan speuran."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Pàipear-balla Ecosia, pàtran nan speuran."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Pàipear-balla soilleir bunaiteach."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Pàipear-balla Firefox, pàtran èirigh na grèine."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Pàipear-balla Ecosia, pàtran èirigh na grèine."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Toglachadh cuairt pàipear-balla na duilleige-dachaigh"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Pàipear-balla Firefox, pàtran chnoc sa chamhanaich."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Pàipear-balla Ecosia, pàtran chnoc sa chamhanaich."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clasaigeach"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Tionndadh cuingichte"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Atharraich am pàipear-balla le bhith a’ toirt gnogag air suaicheantas duilleag-dhachaigh Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Atharraich am pàipear-balla le bhith a’ toirt gnogag air suaicheantas duilleag-dhachaigh Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Pàipear-balla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Gnàthaichte"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Dachaigh Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Dachaigh Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Duilleag-dhachaigh"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bac dealbhan"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Nuair a dh’fhosglas mi Firefox"; +"Settings.OfferClipboardBar.Status" = "Nuair a dh’fhosglas mi Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Nuair a dh’fhosglas tu %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Nuair a dh’fhosglas mi Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Nuair a dh’fhosglas mi Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "faighnich a bheil mi airson ceangal a rinn mi lethbhreac dheth fhosgladh"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Barrachd fiosrachaidh."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Tha Mozilla an dealas dìreach an dàta a chruinneachadh a dh’fheumas sinn airson Firefox a sholar is a leasachadh dhan a h-uile duine."; +"Settings.SendUsage.Message" = "Tha Ecosia an dealas dìreach an dàta a chruinneachadh a dh’fheumas sinn airson Ecosia a sholar is a leasachadh dhan a h-uile duine."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Cuir an dàta cleachdaidh"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Fosgail taba ùr"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Cleachd ath-ghoiridean Siri airson Firefox fhosgladh gu luath le Siri"; +"Settings.Siri.SectionDescription" = "Cleachd ath-ghoiridean Siri airson Ecosia fhosgladh gu luath le Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Ath-ghoiridean Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Barrachd fiosrachaidh."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Dh’fhaoidte gun stàlaich is ruith Firefox rannsachadh o àm gu àm."; +"Settings.Studies.Toggle.Message" = "Dh’fhaoidte gun stàlaich is ruith Ecosia rannsachadh o àm gu àm."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Rannsachadh"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Luchdaich sa chùlaibh"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Ga luchdadh ann am Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ga luchdadh ann am Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Fosgail ann am Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Fosgail ann am Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Lorg ann am Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Lorg ann am Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Dèan lethbhreac"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Cuir ann ⁊ tadhail air"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Lorg le Firefox"; +"UIMenuItem.SearchWithFirefox" = "Lorg le Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "gun leughadh"; @@ -1793,7 +1793,7 @@ "Yesterday" = "An-dè"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Chan eil taba sam bith fosgailte agad ann am Firefox air uidheaman eile."; +"You don’t have any tabs open in Firefox on your other devices." = "Chan eil taba sam bith fosgailte agad ann am Ecosia air uidheaman eile."; /* Your Rights settings section title */ "Your Rights" = "Do chòraichean"; diff --git a/firefox-ios/Shared/gd.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/gd.lproj/PrivateBrowsing.strings index 32f536a5df798..ecfd39f4451a5 100644 --- a/firefox-ios/Shared/gd.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/gd.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Fosgail ann an taba prìobhaideach ùr"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Cha chuimhnich Firefox gin dhen eachdraidh no briosgaidean ach ma shàbhaileas tu comharra-lìn ùr, thèid a ghlèidheadh."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Cha chuimhnich Ecosia gin dhen eachdraidh no briosgaidean ach ma shàbhaileas tu comharra-lìn ùr, thèid a ghlèidheadh."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Barrachd fiosrachaidh"; diff --git a/firefox-ios/Shared/gd.lproj/Today.strings b/firefox-ios/Shared/gd.lproj/Today.strings index 1f3128b9206a4..83bb4d881048f 100644 --- a/firefox-ios/Shared/gd.lproj/Today.strings +++ b/firefox-ios/Shared/gd.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Dùin na tabaichean prìobhaideach"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Cuir ath-ghoiridean Firefox ris an sgrìn-dhachaigh agad."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Cuir ath-ghoiridean Ecosia ris an sgrìn-dhachaigh agad."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Tadhail air a’ cheangal a rinn thu lethbhreac dheth"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Chan eil taba fosgailte ann."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Fosgail Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Fosgail Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Lorg prìobhaideach"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Cuir ath-ghoirid Firefox ris an sgrìn-dhachaigh agad. An dèidh dhut a’ widget a chur ris, bean ris is cùm e airson a dheasachadh agus ath-ghoirid eadar-dhealaichte a thaghadh."; +"TodayWidget.QuickActionGalleryDescription" = "Cuir ath-ghoirid Ecosia ris an sgrìn-dhachaigh agad. An dèidh dhut a’ widget a chur ris, bean ris is cùm e airson a dheasachadh agus ath-ghoirid eadar-dhealaichte a thaghadh."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Grad-ghnìomhan"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Ath-ghoiridean Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ath-ghoiridean Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Cuir ath-ghoiridean ris na tabaichean fosgailte agad."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Grad-shealladh"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Lorg ann am Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Lorg ann am Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Lorg ann an taba prìobhaideach"; diff --git a/firefox-ios/Shared/gl.lproj/Default Browser.strings b/firefox-ios/Shared/gl.lproj/Default Browser.strings index 6fcafc997a17f..2fdb6ad1178d0 100644 --- a/firefox-ios/Shared/gl.lproj/Default Browser.strings +++ b/firefox-ios/Shared/gl.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Facer Firefox o seu navegador predeterminado é un voto por unha Internet aberta e accesíbel."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Facer Ecosia o seu navegador predeterminado é un voto por unha Internet aberta e accesíbel."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Unha mellor Internet de forma predeterminada"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Aprender como"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Estableza ligazóns de sitios web, correos electrónicos e mensaxes para que se abran automaticamente en Firefox."; +"DefaultBrowserCard.Description" = "Estableza ligazóns de sitios web, correos electrónicos e mensaxes para que se abran automaticamente en Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Escolla Firefox como o seu navegador predeterminado para que a velocidade, a seguridade e a privacidade sexan automáticas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Escolla Ecosia como o seu navegador predeterminado para que a velocidade, a seguridade e a privacidade sexan automáticas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Mellore a súa navegación diaria"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox bloquea máis de 3.000 rastrexadores por usuario cada mes de media. Convértanos no seu navegador predeterminado para a súa tranquilidade e privacidade."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia bloquea máis de 3.000 rastrexadores por usuario cada mes de media. Convértanos no seu navegador predeterminado para a súa tranquilidade e privacidade."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ten a privacidade cuberta"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ten a privacidade cuberta"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambia o teu navegador predeterminado"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Pulse a Aplicación do navegador por defecto"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleccione Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Seleccione Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplicación de navegador predeterminada"; diff --git a/firefox-ios/Shared/gl.lproj/Intro.strings b/firefox-ios/Shared/gl.lproj/Intro.strings index 3ae52bad61d37..161f21619b51b 100644 --- a/firefox-ios/Shared/gl.lproj/Intro.strings +++ b/firefox-ios/Shared/gl.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Acceder"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Benvida ao Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Benvida ao Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Iniciar a navegación"; diff --git a/firefox-ios/Shared/gl.lproj/Localizable.strings b/firefox-ios/Shared/gl.lproj/Localizable.strings index 9872fd90e3bec..912ee001c6df7 100644 --- a/firefox-ios/Shared/gl.lproj/Localizable.strings +++ b/firefox-ios/Shared/gl.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Pode que sexa unha mala configuración ou unha tentativa de ataque. Continúe se acepta o posible risco."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "O propietario de %@ configurou incorrectamente o seu sitio web. Para protexer a súa información contra roubos, Firefox non se conectou a este sitio web."; +"ErrorPages.CertWarning.Description" = "O propietario de %@ configurou incorrectamente o seu sitio web. Para protexer a súa información contra roubos, Ecosia non se conectou a este sitio web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Esta conexión non é fiábel"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Páxina de inicio do Firefox"; +"Firefox.HomePage.Title" = "Páxina de inicio do Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar a páxina de inicio"; @@ -377,7 +377,7 @@ "Forward" = "Seguinte"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Conta Firefox"; +"FxA.FirefoxAccount" = "Conta Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Xestionar conta e dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Acceder coa cámara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Abra Firefox no computador e vaia a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Abra Ecosia no computador e vaia a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Listo para escanear"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar agora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync conectouse a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync conectouse a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ desconectouse correctamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo desconectouse correctamente de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo desconectouse correctamente de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo desconectouse de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo desconectouse de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Firefox, cambiar o fondo de pantalla."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo de Ecosia, cambiar o fondo de pantalla."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Axuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Acceda para ver unha lista de lapelas dos outros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historial"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Máis información"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para usar a función de completado automático para Firefox, debe ter activado un código de acceso do dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Para usar a función de completado automático para Ecosia, debe ter activado un código de acceso do dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Leve os seus contrasinais a todas partes"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autocompletar contrasinais en Firefox"; +"Logins.WelcomeView.Title2" = "Autocompletar contrasinais en Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activar autocompletado"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "IDENTIFICACIÓNS GARDADAS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Semella que Firefox se pechou inesperadamente. Desexa restaurar as súas lapelas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Semella que Ecosia se pechou inesperadamente. Desexa restaurar as súas lapelas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como lido"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Iniciar a navegación"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Vaia! Firefox quebrou"; +"Oops! Firefox crashed" = "Vaia! Ecosia quebrou"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abra artigos na vista de lectura pulsando na icona do libro cando apareza na barra de título."; @@ -905,7 +905,7 @@ "Open Tabs" = "Lapelas abertas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Non é posíbel que Firefox abra a páxina porque o enderezo non é válido."; +"OpenURL.Error.Message" = "Non é posíbel que Ecosia abra a páxina porque o enderezo non é válido."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Non foi posíbel abrir a páxina"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Isto permítelle gardar a imaxe no seu carrete."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox quere acceder ás súas fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia quere acceder ás súas fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Pechar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "A data non é válida"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Vaia a «Configuración» > «Firefox». Permitir que Firefox acceda á cámara."; +"ScanQRCode.PermissionError.Message.v100" = "Vaia a «Configuración» > «Ecosia». Permitir que Ecosia acceda á cámara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Examinar código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Cambiar á lapela"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox suxire"; +"Search.SuggestSectionTitle.v102" = "Ecosia suxire"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "O novo buscador aparecerá na barra de busca rápida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Aceptar"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Enviar un informe de erro para que Mozilla poida solucionar o problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Enviar un informe de erro para que Ecosia poida solucionar o problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar opinión"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar o separador"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Non ten ningún outro dispositivo conectado a esta conta de Firefox dispoñíbel para sincronizarse."; +"SendTo.NoDevicesFound.Message" = "Non ten ningún outro dispositivo conectado a esta conta de Ecosia dispoñíbel para sincronizarse."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Abra Firefox, vaia á configuración e identifíquese para continuar."; +"SendTo.NotSignedIn.Message" = "Abra Ecosia, vaia á configuración e identifíquese para continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Non se identificou coa súa conta de Firefox."; +"SendTo.NotSignedIn.Title" = "Non se identificou coa súa conta de Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Pulse para comezar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Xestión dos datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox deterá a sincronización coa súa conta, pero non eliminará ningún dos seus datos de navegación neste dispositivo."; +"Settings.Disconnect.Body" = "Ecosia deterá a sincronización coa súa conta, pero non eliminará ningún dos seus datos de navegación neste dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectar Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Axustes de sincronización"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Conta Firefox"; +"Settings.FxA.Title" = "Conta Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Xeral"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Escolla o que se mostra como páxina de inicio."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Escolls o que se inclúe na páxina de inicio de Firefox."; +"Settings.Home.Option.Description.v101" = "Escolls o que se inclúe na páxina de inicio de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Volver"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Páxina de inicio"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Escolla o que ve cando volva a Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Escolla o que ve cando volva a Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Última lapela"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fondo de pantalla"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Firefox, padrón de amatista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fondo de pantalla de Ecosia, padrón de amatista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Firefox, padrón de dunas de praia."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, padrón de dunas de praia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Firefox, padrón cerúleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fondo de pantalla de Ecosia, padrón cerúleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fondo de pantalla claro predeterminado."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Firefox, padrón de amencer."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fondo de pantalla de Ecosia, padrón de amencer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Cambio de ciclo do fondo de pantalla da páxina de inicio"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Firefox, padrón de outeiros crepusculares."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fondo de pantalla de Ecosia, padrón de outeiros crepusculares."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clásico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edición limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia o fondo de pantalla tocando o logotipo da páxina de inicio de Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia o fondo de pantalla tocando o logotipo da páxina de inicio de Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fondo de pantalla"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizado"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Inicio de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Inicio de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Páxina de inicio"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imaxes"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Ao abrir o Firefox"; +"Settings.OfferClipboardBar.Status" = "Ao abrir o Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Ao abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Ao abrir o Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Ao abrir o Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Propor abrir ligazóns copiadas"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Máis información."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla esforzase por recoller só os datos necesarios para fornecer e mellorar Firefox para todos."; +"Settings.SendUsage.Message" = "Ecosia esforzase por recoller só os datos necesarios para fornecer e mellorar Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar datos de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir unha nova lapela"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Usar os atallos de Siri para abrir rapidamente Firefox a través de Siri"; +"Settings.Siri.SectionDescription" = "Usar os atallos de Siri para abrir rapidamente Ecosia a través de Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atallos de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Máis información."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox pode instalar e executar estudos de vez en cando."; +"Settings.Studies.Toggle.Message" = "Ecosia pode instalar e executar estudos de vez en cando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudos"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando no Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargando no Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir no Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir no Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Buscar no Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Buscar no Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Pegar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Buscar co Firefox"; +"UIMenuItem.SearchWithFirefox" = "Buscar co Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "non lido"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Onte"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Non ten ningunha lapela aberta en Firefox nos outros dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "Non ten ningunha lapela aberta en Ecosia nos outros dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Os seus dereitos"; diff --git a/firefox-ios/Shared/gl.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/gl.lproj/PrivateBrowsing.strings index 2a0396beb9d95..b1162cf1c662e 100644 --- a/firefox-ios/Shared/gl.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/gl.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir nun novo separador privado"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox non lembrará nada do seu historial ou cookies, pero si que se gardarán os novos marcadores."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia non lembrará nada do seu historial ou cookies, pero si que se gardarán os novos marcadores."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Máis información"; diff --git a/firefox-ios/Shared/gl.lproj/Today.strings b/firefox-ios/Shared/gl.lproj/Today.strings index 40b19c82ce79e..f10d66043514b 100644 --- a/firefox-ios/Shared/gl.lproj/Today.strings +++ b/firefox-ios/Shared/gl.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Pechar\nlapelas privadas"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Engada atallos de Firefox á súa pantalla de inicio."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Engada atallos de Ecosia á súa pantalla de inicio."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir á ligazón copiada"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ningunha lapela aberta."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Busca privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Engada un atallo de Firefox á súa pantalla de inicio. Despois de engadir o widget, manteña premido para editalo e seleccione un atallo diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Engada un atallo de Ecosia á súa pantalla de inicio. Despois de engadir o widget, manteña premido para editalo e seleccione un atallo diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Accións rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Atallos de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Atallos de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Engada atallos as súas lapelas abertas."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Buscar no\nFirefox"; +"TodayWidget.SearchInFirefoxV2" = "Buscar no\nEcosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Buscar nunha\nlapela privada"; diff --git a/firefox-ios/Shared/gu-IN.lproj/Intro.strings b/firefox-ios/Shared/gu-IN.lproj/Intro.strings index f59e1121ba122..d99cf911589d6 100644 --- a/firefox-ios/Shared/gu-IN.lproj/Intro.strings +++ b/firefox-ios/Shared/gu-IN.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "સાઇન ઇન"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox માં તમારું સ્વાગત છે"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia માં તમારું સ્વાગત છે"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "બ્રાઉઝિંગ પ્રારંભ કરો"; diff --git a/firefox-ios/Shared/gu-IN.lproj/Localizable.strings b/firefox-ios/Shared/gu-IN.lproj/Localizable.strings index e5c576a374ffa..f24b0edd60a26 100644 --- a/firefox-ios/Shared/gu-IN.lproj/Localizable.strings +++ b/firefox-ios/Shared/gu-IN.lproj/Localizable.strings @@ -239,7 +239,7 @@ "ErrorPages.AdvancedWarning2.Text" = "તે કોઈ અયોગ્ય ગોઠવણી અથવા હુમલાખોર દ્વારા છૂટાછેડા થઈ શકે છે. જો તમે સંભવિત જોખમને સ્વીકારો છો તો આગળ વધો."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ ના માલિકે તેમની વેબસાઇટને અયોગ્ય રીતે ગોઠવી છે. તમારી માહિતી ચોરી કરવાથી બચાવવા માટે, Firefox આ વેબસાઇટથી જોડાયેલું નથી."; +"ErrorPages.CertWarning.Description" = "%@ ના માલિકે તેમની વેબસાઇટને અયોગ્ય રીતે ગોઠવી છે. તમારી માહિતી ચોરી કરવાથી બચાવવા માટે, Ecosia આ વેબસાઇટથી જોડાયેલું નથી."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "આ જોડાણ અવિશ્વસનીય છે"; @@ -260,13 +260,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox મુખ્ય પૃષ્ઠ"; +"Firefox.HomePage.Title" = "Ecosia મુખ્ય પૃષ્ઠ"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "આગડ"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox ખાતું"; +"FxA.FirefoxAccount" = "Ecosia ખાતું"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ખાતું અને ઉપકરણો મેનેજ કરો"; @@ -278,7 +278,7 @@ "FxA.SyncNow" = "હવે સમન્વયિત કરો"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox સમન્વયન %@ થી કનેક્ટ થયેલ છે"; +"FxAPush_DeviceConnected_body" = "Ecosia સમન્વયન %@ થી કનેક્ટ થયેલ છે"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "જોડાયેલ સમન્વય"; @@ -287,7 +287,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ સફળતાપૂર્વક ડિસ્કનેક્ટ થઈ ગયેલ છે."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "આ ઉપકરણ સફળતાપૂર્વક Firefox સમન્વયનથી ડિસ્કનેક્ટ થઈ ગયું છે."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "આ ઉપકરણ સફળતાપૂર્વક Ecosia સમન્વયનથી ડિસ્કનેક્ટ થઈ ગયું છે."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "સમન્વયન છૂટું"; @@ -296,7 +296,7 @@ "FxAPush_DeviceDisconnected_title" = "સમન્વયન છૂટું"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Firefox સમન્વયનથી એક ઉપકરણ ડિસ્કનેક્ટ થયું છે"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ecosia સમન્વયનથી એક ઉપકરણ ડિસ્કનેક્ટ થયું છે"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "મદદ"; @@ -329,7 +329,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "તમારા અન્ય ઉપકરણોથી ટૅબ્સની સૂચિ જોવા માટે સાઇન ઇન કરો."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox સમન્વયન"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia સમન્વયન"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ઇતિહાસ"; @@ -452,7 +452,7 @@ "LoginsList.Title" = "SAVEDLOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "એવું લાગે છે કે Firefox અગાઉ ક્રેશ થયું છે. શું તમે તમારા ટૅબ્સને પુનઃસ્થાપિત કરવા માંગો છો?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "એવું લાગે છે કે Ecosia અગાઉ ક્રેશ થયું છે. શું તમે તમારા ટૅબ્સને પુનઃસ્થાપિત કરવા માંગો છો?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "વાંચેલ તરીકે ચિહ્નિત કરો"; @@ -554,7 +554,7 @@ "Okay" = "બરાબર"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "અરે! Firefox ક્રેશ થયું"; +"Oops! Firefox crashed" = "અરે! Ecosia ક્રેશ થયું"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "શીર્ષક પટ્ટીમાં દેખાય ત્યારે પુસ્તક આયકનને ટેપ કરીને રીડર દૃશ્યમાં લેખો ખોલો."; @@ -566,7 +566,7 @@ "Open Tabs" = "ટૅબ્સ ખોલો"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox પૃષ્ઠ ખોલી શકતું નથી કારણ કે તેમાં અમાન્ય સરનામું છે."; +"OpenURL.Error.Message" = "Ecosia પૃષ્ઠ ખોલી શકતું નથી કારણ કે તેમાં અમાન્ય સરનામું છે."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "પૃષ્ઠ ખોલી શકતા નથી"; @@ -686,7 +686,7 @@ "Search.ThirdPartyEngines.OK" = "ઠીક છે"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "ક્રેશ રિપોર્ટ મોકલો જેથી Mozilla સમસ્યાને ઠીક કરી શકે?"; +"Send a crash report so Mozilla can fix the problem?" = "ક્રેશ રિપોર્ટ મોકલો જેથી Ecosia સમસ્યાને ઠીક કરી શકે?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "પ્રતિસાદ મોકલ"; @@ -713,13 +713,13 @@ "SendTo.NavBar.Title" = "ટૅબ મોકલો"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "તમારી પાસે સમન્વયિત કરવા માટે ઉપલબ્ધ આ Firefox એકાઉન્ટથી કનેક્ટેડ અન્ય ઉપકરણો નથી."; +"SendTo.NoDevicesFound.Message" = "તમારી પાસે સમન્વયિત કરવા માટે ઉપલબ્ધ આ Ecosia એકાઉન્ટથી કનેક્ટેડ અન્ય ઉપકરણો નથી."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "કૃપા કરીને Firefox ખોલો, સેટિંગ્સ પર જાઓ અને ચાલુ રાખવા માટે સાઇન ઇન કરો."; +"SendTo.NotSignedIn.Message" = "કૃપા કરીને Ecosia ખોલો, સેટિંગ્સ પર જાઓ અને ચાલુ રાખવા માટે સાઇન ઇન કરો."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "તમે તમારા Firefox એકાઉન્ટમાં સાઇન ઇન નથી કર્યું."; +"SendTo.NotSignedIn.Title" = "તમે તમારા Ecosia એકાઉન્ટમાં સાઇન ઇન નથી કર્યું."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "મોકલો"; @@ -740,7 +740,7 @@ "SentTab.NoTabArrivingNotification.body" = "પ્રારંભ કરવા માટે ટેપ કરો"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox સીંક"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia સીંક"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "જુઓ"; @@ -797,7 +797,7 @@ "Settings.DataManagement.Title" = "માહિતી વ્યવસ્થાપન"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox તમારા એકાઉન્ટ સાથે સમન્વય કરવાનું બંધ કરશે, પરંતુ આ ઉપકરણ પરના તમારા કોઈપણ બ્રાઉઝિંગ ડેટાને કાઢી નાખશે નહીં."; +"Settings.Disconnect.Body" = "Ecosia તમારા એકાઉન્ટ સાથે સમન્વય કરવાનું બંધ કરશે, પરંતુ આ ઉપકરણ પરના તમારા કોઈપણ બ્રાઉઝિંગ ડેટાને કાઢી નાખશે નહીં."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "સમન્વયન ડિસ્કનેક્ટ કરો"; @@ -860,7 +860,7 @@ "Settings.FxA.Sync.SectionName" = "સમન્વયન સેટિંગ્સ"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox ખાતુ"; +"Settings.FxA.Title" = "Ecosia ખાતુ"; /* General settings section title */ "Settings.General.SectionName" = "સામાન્ય"; @@ -884,7 +884,7 @@ "Settings.NewTab.Option.BlankPage" = "કોરો કાગળ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox હોમ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia હોમ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "મુખપૃષ્ઠ"; @@ -902,10 +902,10 @@ "Settings.NewTab.TopSectionNameFooter" = "નવું ટેબ ખોલતી વખતે શું લોડ કરવું તે પસંદ કરો"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox ખુલતી વખતે"; +"Settings.OfferClipboardBar.Status" = "Ecosia ખુલતી વખતે"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox ખુલતી વખતે"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia ખુલતી વખતે"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "કૉપિ કરેલી લિંક્સ ખોલવાની ઑફર કરો"; @@ -929,7 +929,7 @@ "Settings.SendUsage.Link" = "વધુ શીખો."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ફક્ત દરેકને માટે Firefox આપવા અને સુધારવા માટે જરૂરી છે તે એકત્રિત કરવાનો પ્રયાસ કરે છે."; +"Settings.SendUsage.Message" = "Ecosia ફક્ત દરેકને માટે Ecosia આપવા અને સુધારવા માટે જરૂરી છે તે એકત્રિત કરવાનો પ્રયાસ કરે છે."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "વપરાશ ડેટા મોકલો"; @@ -941,7 +941,7 @@ "Settings.Siri.OpenTabShortcut" = "નવું ટેબ ખોલો"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "સિરી દ્વારા Firefoxને ઝડપથી ખોલવા માટે સિરી શોર્ટકટ્સનો ઉપયોગ કરો"; +"Settings.Siri.SectionDescription" = "સિરી દ્વારા Ecosiaને ઝડપથી ખોલવા માટે સિરી શોર્ટકટ્સનો ઉપયોગ કરો"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "સિરી શૉર્ટકટ્સ"; @@ -998,13 +998,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "પૃષ્ઠભૂમિમાં લોડ કરો"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefoxમાં લોડ કરી રહ્યું છે"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosiaમાં લોડ કરી રહ્યું છે"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefoxમાં ખોલો"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosiaમાં ખોલો"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefoxમાં શોધો"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosiaમાં શોધો"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "શોધ સૂચનો બતાવો"; @@ -1103,7 +1103,7 @@ "UIMenuItem.PasteGo" = "પેસ્ટ કરો અને જાઓ"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox સાથે શોધો"; +"UIMenuItem.SearchWithFirefox" = "Ecosia સાથે શોધો"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ન વાંચેલું"; @@ -1130,7 +1130,7 @@ "Yesterday" = "ગઈ કાલે"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "તમારી પાસે અન્ય ઉપકરણો પર Firefoxમાં કોઈપણ ટેબ્સ ખોલવામાં આવ્યાં નથી."; +"You don’t have any tabs open in Firefox on your other devices." = "તમારી પાસે અન્ય ઉપકરણો પર Ecosiaમાં કોઈપણ ટેબ્સ ખોલવામાં આવ્યાં નથી."; /* Your Rights settings section title */ "Your Rights" = "તમારા અધિકારો"; diff --git a/firefox-ios/Shared/gu-IN.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/gu-IN.lproj/PrivateBrowsing.strings index 4377d55e256dc..650a1eab54209 100644 --- a/firefox-ios/Shared/gu-IN.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/gu-IN.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "નવી ખાનગી ટૅબમાં ખોલો"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox તમારા કોઈપણ ઇતિહાસ અથવા કુકીઝને યાદ કરશે નહીં, પરંતુ નવા બુકમાર્ક્સ સાચવવામાં આવશે."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia તમારા કોઈપણ ઇતિહાસ અથવા કુકીઝને યાદ કરશે નહીં, પરંતુ નવા બુકમાર્ક્સ સાચવવામાં આવશે."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "વધુ શીખો"; diff --git a/firefox-ios/Shared/he.lproj/Default Browser.strings b/firefox-ios/Shared/he.lproj/Default Browser.strings index 2af2c9b920d1f..40a120cee8398 100644 --- a/firefox-ios/Shared/he.lproj/Default Browser.strings +++ b/firefox-ios/Shared/he.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "הפיכת Firefox לדפדפן ברירת המחדל שלך היא הצבעה בעד אינטרנט פתוח ונגיש."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "הפיכת Ecosia לדפדפן ברירת המחדל שלך היא הצבעה בעד אינטרנט פתוח ונגיש."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "לעבור לאינטרנט טוב יותר כברירת מחדל"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "מידע נוסף"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "פתיחה אוטומטית של קישורים מאתרים, מהודעות דוא״ל ומהיישום הודעות ב־Firefox."; +"DefaultBrowserCard.Description" = "פתיחה אוטומטית של קישורים מאתרים, מהודעות דוא״ל ומהיישום הודעות ב־Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "כדאי לבחור ב־Firefox כדפדפן ברירת המחדל שלך בשביל מהירות, בטיחות ופרטיות אוטומטית."; +"DefaultBrowserCard.NextLevel.Description.v108" = "כדאי לבחור ב־Ecosia כדפדפן ברירת המחדל שלך בשביל מהירות, בטיחות ופרטיות אוטומטית."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "שיפור הגלישה היומיומית"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "‏Firefox חוסם בממוצע למעלה מ־3,000 רכיבי מעקב למשתמש בכל חודש. הפכו אותנו לדפדפן ברירת המחדל שלך לקבלת שקט נפשי בפרטיות."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "‏Ecosia חוסם בממוצע למעלה מ־3,000 רכיבי מעקב למשתמש בכל חודש. הפכו אותנו לדפדפן ברירת המחדל שלך לקבלת שקט נפשי בפרטיות."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "‏Firefox דואג לפרטיות שלך"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "‏Ecosia דואג לפרטיות שלך"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "החלפת דפדפן ברירת המחדל שלך"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. להקיש על אפליקציית דפדפן ברירת מחדל"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. לבחור ב־Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. לבחור ב־Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "יישומון דפדפן ברירת המחדל"; diff --git a/firefox-ios/Shared/he.lproj/Intro.strings b/firefox-ios/Shared/he.lproj/Intro.strings index 1ec11f92d7ea0..a645b9e032306 100644 --- a/firefox-ios/Shared/he.lproj/Intro.strings +++ b/firefox-ios/Shared/he.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "כניסה"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "ברוכים הבאים אל Firefox"; +"Intro.Slides.Welcome.Title.v2" = "ברוכים הבאים אל Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "התחלת גלישה"; diff --git a/firefox-ios/Shared/he.lproj/Localizable.strings b/firefox-ios/Shared/he.lproj/Localizable.strings index d394d40f760d7..4d23e4251dd27 100644 --- a/firefox-ios/Shared/he.lproj/Localizable.strings +++ b/firefox-ios/Shared/he.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "יתכן כי מדובר בהגדרות לקויות או בתוקף. ניתן להמשיך אם הסיכון האפשרי מקובל עליך."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "הבעלים של %@ הגדיר את האתר באופן לקוי. כדי להגן על המידע שלך מפני גניבה Firefox מנע את החיבור לאתר זה."; +"ErrorPages.CertWarning.Description" = "הבעלים של %@ הגדיר את האתר באופן לקוי. כדי להגן על המידע שלך מפני גניבה Ecosia מנע את החיבור לאתר זה."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "חיבור זה אינו בטוח"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "דף הבית של Firefox"; +"Firefox.HomePage.Title" = "דף הבית של Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "התאמה אישית של מסך הבית"; @@ -377,7 +377,7 @@ "Forward" = "קדימה"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "חשבון Firefox"; +"FxA.FirefoxAccount" = "חשבון Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ניהול חשבון ומכשירים"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "התחברות באמצעות המצלמה שלך"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "במחשב שלך יש לפתוח את Firefox ולעבור לכתובת firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "במחשב שלך יש לפתוח את Ecosia ולעבור לכתובת ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "מוכן לסריקה"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "סנכרון כעת"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "‏Firefox Sync התחבר ל־%@"; +"FxAPush_DeviceConnected_body" = "‏Ecosia Sync התחבר ל־%@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "‏Sync מחובר"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "‏%@ נותק בהצלחה."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "מכשיר זה נותק בהצלחה מ־Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "מכשיר זה נותק בהצלחה מ־Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "‏Sync מנותק"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "‏Sync מנותק"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "נותק מכשיר מ־Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "נותק מכשיר מ־Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "הלוגו של Firefox, החלפת תמונת הרקע."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "הלוגו של Ecosia, החלפת תמונת הרקע."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "עזרה"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "יש להיכנס כדי לצפות ברשימת לשוניות משאר המכשירים שלך."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "היסטוריה"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "מידע נוסף"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "כדי להשתמש בתכונה של המילוי האוטומטי עבור Firefox, עליך להפעיל קוד גישה במכשיר."; +"Logins.PasscodeRequirement.Warning" = "כדי להשתמש בתכונה של המילוי האוטומטי עבור Ecosia, עליך להפעיל קוד גישה במכשיר."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "לקחת את הססמאות שלך לכל מקום"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "מילוי אוטומטי של ססמאות ב־Firefox"; +"Logins.WelcomeView.Title2" = "מילוי אוטומטי של ססמאות ב־Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "הפעלת מילוי אוטומטי"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "כניסות שמורות"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "נראה ש־Firefox קרס לאחרונה. האם ברצונך לשחזר את הלשוניות שלך?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "נראה ש־Ecosia קרס לאחרונה. האם ברצונך לשחזר את הלשוניות שלך?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "סימון כנקרא"; @@ -890,7 +890,7 @@ "Onboarding.WhatsNew.Button.Title" = "התחלת גלישה"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "אבוי! Firefox קרס"; +"Oops! Firefox crashed" = "אבוי! Ecosia קרס"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "ניתן לפתוח מאמרים בתצוגת קריאה על־ידי נגיעה בסמל הספר כאשר הוא מופיע בשורת הכותרת."; @@ -902,7 +902,7 @@ "Open Tabs" = "פתיחת לשוניות"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "דפדפן Firefox אינו יכול לפתוח דף זה מאחר שהוא כולל כתובת שאינה חוקית."; +"OpenURL.Error.Message" = "דפדפן Ecosia אינו יכול לפתוח דף זה מאחר שהוא כולל כתובת שאינה חוקית."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "לא ניתן לפתוח דף"; @@ -914,7 +914,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "תכונה זו מאפשרת לך לשמור תמונות בסליל המצלמה שלך."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "ל־Firefox יש צורך בגישה לתמונות שלך"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "ל־Ecosia יש צורך בגישה לתמונות שלך"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "סגירה"; @@ -1001,7 +1001,7 @@ "ScanQRCode.InvalidDataError.Message" = "הנתונים שגויים"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "יש לעבור להגדרות במכשיר, לבחור ב־Firefox, ולאפשר ל־Firefox לגשת למצלמה."; +"ScanQRCode.PermissionError.Message.v100" = "יש לעבור להגדרות במכשיר, לבחור ב־Ecosia, ולאפשר ל־Ecosia לגשת למצלמה."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "סריקת קוד QR"; @@ -1016,7 +1016,7 @@ "Search.Awesomebar.SwitchToTab" = "מעבר ללשונית"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "הצעות של Firefox"; +"Search.SuggestSectionTitle.v102" = "הצעות של Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "מנועי החיפוש החדשים יופיעו בסרגל החיפוש המהיר."; @@ -1052,7 +1052,7 @@ "Search.ThirdPartyEngines.OK" = "אישור"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "האם לשלוח דיווח קריסה אל Mozilla כדי לסייע בתיקון הבעיה?"; +"Send a crash report so Mozilla can fix the problem?" = "האם לשלוח דיווח קריסה אל Ecosia כדי לסייע בתיקון הבעיה?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "שליחת משוב"; @@ -1082,13 +1082,13 @@ "SendTo.NavBar.Title" = "שליחת לשונית"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "אין לך מכשירים אחרים המחוברים לחשבון Firefox זה הזמינים לסנכרון."; +"SendTo.NoDevicesFound.Message" = "אין לך מכשירים אחרים המחוברים לחשבון Ecosia זה הזמינים לסנכרון."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "נא לפתוח את Firefox, לעבור אל ההגדרות ולהתחבר, כדי להמשיך."; +"SendTo.NotSignedIn.Message" = "נא לפתוח את Ecosia, לעבור אל ההגדרות ולהתחבר, כדי להמשיך."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "לא מחובר לחשבון Firefox."; +"SendTo.NotSignedIn.Title" = "לא מחובר לחשבון Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "שליחה"; @@ -1109,7 +1109,7 @@ "SentTab.NoTabArrivingNotification.body" = "יש לגעת כדי להתחיל"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "הצגה"; @@ -1172,7 +1172,7 @@ "Settings.DataManagement.Title" = "ניהול נתונים"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "‏Firefox יפסיק להסתנכרן עם החשבון שלך, אבל לא ימחק את נתוני הגלישה שלך ממכשיר זה."; +"Settings.Disconnect.Body" = "‏Ecosia יפסיק להסתנכרן עם החשבון שלך, אבל לא ימחק את נתוני הגלישה שלך ממכשיר זה."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "התנתקות"; @@ -1238,7 +1238,7 @@ "Settings.FxA.Sync.SectionName" = "הגדרות סנכרון"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "חשבון Firefox"; +"Settings.FxA.Title" = "חשבון Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "כללי"; @@ -1247,7 +1247,7 @@ "Settings.Home.Current.Description.v101" = "נא לבחור מה יופיע כדף הבית."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "נא לבחור מה יופיע בדף הבית של Firefox."; +"Settings.Home.Option.Description.v101" = "נא לבחור מה יופיע בדף הבית של Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "המשך גלישה"; @@ -1271,7 +1271,7 @@ "Settings.Home.Option.StartAtHome.Always" = "מסך הבית"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "בחירת התוכן שיוצג בפניך עם חזרה ל־Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "בחירת התוכן שיוצג בפניך עם חזרה ל־Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "הלשונית האחרונה"; @@ -1286,25 +1286,25 @@ "Settings.Home.Option.Wallpaper" = "רקע"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "הרקע של Firefox, תבנית אמטיסט."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "הרקע של Ecosia, תבנית אמטיסט."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "הרקע של Firefox, תבנית גבעות חוף."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "הרקע של Ecosia, תבנית גבעות חוף."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "הרקע של Firefox, תבנית שמיים."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "הרקע של Ecosia, תבנית שמיים."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "הרקע החלק כברירת מחדל."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "הרקע של Firefox, תבנית זריחה."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "הרקע של Ecosia, תבנית זריחה."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "כפתור החלפת תמונת רקע"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "הרקע של Firefox, תבנית גבעות דמדומיות."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "הרקע של Ecosia, תבנית גבעות דמדומיות."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "‏%@ קלאסי"; @@ -1325,7 +1325,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "מהדורה מוגבלת"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "החלפת הרקע על־ידי נגיעה בלוגו דף הבית של Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "החלפת הרקע על־ידי נגיעה בלוגו דף הבית של Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "תמונת רקע"; @@ -1379,7 +1379,7 @@ "Settings.NewTab.Option.Custom" = "מותאם אישית"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "מסך הבית של Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "מסך הבית של Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "דף הבית"; @@ -1400,13 +1400,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "חסימת תמונות"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "בעת פתיחת Firefox"; +"Settings.OfferClipboardBar.Status" = "בעת פתיחת Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "בעת פתיחת %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "בעת פתיחת Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "בעת פתיחת Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "הצעה לפתיחת קישורים מועתקים"; @@ -1442,7 +1442,7 @@ "Settings.SendUsage.Link" = "מידע נוסף."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "הגישה של Mozilla היא לאסוף רק מה שנדרש לצורך שיפור Firefox לטובת הכלל."; +"Settings.SendUsage.Message" = "הגישה של Ecosia היא לאסוף רק מה שנדרש לצורך שיפור Ecosia לטובת הכלל."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "שליחת נתוני שימוש"; @@ -1463,7 +1463,7 @@ "Settings.Siri.OpenTabShortcut" = "פתיחת לשונית חדשה"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "ניתן להשתמש בקיצורי דרך של Siri כדי לפתוח במהירות את Firefox באמצעות Siri"; +"Settings.Siri.SectionDescription" = "ניתן להשתמש בקיצורי דרך של Siri כדי לפתוח במהירות את Ecosia באמצעות Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "קיצורי דרך של Siri"; @@ -1472,7 +1472,7 @@ "Settings.Studies.Toggle.Link" = "מידע נוסף."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "‏Firefox עשוי להתקין ולהריץ מחקרים מדי פעם בפעם."; +"Settings.Studies.Toggle.Message" = "‏Ecosia עשוי להתקין ולהריץ מחקרים מדי פעם בפעם."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "מחקרים"; @@ -1589,13 +1589,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "טעינה ברקע"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "בטעינה ב־Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "בטעינה ב־Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "פתיחה ב־Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "פתיחה ב־Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "חיפוש ב־Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "חיפוש ב־Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "העתקה"; @@ -1742,7 +1742,7 @@ "UIMenuItem.PasteGo" = "הדבקה ומעבר"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "חיפוש עם Firefox"; +"UIMenuItem.SearchWithFirefox" = "חיפוש עם Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "לא נקרא"; @@ -1790,7 +1790,7 @@ "Yesterday" = "אתמול"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "אין לך לשוניות פתוחות ב־Firefox על שאר המכשירים שלך."; +"You don’t have any tabs open in Firefox on your other devices." = "אין לך לשוניות פתוחות ב־Ecosia על שאר המכשירים שלך."; /* Your Rights settings section title */ "Your Rights" = "הזכויות שלך"; diff --git a/firefox-ios/Shared/he.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/he.lproj/PrivateBrowsing.strings index 48538e3b04c08..e7e7b8c271991 100644 --- a/firefox-ios/Shared/he.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/he.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "פתיחה בלשונית פרטית חדשה"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "‏Firefox לא ישמור את ההיסטוריה או העוגיות שלך, אבל סימניות חדשות יישמרו."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "‏Ecosia לא ישמור את ההיסטוריה או העוגיות שלך, אבל סימניות חדשות יישמרו."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "מידע נוסף"; diff --git a/firefox-ios/Shared/he.lproj/Today.strings b/firefox-ios/Shared/he.lproj/Today.strings index 4369eb066a212..7edbad32d4273 100644 --- a/firefox-ios/Shared/he.lproj/Today.strings +++ b/firefox-ios/Shared/he.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "סגירת לשוניות פרטיות"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "הוספת קיצורי דרך של Firefox למסך הבית שלך."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "הוספת קיצורי דרך של Ecosia למסך הבית שלך."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "מעבר לקישור שהועתק"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "אין לשוניות פתוחות."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "פתיחת Firefox"; +"TodayWidget.OpenFirefoxLabel" = "פתיחת Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "חיפוש פרטי"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "ניתן להוסיף קיצור דרך ל־Firefox למסך הבית שלך. לאחר הוספת הוידג’ט, יש ללחוץ ולהחזיק כדי לערוך אותו ולבחור בקיצור דרך שונה."; +"TodayWidget.QuickActionGalleryDescription" = "ניתן להוסיף קיצור דרך ל־Ecosia למסך הבית שלך. לאחר הוספת הוידג’ט, יש ללחוץ ולהחזיק כדי לערוך אותו ולבחור בקיצור דרך שונה."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "פעולות מהירות"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "קיצורי דרך של Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "קיצורי דרך של Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "הוספת קיצורי דרך ללשוניות הפתוחות שלך."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "תצוגה מהירה"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "חיפוש ב־Firefox"; +"TodayWidget.SearchInFirefoxV2" = "חיפוש ב־Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "חיפוש בלשונית פרטית"; diff --git a/firefox-ios/Shared/hi-IN.lproj/Default Browser.strings b/firefox-ios/Shared/hi-IN.lproj/Default Browser.strings index 8e49fe9c5b28b..f635a7824b691 100644 --- a/firefox-ios/Shared/hi-IN.lproj/Default Browser.strings +++ b/firefox-ios/Shared/hi-IN.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "सीखें कैसे"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Firefox में स्वचालित रूप से वेबसाइट, ईमेल और संदेशों को खोलने के लिए लिंक सेट करें।"; +"DefaultBrowserCard.Description" = "Ecosia में स्वचालित रूप से वेबसाइट, ईमेल और संदेशों को खोलने के लिए लिंक सेट करें।"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "अपना तयशुदा ब्राउज़र बदलें"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. डिफ़ॉल्ट ब्राउज़र के रूप में टैप करें"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox चुनें"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia चुनें"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "तयशुदा ब्राउज़र ऐप"; diff --git a/firefox-ios/Shared/hi-IN.lproj/Intro.strings b/firefox-ios/Shared/hi-IN.lproj/Intro.strings index edeea4d462620..1d7b3849c170b 100644 --- a/firefox-ios/Shared/hi-IN.lproj/Intro.strings +++ b/firefox-ios/Shared/hi-IN.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "साइन इन करें"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox में आपका स्वागत है"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia में आपका स्वागत है"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ब्राउजिंग शुरू करें"; diff --git a/firefox-ios/Shared/hi-IN.lproj/Localizable.strings b/firefox-ios/Shared/hi-IN.lproj/Localizable.strings index 94fbd5bec1c70..ab94608e6fbe6 100644 --- a/firefox-ios/Shared/hi-IN.lproj/Localizable.strings +++ b/firefox-ios/Shared/hi-IN.lproj/Localizable.strings @@ -281,7 +281,7 @@ "ErrorPages.AdvancedWarning2.Text" = "यह समाकृति में एक चूक या एक आक्रमणकर्ता द्वारा छेड़छाड़ हो सकती है। यदि आप संभावित जोखिम स्वीकार करते हैं तो आगे बढ़ें।"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ के उत्तराधिकारी ने वेबसाइट को गलत तरीके से कॉन्फ़िगर किया है। अपनी जानकारी को चोरी होने से सुरक्षित रखने के लिए, Firefox इस वेबसाइट से नहीं जुड़ा।"; +"ErrorPages.CertWarning.Description" = "%@ के उत्तराधिकारी ने वेबसाइट को गलत तरीके से कॉन्फ़िगर किया है। अपनी जानकारी को चोरी होने से सुरक्षित रखने के लिए, Ecosia इस वेबसाइट से नहीं जुड़ा।"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "यह संपर्क अविश्वसनीय है"; @@ -302,7 +302,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox मुख्य पृष्ठ"; +"Firefox.HomePage.Title" = "Ecosia मुख्य पृष्ठ"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "मुखपृष्ठ मनपसंद बनाएँ"; @@ -311,7 +311,7 @@ "Forward" = "आगे बढ़ाएं"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "खाता एवं उपकरण प्रबंधित करें"; @@ -323,7 +323,7 @@ "fxa.signin.camera-signin" = "अपने कैमरे से साइन इन करें"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "अपने कंप्यूटर पर Firefox खोलें और firefox.com/pair पर जाएं"; +"fxa.signin.qr-link-instruction" = "अपने कंप्यूटर पर Ecosia खोलें और ecosia.org/pair पर जाएं"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "स्कैन के लिए तैयार"; @@ -335,7 +335,7 @@ "FxA.SyncNow" = "अभी सिंक करें"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox सिंक %@ में जोड़ा गया"; +"FxAPush_DeviceConnected_body" = "Ecosia सिंक %@ में जोड़ा गया"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "सिंक जुड़ गया"; @@ -344,7 +344,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ सफलतापूर्वक डिसकनेक्ट किया गया।"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "यह उपकरण Firefox सिंक से सफलतापूर्वक डिसकनेक्ट हो गया।"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "यह उपकरण Ecosia सिंक से सफलतापूर्वक डिसकनेक्ट हो गया।"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "सिंक डिस्कनेक्ट किया गया"; @@ -353,7 +353,7 @@ "FxAPush_DeviceDisconnected_title" = "सिंक डिसकनेक्ट हुआ"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "एक उपकरण Firefox सिंक से डिस्कनेक्ट कर दिया है"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "एक उपकरण Ecosia सिंक से डिस्कनेक्ट कर दिया है"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "मदद"; @@ -386,7 +386,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "टैब की सूची देखने के लिए अपने दूसरे उपकरणों से साइन इन करें।"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox सिंक"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia सिंक"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "इतिहास"; @@ -545,7 +545,7 @@ "Logins.WelcomeView.Tagline" = "हर जगह अपना पासवर्ड लेकर चलें"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill Firefox पासवर्ड"; +"Logins.WelcomeView.Title2" = "AutoFill Ecosia पासवर्ड"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "AutoFill चालू करें"; @@ -590,7 +590,7 @@ "LoginsList.Title" = "लॉग इन सहेजा गया"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ऐसा लगता है जैसे कि Firefox पूर्व में नष्ट हो गया। क्या आप अपने टैब को पूर्ववत करना चाहेंगे?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ऐसा लगता है जैसे कि Ecosia पूर्व में नष्ट हो गया। क्या आप अपने टैब को पूर्ववत करना चाहेंगे?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "पठित चिह्नित करें"; @@ -722,7 +722,7 @@ "Onboarding.WhatsNew.Button.Title" = "ब्राउजिंग शुरू करें"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "उफ़! Firefox नष्ट हो गया"; +"Oops! Firefox crashed" = "उफ़! Ecosia नष्ट हो गया"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "पुस्तक प्रतीक को दबाकर आलेखों को पाठक दृश्य में खोलें जब यह शीर्षक पट्टी में दिखाई देता है।"; @@ -734,7 +734,7 @@ "Open Tabs" = "टैब खोलें"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox पृष्ठ को नही खोल सकता हैं क्योंकि इसमें एक अमान्य पता है।"; +"OpenURL.Error.Message" = "Ecosia पृष्ठ को नही खोल सकता हैं क्योंकि इसमें एक अमान्य पता है।"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "पृष्ठ नहीं खोला जा सकता है"; @@ -746,7 +746,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "यह आपको अपने कैमरा रोल करने में चित्रों को सहेजने की अनुमति देता है।"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox आपके फ़ोटो को एक्सेस करना चाहेंगे"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia आपके फ़ोटो को एक्सेस करना चाहेंगे"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "बंद करें"; @@ -872,7 +872,7 @@ "Search.ThirdPartyEngines.OK" = "ठीक"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "खराबी की एक विवरण भेजें, ताकि Mozilla समस्या को ठीक कर सके?"; +"Send a crash report so Mozilla can fix the problem?" = "खराबी की एक विवरण भेजें, ताकि Ecosia समस्या को ठीक कर सके?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "प्रतिक्रिया भेजें"; @@ -899,13 +899,13 @@ "SendTo.NavBar.Title" = "टैब भेजें"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "आपके पास सिंक के लिए इस Firefox खाते से जुड़े कोई अन्य उपकरण उपलब्ध नहीं हैं।"; +"SendTo.NoDevicesFound.Message" = "आपके पास सिंक के लिए इस Ecosia खाते से जुड़े कोई अन्य उपकरण उपलब्ध नहीं हैं।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "कृपया Firefox खोलें, सेटिंग में जाएं और जारी रखने के लिए साइन इन करें।"; +"SendTo.NotSignedIn.Message" = "कृपया Ecosia खोलें, सेटिंग में जाएं और जारी रखने के लिए साइन इन करें।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "आप अपने Firefox खाते में साइन इन नहीं हैं।"; +"SendTo.NotSignedIn.Title" = "आप अपने Ecosia खाते में साइन इन नहीं हैं।"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "भेजें"; @@ -926,7 +926,7 @@ "SentTab.NoTabArrivingNotification.body" = "शुरू करने के लिए टैप करें"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox सिंक"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia सिंक"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "देखें"; @@ -986,7 +986,7 @@ "Settings.DataManagement.Title" = "डेटा प्रबंधन"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox आपके खाते के साथ सिंक करना बंद कर देगा, लेकिन इस उपकरण पर स्थित आपका कोई भी ब्राउजिंग डाटा नहीं मिटाएगा।"; +"Settings.Disconnect.Body" = "Ecosia आपके खाते के साथ सिंक करना बंद कर देगा, लेकिन इस उपकरण पर स्थित आपका कोई भी ब्राउजिंग डाटा नहीं मिटाएगा।"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "डिस्कनेक्ट करें"; @@ -1049,7 +1049,7 @@ "Settings.FxA.Sync.SectionName" = "सिंक सेटिंग"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox खाता"; +"Settings.FxA.Title" = "Ecosia खाता"; /* General settings section title */ "Settings.General.SectionName" = "सामान्य"; @@ -1100,7 +1100,7 @@ "Settings.NewTab.Option.BlankPage" = "खाली पृष्ठ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox मुख्य पृष्ठ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia मुख्य पृष्ठ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "मुख्य पृष्ठ"; @@ -1118,10 +1118,10 @@ "Settings.NewTab.TopSectionNameFooter" = "चयन करें कि नया टैब खोलने पर आप क्या देखेंगे"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox खुलते समय"; +"Settings.OfferClipboardBar.Status" = "Ecosia खुलते समय"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox खुलते समय"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia खुलते समय"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "कॉपी किए लिंक को खोलने का प्रस्ताव दें"; @@ -1145,7 +1145,7 @@ "Settings.SendUsage.Link" = "अधिक जानें।"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla मात्र उसे एकत्रित करने का प्रयास करता है जिसकी हमें आवश्यकता होती है तथा Firefox को सभी के लिए बेहतर बनता है।"; +"Settings.SendUsage.Message" = "Ecosia मात्र उसे एकत्रित करने का प्रयास करता है जिसकी हमें आवश्यकता होती है तथा Ecosia को सभी के लिए बेहतर बनता है।"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "उपयोगित डेटा भेजें"; @@ -1160,7 +1160,7 @@ "Settings.Siri.OpenTabShortcut" = "नया टैब खोलें"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri के द्वारा तुरंत Firefox खोलने के लिए Siri शॉर्टकट का उपयोग करें"; +"Settings.Siri.SectionDescription" = "Siri के द्वारा तुरंत Ecosia खोलने के लिए Siri शॉर्टकट का उपयोग करें"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri शॉर्टकट"; @@ -1244,13 +1244,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "बैकग्राउंड में लोड करें"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox में लोड हो रहा है"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia में लोड हो रहा है"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox में खोलें"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia में खोलें"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox में खोजें"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia में खोजें"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "खोज सुझाव दिखाएं"; @@ -1376,7 +1376,7 @@ "UIMenuItem.PasteGo" = "पेस्ट करें और आगे बढ़ें"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox के साथ खोजें"; +"UIMenuItem.SearchWithFirefox" = "Ecosia के साथ खोजें"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "अपठित"; @@ -1406,7 +1406,7 @@ "Yesterday" = "कल"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "आपके अन्य उपकरणों पर Firefox में कोई टैब खुला नहीं है।"; +"You don’t have any tabs open in Firefox on your other devices." = "आपके अन्य उपकरणों पर Ecosia में कोई टैब खुला नहीं है।"; /* Your Rights settings section title */ "Your Rights" = "आपके अधिकार"; diff --git a/firefox-ios/Shared/hi-IN.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/hi-IN.lproj/PrivateBrowsing.strings index 8e98383dc0b54..2fd04332e2003 100644 --- a/firefox-ios/Shared/hi-IN.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/hi-IN.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "नए निजी टैब में खोलें"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox आपके इतिहास और कुकीज़ कुछ भी याद नही रखेगा, लेकिन नए बुकमार्क सहेजे जाएंगे।"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia आपके इतिहास और कुकीज़ कुछ भी याद नही रखेगा, लेकिन नए बुकमार्क सहेजे जाएंगे।"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "अधिक जानें"; diff --git a/firefox-ios/Shared/hi-IN.lproj/Today.strings b/firefox-ios/Shared/hi-IN.lproj/Today.strings index fcf943829dccf..2677982c4458e 100644 --- a/firefox-ios/Shared/hi-IN.lproj/Today.strings +++ b/firefox-ios/Shared/hi-IN.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "निजी टैब बंद करें"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "अपने होम स्क्रीन पर Firefox शॉर्टकट जोड़ें।"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "अपने होम स्क्रीन पर Ecosia शॉर्टकट जोड़ें।"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "कॉपी की गई लिंक पर जाएं"; @@ -23,7 +23,7 @@ "TodayWidget.NoOpenTabsLabel" = "कोई खुला टैब नहीं।"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox खोलें"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia खोलें"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "निजी खोज"; @@ -32,13 +32,13 @@ "TodayWidget.QuickActionsGalleryTitle" = "तुरंत कारवाई"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox शॉर्टकट"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia शॉर्टकट"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "अपने खुले टैब में शॉर्टकट जोड़ें।"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox में खोजें"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia में खोजें"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "निजी टैब में खोजें"; diff --git a/firefox-ios/Shared/hr.lproj/Default Browser.strings b/firefox-ios/Shared/hr.lproj/Default Browser.strings index fafb5945457fa..0b8dc11c7d1e4 100644 --- a/firefox-ios/Shared/hr.lproj/Default Browser.strings +++ b/firefox-ios/Shared/hr.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Postavljanje Firefoxa kao standardni preglednik je glas za otvoren i pristupačan internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Postavljanje Ecosiaa kao standardni preglednik je glas za otvoren i pristupačan internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standardni web preglednik za bolji internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Saznaj kako"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Postavi automatsko otvaranje poveznica web lokacija, e-mailova i poruka u Firefoxu."; +"DefaultBrowserCard.Description" = "Postavi automatsko otvaranje poveznica web lokacija, e-mailova i poruka u Ecosiau."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Odaberi Firefox kao standardni preglednik za automatsku brzinu, sigurnost i privatnost."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Odaberi Ecosia kao standardni preglednik za automatsku brzinu, sigurnost i privatnost."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Poboljšaj svakodnevno pregledavanje"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox u prosjeku blokira više od 3000 programa za praćenje po korisniku svaki mjesec. Postavi nas kao standardni preglednik za bezbrižnu privatnost."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia u prosjeku blokira više od 3000 programa za praćenje po korisniku svaki mjesec. Postavi nas kao standardni preglednik za bezbrižnu privatnost."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox pokriva privatnost"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia pokriva privatnost"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Promijeni standardni preglednik"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Dodirni standardni preglednik"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Odaberi Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Odaberi Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standardni preglednik"; diff --git a/firefox-ios/Shared/hr.lproj/Intro.strings b/firefox-ios/Shared/hr.lproj/Intro.strings index 0644347765894..dea5f7414c06b 100644 --- a/firefox-ios/Shared/hr.lproj/Intro.strings +++ b/firefox-ios/Shared/hr.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Prijavi se"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Dobro došli u Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Dobro došli u Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Započni s pregledavanjem"; diff --git a/firefox-ios/Shared/hr.lproj/Localizable.strings b/firefox-ios/Shared/hr.lproj/Localizable.strings index da37f76217183..fab422f1be807 100644 --- a/firefox-ios/Shared/hr.lproj/Localizable.strings +++ b/firefox-ios/Shared/hr.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "To može biti pogrešna konfiguracija ili napad na nju. Nastavi, ako prihvaćaš potencijalni rizik."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Vlasnik %@ je pogrešno podesio svoju stranicu. Kako bismo zaštitili tvoje informacije od krađe, Firefox se nije povezao na ovu stranicu."; +"ErrorPages.CertWarning.Description" = "Vlasnik %@ je pogrešno podesio svoju stranicu. Kako bismo zaštitili tvoje informacije od krađe, Ecosia se nije povezao na ovu stranicu."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ova veza nije sigurna"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox početna stranica"; +"Firefox.HomePage.Title" = "Ecosia početna stranica"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Prilagodi početnu"; @@ -377,7 +377,7 @@ "Forward" = "Naprijed"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox račun"; +"FxA.FirefoxAccount" = "Ecosia račun"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Upravljanje računom"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Prijavi se pomoću kamere"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Na računalu otvori Firefox i idi na firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Na računalu otvori Ecosia i idi na ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Spremno za snimanje"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sinkroniziraj sada"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox sinkronizacija se povezala s %@"; +"FxAPush_DeviceConnected_body" = "Ecosia sinkronizacija se povezala s %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync je povezan"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ je uspješno odspojen."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ovaj je uređaj uspješno odspojen od Firefox sinkronizacije."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ovaj je uređaj uspješno odspojen od Ecosia sinkronizacije."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync je odspojen"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync je odspojen"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Jedan uređaj se odspojio od Firefox sinkronizacije"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Jedan uređaj se odspojio od Ecosia sinkronizacije"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logo, promijeni sliku pozadine."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logo, promijeni sliku pozadine."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomoć"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Prijavi se za prikaz kartica s tvojih ostalih uređaja."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Povijest"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Saznaj više"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Za korištenje funkcije automatskog ispunjavanja za Firefox lozinka uređaja mora biti aktivirana."; +"Logins.PasscodeRequirement.Warning" = "Za korištenje funkcije automatskog ispunjavanja za Ecosia lozinka uređaja mora biti aktivirana."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ponesi svoje lozinke sa sobom"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Automatski ispuni Firefoxove lozinke"; +"Logins.WelcomeView.Title2" = "Automatski ispuni Ecosiaove lozinke"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Uključi auto. ispunjavanje"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SPREMLJENE PRIJAVE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Čini se da se Firefox prethodno srušio. Želiš li obnoviti kartice?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Čini se da se Ecosia prethodno srušio. Želiš li obnoviti kartice?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Označi kao pročitano"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Započni pregledavanje"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! Firefox se srušio"; +"Oops! Firefox crashed" = "Ups! Ecosia se srušio"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Otvori članke u prikazu za čitanje dodirom na ikonu knjige, kad se ikona pojavi u naslovnoj traci."; @@ -905,7 +905,7 @@ "Open Tabs" = "Otvorene kartice"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ne može otvoriti stranicu, jer stranica nema ispravnu adresu."; +"OpenURL.Error.Message" = "Ecosia ne može otvoriti stranicu, jer stranica nema ispravnu adresu."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nije moguće otvoriti stranicu"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Ovo omogućuje spremanje slike u album snimaka."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox želi pristupiti tvojim fotografijama"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia želi pristupiti tvojim fotografijama"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zatvori"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Podaci nisu valjani"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Idi u „Postavke” > „Firefox”. Dozvoli Firefoxu pristup kameri."; +"ScanQRCode.PermissionError.Message.v100" = "Idi u „Postavke” > „Ecosia”. Dozvoli Ecosia pristup kameri."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Snimi QR kod"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Prebaci na karticu"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefoxovi prijedlozi"; +"Search.SuggestSectionTitle.v102" = "Ecosiaovi prijedlozi"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nova tražilica će se pojavit na traci za brzo pretraživanje."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "U redu"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Poslati izvještaj o padu, kako bi Mozilla mogao riješiti problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Poslati izvještaj o padu, kako bi Ecosia mogao riješiti problem?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Pošalji povratne informacije"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Pošalji karticu"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Za sinkronizaciju ne raspolažeš s nijednim drugim uređajem koji je povezan s ovim Firefox računom."; +"SendTo.NoDevicesFound.Message" = "Za sinkronizaciju ne raspolažeš s nijednim drugim uređajem koji je povezan s ovim Ecosia računom."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Otvori Firefox, idi na postavke i prijavi se."; +"SendTo.NotSignedIn.Message" = "Otvori Ecosia, idi na postavke i prijavi se."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nisi prijavljen/a na svom Firefox računu."; +"SendTo.NotSignedIn.Title" = "Nisi prijavljen/a na svom Ecosia računu."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Pošalji"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Dodirni za započinjanje"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Prikaz"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Upravljanje podatcima"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox će prestati sinkroniziranje s tvojim računom, ali neće izbrisati podatke o tvom pregledavanju na ovom uređaju."; +"Settings.Disconnect.Body" = "Ecosia će prestati sinkroniziranje s tvojim računom, ali neće izbrisati podatke o tvom pregledavanju na ovom uređaju."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Prekini vezu"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Postavke sinkronizacije"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox račun"; +"Settings.FxA.Title" = "Ecosia račun"; /* General settings section title */ "Settings.General.SectionName" = "Opće"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Odaberi što se prikazuje kao početna stranica."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Odaberi što je uključeno na početnoj Firefox stranici."; +"Settings.Home.Option.Description.v101" = "Odaberi što je uključeno na početnoj Ecosia stranici."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Vrati se natrag"; @@ -1274,13 +1274,13 @@ "Settings.Home.Option.StartAtHome.Always" = "Početna stranica"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Odaberi što ćeš vidjeti kada se vratiš u Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Odaberi što ćeš vidjeti kada se vratiš u Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Posljednja kartica"; /* Title for the section in the settings menu where users can configure the behaviour of the Start at Home feature on the Firefox Homepage. */ -"Settings.Home.Option.StartAtHome.Title" = "Zaslon kod otvaranja aplikacije"; +"Settings.Home.Option.StartAtHome.Title" = "Uvodni ekran"; /* In the settings menu, this is the title of the Firefox Homepage customization settings section */ "Settings.Home.Option.Title.v101" = "Uključi u početnu stranicu"; @@ -1289,31 +1289,31 @@ "Settings.Home.Option.Wallpaper" = "Pozadinska slika"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox pozadina, uzorak ametista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia pozadina, uzorak ametista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox pozadina, uzorak brežuljaka na plaži."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia pozadina, uzorak brežuljaka na plaži."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox pozadina, nebesko plavi uzorak."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia pozadina, nebesko plavi uzorak."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standardna pozadina."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox pozadina, uzorak izlaska sunca."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia pozadina, uzorak izlaska sunca."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Prebacivanje pozadine početne stranice"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox pozadina, uzorak brda u sumraku."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia pozadina, uzorak brda u sumraku."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasični %@"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the section that allows users to change the wallpaper settings for the application. */ -"Settings.Home.Option.Wallpaper.CollectionTitle" = "POČETNI EKRAN"; +"Settings.Home.Option.Wallpaper.CollectionTitle" = "UVODNI EKRAN"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the button title of the group of wallpapers that are seasonally available to the user. */ "Settings.Home.Option.Wallpaper.LearnMore.v106" = "Saznaj više"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Ograničeno izdanje"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Promijeni sliku pozadine dodirom na logotip početne stranice Firefoxa"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Promijeni sliku pozadine dodirom na logotip početne stranice Ecosiaa"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Slika pozadine"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Prilagođeno"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox početna stranica"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia početna stranica"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Početna stranica"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokiraj slike"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Prilikom otvaranja Firefoxa"; +"Settings.OfferClipboardBar.Status" = "Prilikom otvaranja Ecosiaa"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Prilikom otvaranja %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Prilikom otvaranja Firefoxa"; +"Settings.OfferClipboardBar.StatusV2" = "Prilikom otvaranja Ecosiaa"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ponudi otvoriti kopirane poveznice"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Saznaj više."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla nastoji sakupljati samo onoliko podataka koliko nam treba, kako bismo za sve korisnike osigurali i poboljšali Firefox."; +"Settings.SendUsage.Message" = "Ecosia nastoji sakupljati samo onoliko podataka koliko nam treba, kako bismo za sve korisnike osigurali i poboljšali Ecosia."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Šalji podatke o korištenju"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Otvori novu karticu"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Koristi Siri prečace za brzo otvaranje Firefoxa putem Sirija"; +"Settings.Siri.SectionDescription" = "Koristi Siri prečace za brzo otvaranje Ecosiaa putem Sirija"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri prečaci"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Saznaj više."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox može s vremena na vrijeme instalirati i pokretati studije."; +"Settings.Studies.Toggle.Message" = "Ecosia može s vremena na vrijeme instalirati i pokretati studije."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studije"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Premješteno u pozadini"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Učitavanje u Firefoxu"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Učitavanje u Ecosiau"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Otvori u Firefoxu"; +"ShareExtension.OpenInFirefoxAction.Title" = "Otvori u Ecosiau"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Traži u Firefoxu"; +"ShareExtension.SeachInFirefoxAction.Title" = "Traži u Ecosiau"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiraj"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Zalijepi i kreni"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Traži s Firefoxom"; +"UIMenuItem.SearchWithFirefox" = "Traži s Ecosiaom"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "nepročitano"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Jučer"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Na tvojim drugim uređajima nemaš otvorenih kartica u Firefoxu."; +"You don’t have any tabs open in Firefox on your other devices." = "Na tvojim drugim uređajima nemaš otvorenih kartica u Ecosiau."; /* Your Rights settings section title */ "Your Rights" = "Vaša prava"; diff --git a/firefox-ios/Shared/hr.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/hr.lproj/PrivateBrowsing.strings index 71c2041addfa6..47dd9a0af6957 100644 --- a/firefox-ios/Shared/hr.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/hr.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Otvori u novoj privatnoj kartici"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox neće pamtiti vašu povijest ili kolačiće, ali nove zabilješke će biti sačuvane."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia neće pamtiti vašu povijest ili kolačiće, ali nove zabilješke će biti sačuvane."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saznaj više"; diff --git a/firefox-ios/Shared/hr.lproj/Today.strings b/firefox-ios/Shared/hr.lproj/Today.strings index 528942c30b174..bf3b065f28292 100644 --- a/firefox-ios/Shared/hr.lproj/Today.strings +++ b/firefox-ios/Shared/hr.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zatvori privatne kartice"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodaj Firefoxove prečace na početni zaslon"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodaj Ecosiaove prečace na tvoj početni ekran."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Idi na kopiranu poveznicu"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nema otvorenih kartica."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Otvori Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Otvori Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privatna pretraga"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Dodaj prečac za Firefox na početni ekran. Nakon dodavanja widgeta dodirni i zadrži ga dodirnutim, kako bi se mogao urediti i odabrati jedan drugi prečac."; +"TodayWidget.QuickActionGalleryDescription" = "Dodaj prečac za Ecosia na početni ekran. Nakon dodavanja widgeta dodirni i zadrži ga dodirnutim, kako bi se mogao urediti i odabrati jedan drugi prečac."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Brze radnje"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Prečaci za Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Prečaci za Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Dodaj prečace svojim otvorenim karticama."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Brzi pogled"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Traži u Firefoxu"; +"TodayWidget.SearchInFirefoxV2" = "Traži u Ecosiau"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Traži u privatnoj kartici"; diff --git a/firefox-ios/Shared/hsb.lproj/Default Browser.strings b/firefox-ios/Shared/hsb.lproj/Default Browser.strings index 7bd7a46a0be45..b67ef20e02bfe 100644 --- a/firefox-ios/Shared/hsb.lproj/Default Browser.strings +++ b/firefox-ios/Shared/hsb.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Hdyž Firefox k swojemu standardnemu wobhladowakej činiće, je to hłós za wotewrjeny, přistupny internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Hdyž Ecosia k swojemu standardnemu wobhladowakej činiće, je to hłós za wotewrjeny, přistupny internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard za lěpši internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Zhońće, kak to dźe"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Nastajće wotkazy z websydłow, mejlkow a powěsćow, zo bychu so awtomatisce we Firefox wočinili."; +"DefaultBrowserCard.Description" = "Nastajće wotkazy z websydłow, mejlkow a powěsćow, zo bychu so awtomatisce we Ecosia wočinili."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Wubjerće Firefox jako swój standardny wobhladowak, zo byšće spěšnosć, wěstotu a priwatnosć awtomatisce dóstał."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Wubjerće Ecosia jako swój standardny wobhladowak, zo byšće spěšnosć, wěstotu a priwatnosć awtomatisce dóstał."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Wšědne přehladowanje – hišće lěpše"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox w přerězku wjace hač 3000 přesćěhowakow na wužiwarja kóždy měsac blokuje. Čińće jón k swojemu standardnemu wobhladowakej, zo byšće w pokoju přehladował."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia w přerězku wjace hač 3000 přesćěhowakow na wužiwarja kóždy měsac blokuje. Čińće jón k swojemu standardnemu wobhladowakej, zo byšće w pokoju přehladował."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox wašu priwatnosć škita"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia wašu priwatnosć škita"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Waš standardny wobhladowak změnić"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Standardneho nałoženja wobhladowaka so podótknyć"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox wubrać"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia wubrać"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standardne nałoženje wobhladowaka"; diff --git a/firefox-ios/Shared/hsb.lproj/Intro.strings b/firefox-ios/Shared/hsb.lproj/Intro.strings index 6275cefec849a..82505554d1bba 100644 --- a/firefox-ios/Shared/hsb.lproj/Intro.strings +++ b/firefox-ios/Shared/hsb.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Přizjewić"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Witajće k Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Witajće k Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Přehladować započeć"; diff --git a/firefox-ios/Shared/hsb.lproj/Localizable.strings b/firefox-ios/Shared/hsb.lproj/Localizable.strings index 6ef571d5a44d8..a975d56e9f3a2 100644 --- a/firefox-ios/Shared/hsb.lproj/Localizable.strings +++ b/firefox-ios/Shared/hsb.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "To je snano wopačna konfigracija abo manipulacija nadpadowarja. Čińće dale, jeli potencielne riziko akceptujeće."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Wobsedźer %@ je websydło wopak konfigurował. Zo by waše daty před padustwom škitał, Firefox njeje was z tutym websydłom zwjazał."; +"ErrorPages.CertWarning.Description" = "Wobsedźer %@ je websydło wopak konfigurował. Zo by waše daty před padustwom škitał, Ecosia njeje was z tutym websydłom zwjazał."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Tutón zwisk je dowěry njehódny"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Startowa strona Firefox"; +"Firefox.HomePage.Title" = "Startowa strona Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Startowu stronu přiměrić"; @@ -377,7 +377,7 @@ "Forward" = "Doprědka"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Konto Firefox"; +"FxA.FirefoxAccount" = "Konto Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Konto a graty rjadować"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Přizjewće so ze swojej kameru"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Wočińće Firefox na swojim ličaku a dźiće k firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Wočińće Ecosia na swojim ličaku a dźiće k ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Hotowy za skenowanje"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Nětko synchronizować"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync je z %@ zwjazany"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync je z %@ zwjazany"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync je zwjazany"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ je so wuspěšnje wotdźělił."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Tutón grat je so wuspěšnje wot Firefox Sync wotdźělił."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Tutón grat je so wuspěšnje wot Ecosia Sync wotdźělił."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync je wotdźěleny"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync je wotdźěleny"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Grat je so wot Firefox Sync wotdźělił"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Grat je so wot Ecosia Sync wotdźělił"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, změńće pozadkowy wobraz."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, změńće pozadkowy wobraz."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomoc"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Přizjewće so, zo byšće sej lisćinu rajtarkow ze swojich druhich gratow wobhladał."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historija"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Dalše informacije"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Zo byšće funkciju awtomatiskeho wupjelnjenja Firefox wužiwał, dyrbiće gratowy hesłowy kod zmóžnjeny być."; +"Logins.PasscodeRequirement.Warning" = "Zo byšće funkciju awtomatiskeho wupjelnjenja Ecosia wužiwał, dyrbiće gratowy hesłowy kod zmóžnjeny być."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Wzmiće swoje hesła wšudźe sobu"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Hesła Firefox awtomatisce wupjelnić"; +"Logins.WelcomeView.Title2" = "Hesła Ecosia awtomatisce wupjelnić"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Awtomatiske wupjelnjenje zmóžnić"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SKŁADOWANE PŘIZJEWJENJA"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zda so, zo Firefox je do toho spadnył. Chceće swoje rajtarki wobnowić?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zda so, zo Ecosia je do toho spadnył. Chceće swoje rajtarki wobnowić?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Jako přečitany markěrować"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Přehladowanje startować"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hopla! Firefox je spadnył"; +"Oops! Firefox crashed" = "Hopla! Ecosia je spadnył"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Wočińće nastawki w čitanskim napohledźe, dótkajo so knižneho symbola, hdyž so w titulnej lajsće jewi."; @@ -905,7 +905,7 @@ "Open Tabs" = "Wotewrjene rajtarki"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox njemóže stronu wočinić, dokelž ma njepłaćiwu adresu."; +"OpenURL.Error.Message" = "Ecosia njemóže stronu wočinić, dokelž ma njepłaćiwu adresu."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Strona njeda so wočinić"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "To wam zmóžnja, wobraz do Camera Roll składować."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox by rady přistup k wašim fotam měł"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia by rady přistup k wašim fotam měł"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Začinić"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Daty su njepłaćiwe"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Dźiće ke gratej „Nastajenja“ > „Firefox“. Dowolće Firefox přistup ke kamerje."; +"ScanQRCode.PermissionError.Message.v100" = "Dźiće ke gratej „Nastajenja“ > „Ecosia“. Dowolće Ecosia přistup ke kamerje."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR-kod skenować"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "K rajtarkej přepinać"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Namjet Firefox"; +"Search.SuggestSectionTitle.v102" = "Namjet Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nowa pytawa so w polu spěšneho pytanja zjewi."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "W porjadku"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Chceće rozprawu wo spadźe słać, zo by Mozilla móhł problem rozrisać?"; +"Send a crash report so Mozilla can fix the problem?" = "Chceće rozprawu wo spadźe słać, zo by Ecosia móhł problem rozrisać?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Komentar pósłać"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Rajtark słać"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Njejsu žane druhe graty z tutym kontom Firefox zwjazane, kotrež bychu so dali synchronizować."; +"SendTo.NoDevicesFound.Message" = "Njejsu žane druhe graty z tutym kontom Ecosia zwjazane, kotrež bychu so dali synchronizować."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Prošu wočińće Firefox, dźiće k nastajenjam a přizjewće so, zo byšće pokročował."; +"SendTo.NotSignedIn.Message" = "Prošu wočińće Ecosia, dźiće k nastajenjam a přizjewće so, zo byšće pokročował."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Njejsće pola swojeho konta Firefox přizjewjeny."; +"SendTo.NotSignedIn.Title" = "Njejsće pola swojeho konta Ecosia přizjewjeny."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Pósłać"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Dótkńće so, zo byšće započał"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Pokazać"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Datowe rjadowanje"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox přestanje z wašim kontom synchronizować, ale njezhaša přehladowanske daty na tutym graće."; +"Settings.Disconnect.Body" = "Ecosia přestanje z wašim kontom synchronizować, ale njezhaša přehladowanske daty na tutym graće."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync dźělić"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Nastajenja synchronizacije"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Konto Firefox"; +"Settings.FxA.Title" = "Konto Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Powšitkowne"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Wubjerće, štož so ma jako startowa strona pokazać."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Wubjerće, kotry wobsah so ma na startowej stronje Firefox pokazać."; +"Settings.Home.Option.Description.v101" = "Wubjerće, kotry wobsah so ma na startowej stronje Ecosia pokazać."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Wróćo skočić"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startowa strona"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Wubjerće, štož chceće widźeć, hdyž so k Firefox wróćeće."; +"Settings.Home.Option.StartAtHome.Description" = "Wubjerće, štož chceće widźeć, hdyž so k Ecosia wróćeće."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Posledni rajtark"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Pozadkowy wobraz"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Pozadkowy wobraz Firefox, ametystowy muster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Pozadkowy wobraz Ecosia, ametystowy muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Pozadkowy wobraz Firefox, muster přibrjóžnych hórkow."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Pozadkowy wobraz Ecosia, muster přibrjóžnych hórkow."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Pozadkowy wobraz Firefox, njebjomódry muster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Pozadkowy wobraz Ecosia, njebjomódry muster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standardny transparentny pozadkowy wobraz."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Pozadkowy wobraz Firefox, muster schadźenja słónca."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Pozadkowy wobraz Ecosia, muster schadźenja słónca."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Přepinak za cyklus pozadkowych wobrazow startoweje strony"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Pozadkowy wobraz Firefox, muster z hórkami w směrkach."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Pozadkowy wobraz Ecosia, muster z hórkami w směrkach."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasiski %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limitowana edicija"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Podótkńće so loga startoweje strony Firefox, zo byšće pozadkowy wobraz změnił"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Podótkńće so loga startoweje strony Ecosia, zo byšće pozadkowy wobraz změnił"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Pozadkowy wobraz"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Swójski"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Startowa strona Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Startowa strona Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startowa strona"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Wobrazy blokować"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Hdyž so Firefox wočinja"; +"Settings.OfferClipboardBar.Status" = "Hdyž so Ecosia wočinja"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Při wočinjenju %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Hdyž so Firefox wočinja"; +"Settings.OfferClipboardBar.StatusV2" = "Hdyž so Ecosia wočinja"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Poskićić, kopěrowane wotkazy wočinić"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Dalše informacije."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla so prócuje, jenož informacije hromadźić, kotrež trjebamy, zo bychmy Firefox za kóždeho poskićili a polěpšili."; +"Settings.SendUsage.Message" = "Ecosia so prócuje, jenož informacije hromadźić, kotrež trjebamy, zo bychmy Ecosia za kóždeho poskićili a polěpšili."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Wužiwanske daty pósłać"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Nowy rajtark wočinić"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Skrótšenki Siri wužiwać, zo by so Firefox spěšnje přez Siri wočinił"; +"Settings.Siri.SectionDescription" = "Skrótšenki Siri wužiwać, zo by so Ecosia spěšnje přez Siri wočinił"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Skrótšenki Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Dalše informacije."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox móže hdys a hdys studije instalować a přewjesć."; +"Settings.Studies.Toggle.Message" = "Ecosia móže hdys a hdys studije instalować a přewjesć."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studije"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "W pozadku začitać"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Začita so w Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Začita so w Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "W Firefox wočinić"; +"ShareExtension.OpenInFirefoxAction.Title" = "W Ecosia wočinić"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "W Firefox pytać"; +"ShareExtension.SeachInFirefoxAction.Title" = "W Ecosia pytać"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopěrować"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Zasadźić a započeć"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Z Firefox pytać"; +"UIMenuItem.SearchWithFirefox" = "Z Ecosia pytać"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "nječitany"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Wčera"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Nimaće žane wočinjene rajtarki w Firefox na swojich druhich gratach."; +"You don’t have any tabs open in Firefox on your other devices." = "Nimaće žane wočinjene rajtarki w Ecosia na swojich druhich gratach."; /* Your Rights settings section title */ "Your Rights" = "Waše prawa"; diff --git a/firefox-ios/Shared/hsb.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/hsb.lproj/PrivateBrowsing.strings index c0a2908c0771d..53f1da538a603 100644 --- a/firefox-ios/Shared/hsb.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/hsb.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "W nowym priwatnym rajtarku wočinić"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox njebudźe sej wašu historiju abo placki spomjatkować, ale nowe zapołožki budu so składować."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia njebudźe sej wašu historiju abo placki spomjatkować, ale nowe zapołožki budu so składować."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Dalše informacije"; diff --git a/firefox-ios/Shared/hsb.lproj/Today.strings b/firefox-ios/Shared/hsb.lproj/Today.strings index a9b27285678cb..50c4532195887 100644 --- a/firefox-ios/Shared/hsb.lproj/Today.strings +++ b/firefox-ios/Shared/hsb.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Priwatne rajtarki začinić"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Přidajće swojej startowej wobrazowce zwjazanja Firefox"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Přidajće swojej startowej wobrazowce zwjazanja Ecosia"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ke kopěrowanemu wotkazej"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Žane wočinjene rajtarki"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox wočinić"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia wočinić"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Priwatne pytanje"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Přidajće startowej wobrazowce zwjazanje Firefox. Po tym zo sće asistent přidał, podótkńće so asistenta a dźeržće jón, zo byšće wobdźěłował a wubjerće druhe zwjazanje."; +"TodayWidget.QuickActionGalleryDescription" = "Přidajće startowej wobrazowce zwjazanje Ecosia. Po tym zo sće asistent přidał, podótkńće so asistenta a dźeržće jón, zo byšće wobdźěłował a wubjerće druhe zwjazanje."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Spěšne akcije"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Zwjazanja Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Zwjazanja Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Přidajće swojim wočinjenym rajtarkam zwjazanja."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Spěšny napohlad"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "W Firefox pytać"; +"TodayWidget.SearchInFirefoxV2" = "W Ecosia pytać"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "W priwatnym rajtarku pytać"; diff --git a/firefox-ios/Shared/hu.lproj/Default Browser.strings b/firefox-ios/Shared/hu.lproj/Default Browser.strings index 137917d871b6c..4f6c244736ac4 100644 --- a/firefox-ios/Shared/hu.lproj/Default Browser.strings +++ b/firefox-ios/Shared/hu.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "A Firefox alapértelmezett böngészővé tétele egy szavazat a nyílt és hozzáférhető internet mellett."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "A Ecosia alapértelmezett böngészővé tétele egy szavazat a nyílt és hozzáférhető internet mellett."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Jobb internet, alapértelmezetten"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Tudja meg hogyan"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Állítsa be a webhelyek, e-mailek és üzenetek hivatkozásait, hogy azok automatikusan a Firefoxban nyíljanak meg."; +"DefaultBrowserCard.Description" = "Állítsa be a webhelyek, e-mailek és üzenetek hivatkozásait, hogy azok automatikusan a Ecosiaban nyíljanak meg."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Válassza a Firefoxot alapértelmezett böngészőjeként, hogy automatikussá tegye a sebességet, a biztonságot és az adatvédelmet."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Válassza a Ecosiaot alapértelmezett böngészőjeként, hogy automatikussá tegye a sebességet, a biztonságot és az adatvédelmet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Emelje új szintre a mindennapi böngészést"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "A Firefox átlagosan több mint 3000 nyomkövetőt blokkol felhasználónként minden hónapban. Tegyen minket az alapértelmezett böngészővé, hogy nyugodt legyen az adatvédelme felől."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "A Ecosia átlagosan több mint 3000 nyomkövetőt blokkol felhasználónként minden hónapban. Tegyen minket az alapértelmezett böngészővé, hogy nyugodt legyen az adatvédelme felől."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Bízza a Firefoxra az adatvédelmet"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Bízza a Ecosiara az adatvédelmet"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Az alapértelmezett böngésző módosítása"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Koppintson az alapértelmezett böngészőalkalmazás elemre"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Válassza a Firefoxot"; +"DefaultBrowserOnboarding.Description3" = "3. Válassza a Ecosiaot"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Alapértelmezett böngésző"; diff --git a/firefox-ios/Shared/hu.lproj/Intro.strings b/firefox-ios/Shared/hu.lproj/Intro.strings index 4a69074731bc3..e4a1f1e2e9fb0 100644 --- a/firefox-ios/Shared/hu.lproj/Intro.strings +++ b/firefox-ios/Shared/hu.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Bejelentkezés"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Üdvözli a Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Üdvözli a Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Böngészés megkezdése"; diff --git a/firefox-ios/Shared/hu.lproj/Localizable.strings b/firefox-ios/Shared/hu.lproj/Localizable.strings index 7b0d6c3ec0e0a..6d4f22b6d7067 100644 --- a/firefox-ios/Shared/hu.lproj/Localizable.strings +++ b/firefox-ios/Shared/hu.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Lehet hibás beállítás vagy egy támadó műve. Akkor haladjon tovább, ha elfogadja a lehetséges kockázatot."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "A(z) %@ tulajdonosa rosszul állította be a webhelyet. Hogy megvédje az információit az ellopásuktól, a Firefox nem kapcsolódott ehhez a webhelyhez."; +"ErrorPages.CertWarning.Description" = "A(z) %@ tulajdonosa rosszul állította be a webhelyet. Hogy megvédje az információit az ellopásuktól, a Ecosia nem kapcsolódott ehhez a webhelyhez."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ez a kapcsolat nem megbízható"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox kezdőlap"; +"Firefox.HomePage.Title" = "Ecosia kezdőlap"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Kezdőoldal testreszabása"; @@ -377,7 +377,7 @@ "Forward" = "Előre"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-fiók"; +"FxA.FirefoxAccount" = "Ecosia-fiók"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Fiók és eszközök kezelése"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Jelentkezzen be a kamerájával"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "A számítógépen nyissa meg a Firefoxot, és ugorjon a firefox.com/pair oldalra"; +"fxa.signin.qr-link-instruction" = "A számítógépen nyissa meg a Ecosiaot, és ugorjon a ecosia.org/pair oldalra"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Leolvasásra kész"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Szinkronizálás most"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "A Firefox Sync kapcsolódott ehhez: %@"; +"FxAPush_DeviceConnected_body" = "A Ecosia Sync kapcsolódott ehhez: %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync kapcsolódva"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "A(z) %@ sikeresen lekapcsolódott."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ez az eszköz sikeresen lekapcsolódott a Firefox Syncről."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ez az eszköz sikeresen lekapcsolódott a Ecosia Syncről."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync leválasztva"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync leválasztva"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Egy eszköz lekapcsolódott a Firefox szinkronizálásról"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Egy eszköz lekapcsolódott a Ecosia szinkronizálásról"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logó, háttérkép megváltoztatása."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logó, háttérkép megváltoztatása."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Súgó"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Jelentkezzen be a más készülékeiről származó lapok listájának megjelenítéséhez."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Előzmények"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "További tudnivalók"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "A Firefox automatikus kitöltési funkciójának használatához engedélyeznie kell az eszköz jelkódját."; +"Logins.PasscodeRequirement.Warning" = "A Ecosia automatikus kitöltési funkciójának használatához engedélyeznie kell az eszköz jelkódját."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Vigye magával a jelszavait bárhová"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox jelszavak automatikus kitöltése"; +"Logins.WelcomeView.Title2" = "Ecosia jelszavak automatikus kitöltése"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Automatikus kitöltés bekapcsolása"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "MENTETT BEJELENTKEZÉSEK"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Úgy néz ki, hogy a Firefox előzőleg összeomlott. Szeretné helyreállítani a lapjait?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Úgy néz ki, hogy a Ecosia előzőleg összeomlott. Szeretné helyreállítani a lapjait?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Megjelölés olvasottként"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Böngészés megkezdése"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hoppá! A Firefox összeomlott"; +"Oops! Firefox crashed" = "Hoppá! A Ecosia összeomlott"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Nyissa meg a cikkeket olvasó nézetben a könyv ikonra kattintva a címsávban."; @@ -905,7 +905,7 @@ "Open Tabs" = "Nyitott lapok"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "A Firefox nem tudja megnyitni az oldalt, mert a címe érvénytelen."; +"OpenURL.Error.Message" = "A Ecosia nem tudja megnyitni az oldalt, mert a címe érvénytelen."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Az oldal nem nyitható meg"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Ez lehetővé teszi, hogy mentse a képet a Filmtekercsébe."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Firefox szeretne hozzáférni a fényképeihez"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "A Ecosia szeretne hozzáférni a fényképeihez"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Bezárás"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Az adatok érvénytelenek"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ugorjon az eszköz „Beállítások” > „Firefox” menüpontjához. Engedélyezze a Firefox számára a kamera elérését."; +"ScanQRCode.PermissionError.Message.v100" = "Ugorjon az eszköz „Beállítások” > „Ecosia” menüpontjához. Engedélyezze a Ecosia számára a kamera elérését."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR-kód leolvasása"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Váltás erre a lapra"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Javaslatok"; +"Search.SuggestSectionTitle.v102" = "Ecosia Javaslatok"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Az új keresési szolgáltatás a gyorskereső sávban fog megjelenni."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Rendben"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Összeomlás jelentése, hogy a Mozilla javíthassa a hibát?"; +"Send a crash report so Mozilla can fix the problem?" = "Összeomlás jelentése, hogy a Ecosia javíthassa a hibát?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Visszajelzés küldése"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Lap küldése"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Nincs más eszköze ezzel a Firefox fiókkal összekötve, amellyel szinkronizálhatna."; +"SendTo.NoDevicesFound.Message" = "Nincs más eszköze ezzel a Ecosia fiókkal összekötve, amellyel szinkronizálhatna."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Nyissa meg a Firefoxot, menjen a beállításokhoz, és jelentkezzen be a folytatáshoz."; +"SendTo.NotSignedIn.Message" = "Nyissa meg a Ecosiaot, menjen a beállításokhoz, és jelentkezzen be a folytatáshoz."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nincs bejelentkezve a Firefox fiókjába."; +"SendTo.NotSignedIn.Title" = "Nincs bejelentkezve a Ecosia fiókjába."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Küldés"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Érintse meg az indításhoz"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Nézet"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Adatkezelés"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "A Firefox leállítja a szinkronizációt a fiókjával, de nem töröl semmilyen böngészési adatot erről az eszközről."; +"Settings.Disconnect.Body" = "A Ecosia leállítja a szinkronizációt a fiókjával, de nem töröl semmilyen böngészési adatot erről az eszközről."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Leválás a Syncről"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync beállítások"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-fiók"; +"Settings.FxA.Title" = "Ecosia-fiók"; /* General settings section title */ "Settings.General.SectionName" = "Általános"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Válasszon, hogy mi jelenjen meg kezdőlapként."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Válassza ki, hogy mi szerepeljen a Firefox kezdőoldalon."; +"Settings.Home.Option.Description.v101" = "Válassza ki, hogy mi szerepeljen a Ecosia kezdőoldalon."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Ugrás vissza"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Kezdőlap"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Válassza ki, hogy mit lásson, amikor visszatér a Firefoxhoz."; +"Settings.Home.Option.StartAtHome.Description" = "Válassza ki, hogy mit lásson, amikor visszatér a Ecosiahoz."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Legutóbbi lap"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Háttérkép"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox háttérkép, ametiszt minta."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia háttérkép, ametiszt minta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox háttérkép, tengerparti dombok minta."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia háttérkép, tengerparti dombok minta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox háttérkép, égszínkék minta."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia háttérkép, égszínkék minta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Alapértelmezett háttérkép eltávolítása."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox háttérkép, napkelte minta."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia háttérkép, napkelte minta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Kezdőlap háttérképváltó kapcsolója"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox háttérkép, napnyugtai dombok minta."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia háttérkép, napnyugtai dombok minta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasszikus %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Korlátozott kiadás"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "A háttérkép módosításához koppintson a Firefox kezdőlap logójára"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "A háttérkép módosításához koppintson a Ecosia kezdőlap logójára"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Háttérkép"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Egyéni"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox kezdőlap"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia kezdőlap"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Kezdőlap"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Képek blokkolása"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "A Firefox megnyitásakor"; +"Settings.OfferClipboardBar.Status" = "A Ecosia megnyitásakor"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "A %@ megnyitásakor"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "A Firefox megnyitásakor"; +"Settings.OfferClipboardBar.StatusV2" = "A Ecosia megnyitásakor"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Másolt hivatkozások megnyitásának felajánlása"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "További tudnivalók"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "A Mozilla arra törekszik, hogy csak azt gyűjtse, ami a Firefox fejlesztéséhez és támogatásához szükséges."; +"Settings.SendUsage.Message" = "A Ecosia arra törekszik, hogy csak azt gyűjtse, ami a Ecosia fejlesztéséhez és támogatásához szükséges."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Használati adatok küldése"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Új lap megnyitása"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "A Siri gyors parancsok segítségével gyorsan megnyithatja a Firefoxot a Sirivel"; +"Settings.Siri.SectionDescription" = "A Siri gyors parancsok segítségével gyorsan megnyithatja a Ecosiaot a Sirivel"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri gyors parancsok"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "További tudnivalók."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "A Firefox időről időre tanulmányokat telepíthet és futtathat."; +"Settings.Studies.Toggle.Message" = "A Ecosia időről időre tanulmányokat telepíthet és futtathat."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Tanulmányok"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Betöltés a háttérben"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Betöltés a Firefoxban"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Betöltés a Ecosiaban"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Megnyitás a Firefoxban"; +"ShareExtension.OpenInFirefoxAction.Title" = "Megnyitás a Ecosiaban"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Keresés a Firefoxban"; +"ShareExtension.SeachInFirefoxAction.Title" = "Keresés a Ecosiaban"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Másolás"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Beillesztés és ugrás"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Keresés a Firefoxszal"; +"UIMenuItem.SearchWithFirefox" = "Keresés a Ecosiaszal"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "olvasatlan"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Tegnap"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Nincs egyetlen lap sem nyitva a Firefoxban a többi eszközén."; +"You don’t have any tabs open in Firefox on your other devices." = "Nincs egyetlen lap sem nyitva a Ecosiaban a többi eszközén."; /* Your Rights settings section title */ "Your Rights" = "Az Ön jogai"; diff --git a/firefox-ios/Shared/hu.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/hu.lproj/PrivateBrowsing.strings index b20b2590ab2f8..cf2db9af32bff 100644 --- a/firefox-ios/Shared/hu.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/hu.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Megnyitás új privát lapon"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "A Firefox nem fog emlékezni az előzményeire vagy a sütijeire, de az új könyvjelzők mentve lesznek."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "A Ecosia nem fog emlékezni az előzményeire vagy a sütijeire, de az új könyvjelzők mentve lesznek."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Tudjon meg többet"; diff --git a/firefox-ios/Shared/hu.lproj/Today.strings b/firefox-ios/Shared/hu.lproj/Today.strings index 3a6380777b374..194b0b7bb928b 100644 --- a/firefox-ios/Shared/hu.lproj/Today.strings +++ b/firefox-ios/Shared/hu.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Privát lapok bezárása"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefoxos parancsikonok hozzáadása a kezdőképernyőhöz."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosiaos parancsikonok hozzáadása a kezdőképernyőhöz."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ugrás a másolt hivatkozáshoz"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nincsenek nyitott lapok."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox megnyitása"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia megnyitása"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privát keresés"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Adjon egy Firefox ikont a kezdőképernyőhöz. A widget hozzáadása után érintse meg és tartsa lenyomva a szerkesztéshez, majd válasszon egy másik ikont."; +"TodayWidget.QuickActionGalleryDescription" = "Adjon egy Ecosia ikont a kezdőképernyőhöz. A widget hozzáadása után érintse meg és tartsa lenyomva a szerkesztéshez, majd válasszon egy másik ikont."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Gyors műveletek"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-hivatkozások"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-hivatkozások"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Parancsikonok hozzáadása a nyitott lapokhoz."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Gyors nézet"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Keresés a Firefoxban"; +"TodayWidget.SearchInFirefoxV2" = "Keresés a Ecosiaban"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Keresés privát lapon"; diff --git a/firefox-ios/Shared/hy-AM.lproj/Default Browser.strings b/firefox-ios/Shared/hy-AM.lproj/Default Browser.strings index e8f81994cf090..ca648f0bb3d23 100644 --- a/firefox-ios/Shared/hy-AM.lproj/Default Browser.strings +++ b/firefox-ios/Shared/hy-AM.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox-ը Ձեր սկզբնադիր դիտարկիչ դարձնելը քվեարկություն է բաց և հասանելի համացանցի համար:"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia-ը Ձեր սկզբնադիր դիտարկիչ դարձնելը քվեարկություն է բաց և հասանելի համացանցի համար:"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Բարի գալուստ ավելի լավ համացանց"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Իմանալ ինչպես"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Տեղադրեք հղումներ կայքերից, էլ. նամակներից և հաղորդագրություններից՝ Firefox-ում ավտոմատ կերպով բացելու համար:"; +"DefaultBrowserCard.Description" = "Տեղադրեք հղումներ կայքերից, էլ. նամակներից և հաղորդագրություններից՝ Ecosia-ում ավտոմատ կերպով բացելու համար:"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Ընտրեք Firefox-ը որպես ձեր լռելյայն դիտարկիչ՝ արագությունը, անվտանգությունը և գաղտնիությունը ավտոմատ դարձնելու համար:"; +"DefaultBrowserCard.NextLevel.Description.v108" = "Ընտրեք Ecosia-ը որպես ձեր լռելյայն դիտարկիչ՝ արագությունը, անվտանգությունը և գաղտնիությունը ավտոմատ դարձնելու համար:"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Բարձրացրեք ամենօրյա զննարկումը"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox-ն ամեն ամիս արգելափակում է 3000+ հետևիչներ մեկ օգտատիրոջ համար: Դարձրեք մեզ Ձեր սկզբնադիր դիտարկիչը՝ որպեսզի գաղտնիության համար հանգիստ լինեք:"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia-ն ամեն ամիս արգելափակում է 3000+ հետևիչներ մեկ օգտատիրոջ համար: Դարձրեք մեզ Ձեր սկզբնադիր դիտարկիչը՝ որպեսզի գաղտնիության համար հանգիստ լինեք:"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox֊ի գաղտնիության ծանուցում"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia֊ի գաղտնիության ծանուցում"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Փոխարկեք Ձեր սկզբնադիր դիտարկիչը"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Հպեք սկզբնադիր դիտարկի հավելվածին"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Ընտրեք Firefox-ը"; +"DefaultBrowserOnboarding.Description3" = "3. Ընտրեք Ecosia-ը"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Դիտարկիչի սկզբնադիր հավելված"; diff --git a/firefox-ios/Shared/hy-AM.lproj/Intro.strings b/firefox-ios/Shared/hy-AM.lproj/Intro.strings index 54d021f7c136c..a87ac3307e436 100644 --- a/firefox-ios/Shared/hy-AM.lproj/Intro.strings +++ b/firefox-ios/Shared/hy-AM.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Մուտք գործել"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Բարի գալուստ Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Բարի գալուստ Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Սկսել դիտարկումը"; diff --git a/firefox-ios/Shared/hy-AM.lproj/Localizable.strings b/firefox-ios/Shared/hy-AM.lproj/Localizable.strings index 3cf0654720cd3..e677445b476a8 100644 --- a/firefox-ios/Shared/hy-AM.lproj/Localizable.strings +++ b/firefox-ios/Shared/hy-AM.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Դա կարող է լինել հարցակվողի կողմից սխալ կազմաձևում կամ խախտում։ Առաջ անցեք, եթե դուք ընդունում եք հնարավոր վտանգը։"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@-ի սեփականատերը սխալ է տեղադրել իր կայքը։ Ձեր տեղեկատվությունը գողությունից պաշտպանելու համար Firefox֊ը չի միացել այս վեբ կայքին։"; +"ErrorPages.CertWarning.Description" = "%@-ի սեփականատերը սխալ է տեղադրել իր կայքը։ Ձեր տեղեկատվությունը գողությունից պաշտպանելու համար Ecosia֊ը չի միացել այս վեբ կայքին։"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Այս կապակցումն անվստահելի է"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox-ի Տնային էջը"; +"Firefox.HomePage.Title" = "Ecosia-ի Տնային էջը"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Հարմարեցնել տնէջը"; @@ -377,7 +377,7 @@ "Forward" = "Փոխանցել"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox հաշիվ"; +"FxA.FirefoxAccount" = "Ecosia հաշիվ"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Կառավարել հաշիվը"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Մուտք գործեք Ձեր տեսախցիկով"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Ձեր համակարգչի վրա բացեք Firefox-ը և անցեք firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Ձեր համակարգչի վրա բացեք Ecosia-ը և անցեք ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Պատրաստել սկանավորման"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Համաժամեցնել"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox֊ի համաժամեցումը միացվել է %@-ին"; +"FxAPush_DeviceConnected_body" = "Ecosia֊ի համաժամեցումը միացվել է %@-ին"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "ՍԻնքը ավարտվեց"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@-ը հաջողությամբ կապախզվել է:"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ԱՅս սարքը հաջողությամբ կապախզվել է Firefox Սինքից:"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ԱՅս սարքը հաջողությամբ կապախզվել է Ecosia Սինքից:"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Համաժամեցումը կապախզված է"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Համաժամեցումը կապախզված է"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Սարքը կապախզվել է Firefox Սինքից"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Սարքը կապախզվել է Ecosia Սինքից"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox լոգո, փոխել պաստառը"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia լոգո, փոխել պաստառը"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Օգնություն"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Մուտք գործե՛ք՝ ձեր այլ սարքերից ներդիրների ցուցակը տեսնելու համար։"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Սինք"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Սինք"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Պատմություն"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Իմանալ ավելին"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox-ի ինքնալրացման գործառույթն օգտագործելու համար դուք պետք է միացված ունենաք սարքի անցակոդը:"; +"Logins.PasscodeRequirement.Warning" = "Ecosia-ի ինքնալրացման գործառույթն օգտագործելու համար դուք պետք է միացված ունենաք սարքի անցակոդը:"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Վերցրեք ձեր գաղտնաբառերը ամենուր"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox-ի գաղտնաբառերի ինքնալրացում"; +"Logins.WelcomeView.Title2" = "Ecosia-ի գաղտնաբառերի ինքնալրացում"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Միացրեք Ինքնալրացումը"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ՊԱՀՊԱՆՎԱԾ ՄՈՒՏՔԱՆՈՒՆՆԵՐ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Կարծես Firefox-ը վթարվել է: Վերականգնե՞լ ներդիրները:"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Կարծես Ecosia-ը վթարվել է: Վերականգնե՞լ ներդիրները:"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Նշել որպես ընթերցված"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Սկսել դիտարկումը"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Վա՜յ, Firefox-ը վթարվեց:"; +"Oops! Firefox crashed" = "Վա՜յ, Ecosia-ը վթարվեց:"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Բացեք հոդվածները Reader View֊ում՝ կտտացնելով գրքի պատկերակը, երբ այն հայտնվում է վերնագրի սանդղակում։"; @@ -905,7 +905,7 @@ "Open Tabs" = "Բացել ներդիրները"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox-ը չի կարող բացել էջը, որովհետև այս ունի անվավեր հասցե։"; +"OpenURL.Error.Message" = "Ecosia-ը չի կարող բացել էջը, որովհետև այս ունի անվավեր հասցե։"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Հնարավոր չէ բացել էջը"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Սա ձեզ թույլատրում է պահպանել պատկերը ձեր Խցիկի Շարժում։"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox-ը ցանկանում է մատչել ձեր լուսանկարները"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia-ը ցանկանում է մատչել ձեր լուսանկարները"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Փակել"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Տվյալը անվավեր է"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Գնացեք սարքի «Կարգավորումներ» > «Firefox»: Թույլ տվեք Firefox-ին մատչել տեսախցիկին:"; +"ScanQRCode.PermissionError.Message.v100" = "Գնացեք սարքի «Կարգավորումներ» > «Ecosia»: Թույլ տվեք Ecosia-ին մատչել տեսախցիկին:"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Սկանավորել QR կոդը"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Անցնել ներդիրին"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-ի առաջարկ"; +"Search.SuggestSectionTitle.v102" = "Ecosia-ի առաջարկ"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Նոր որոնիչը կհայտնվի արագ որոնման գոտիում:"; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Լավ"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Ուղարկե՞լ վթարի զեկույց Mozilla-ին, որպեսզի այն շտկվի:"; +"Send a crash report so Mozilla can fix the problem?" = "Ուղարկե՞լ վթարի զեկույց Ecosia-ին, որպեսզի այն շտկվի:"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Ուղարկել արձագանք"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Ուղարկել ներդիրը"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Դուք չունեք մի սարք, որը կապված է Firefox֊ի այս հաշվին և առկա է համաժամեցման համար։"; +"SendTo.NoDevicesFound.Message" = "Դուք չունեք մի սարք, որը կապված է Ecosia֊ի այս հաշվին և առկա է համաժամեցման համար։"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Խնդրում ենք բացել Firefox֊ը, անցեք ընտրանքներ և գրանցվեք ՝ շարունակելու համար։"; +"SendTo.NotSignedIn.Message" = "Խնդրում ենք բացել Ecosia֊ը, անցեք ընտրանքներ և գրանցվեք ՝ շարունակելու համար։"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Դուք մուտք չեք գործել Firefox-ի ձեր Հաշիվ։"; +"SendTo.NotSignedIn.Title" = "Դուք մուտք չեք գործել Ecosia-ի ձեր Հաշիվ։"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Ուղարկել"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Հպեք՝ սկսելու համար"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Սինք"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Սինք"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Տեսք"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Տվյալների կառավարում"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox- ը կդադարեցնի համաժամացումը ձեր հաշվի հետ, բայց չի ջնջվի այս սարքի ձեր զննարկման որևէ տվյալ։"; +"Settings.Disconnect.Body" = "Ecosia- ը կդադարեցնի համաժամացումը ձեր հաշվի հետ, բայց չի ջնջվի այս սարքի ձեր զննարկման որևէ տվյալ։"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Կապախզել Սինքը"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Սինքի կարգավորումներ"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox հաշիվ"; +"Settings.FxA.Title" = "Ecosia հաշիվ"; /* General settings section title */ "Settings.General.SectionName" = "Գլխավոր"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Ընտրեք այն, ինչ ցուցադրվում է որպես գլխավոր էջ:"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Ընտրեք բովանդակությունը, որը տեսնում եք Firefox-ի գլխավոր էջում:"; +"Settings.Home.Option.Description.v101" = "Ընտրեք բովանդակությունը, որը տեսնում եք Ecosia-ի գլխավոր էջում:"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Վերադառնալ"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Տնային էջ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Ընտրեք այն, ինչ կտեսնեք Firefox-ին վերադառնալիս:"; +"Settings.Home.Option.StartAtHome.Description" = "Ընտրեք այն, ինչ կտեսնեք Ecosia-ին վերադառնալիս:"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Վերջին ներդիրը"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Պաստառներ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-ի պաստառ, ամեթիստ նախշ:"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-ի պաստառ, ամեթիստ նախշ:"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-ի պաստառ, լողափի բլուրների նախշ:"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-ի պաստառ, լողափի բլուրների նախշ:"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-ի պաստառ, սերուլյան օրինակ:"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-ի պաստառ, սերուլյան օրինակ:"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Սկզբնադիր մաքուր պաստառ:"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox-ի պաստառ, սերուլյան օրինակ:"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia-ի պաստառ, սերուլյան օրինակ:"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Գլխավոր էջի պաստառի ցիկլի միացում"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-ի պաստառ, լողափի բլուրների նախշ:"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-ի պաստառ, լողափի բլուրների նախշ:"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Դասական %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Սահմանափակ թողարկում"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Փոխեք պաստառը՝ կտտացնելով Firefox-ի գլխավոր էջի պատկերանշանին"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Փոխեք պաստառը՝ կտտացնելով Ecosia-ի գլխավոր էջի պատկերանշանին"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Պաստառներ"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Հարմարեցված"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox֊-ի սկիզբ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia֊-ի սկիզբ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Տնէջ"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Արգելափակել պատկերները"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox-ը բացելիս"; +"Settings.OfferClipboardBar.Status" = "Ecosia-ը բացելիս"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@-ը բացելիս"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox-ը բացելիս"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia-ը բացելիս"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Առաջարկեք բացել պատճենված հղումները"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Իմանալ ավելին:"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla-ն ձգտում է հավաքել միայն այն ինչ անհրաժեշտ է տրամադրել՝ Firefox-ը բոլորի համար լավարկել ու համար:"; +"Settings.SendUsage.Message" = "Ecosia-ն ձգտում է հավաքել միայն այն ինչ անհրաժեշտ է տրամադրել՝ Ecosia-ը բոլորի համար լավարկել ու համար:"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Ուղարկել օգտագործման տվյալները"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Բացել նոր ներդիր"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Օգտագործեք Siri-ի դյուրանցումները՝ Siri֊ի միջոցով Firefox-ը արագ բացելու համար"; +"Settings.Siri.SectionDescription" = "Օգտագործեք Siri-ի դյուրանցումները՝ Siri֊ի միջոցով Ecosia-ը արագ բացելու համար"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri֊ի դյուրանցումներ"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Իմանալ ավելին:"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox-ը կարող է ժամանակ առ ժամանակ տեղադրել և գործարկել ուսումնասիրություններ:"; +"Settings.Studies.Toggle.Message" = "Ecosia-ը կարող է ժամանակ առ ժամանակ տեղադրել և գործարկել ուսումնասիրություններ:"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Ուսումնասիրություններ"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Բեռնվում է հետնաշերտում"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Բեռնում Firefox-ում"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Բեռնում Ecosia-ում"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Բացել Firefox-֊ում"; +"ShareExtension.OpenInFirefoxAction.Title" = "Բացել Ecosia-֊ում"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Որոնել Firefox֊-ում"; +"ShareExtension.SeachInFirefoxAction.Title" = "Որոնել Ecosia֊-ում"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Պատճենել"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Փակցնել և գնալ"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Որոնել Firefox֊-ով"; +"UIMenuItem.SearchWithFirefox" = "Որոնել Ecosia֊-ով"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "չընթերցված"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Երեկ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Դուք չունեք բացված որևէ ներդիր Firefox-ում՝ ձեր մյուս սարքերում:"; +"You don’t have any tabs open in Firefox on your other devices." = "Դուք չունեք բացված որևէ ներդիր Ecosia-ում՝ ձեր մյուս սարքերում:"; /* Your Rights settings section title */ "Your Rights" = "Ձեր իրավունքները"; diff --git a/firefox-ios/Shared/hy-AM.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/hy-AM.lproj/PrivateBrowsing.strings index 61740403ff281..2d8a3aad45669 100644 --- a/firefox-ios/Shared/hy-AM.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/hy-AM.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Բացել նոր անձնական ներդիրում"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox-֊ը չի հիշի ձեր պատմությունը կամ նշոցիկները, բայց նոր էջանիշերը կպահպանվեն։"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia-֊ը չի հիշի ձեր պատմությունը կամ նշոցիկները, բայց նոր էջանիշերը կպահպանվեն։"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Իմանալ ավելին"; diff --git a/firefox-ios/Shared/hy-AM.lproj/Today.strings b/firefox-ios/Shared/hy-AM.lproj/Today.strings index c572a4c29c77e..b205818d0e4c9 100644 --- a/firefox-ios/Shared/hy-AM.lproj/Today.strings +++ b/firefox-ios/Shared/hy-AM.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Փակել\nգաղտնի ներդիրները"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefox-ի դյուրանցումները ավելացնելք ձեր հիմնական էկրանին:"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosia-ի դյուրանցումները ավելացնելք ձեր հիմնական էկրանին:"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Անցնել պատճենված հղմանը"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Չկան բաց ներդիրներ:"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Բացել Firefox-ը"; +"TodayWidget.OpenFirefoxLabel" = "Բացել Ecosia-ը"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Գաղտնի որոնում"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Ձեր Հիմնական էկրանին ավելացրեք Firefox դյուրանցում: Վիջեթն ավելացնելուց հետո հպեք և պահեք այն խմբագրելու համար և ընտրեք այլ դյուրանցում:"; +"TodayWidget.QuickActionGalleryDescription" = "Ձեր Հիմնական էկրանին ավելացրեք Ecosia դյուրանցում: Վիջեթն ավելացնելուց հետո հպեք և պահեք այն խմբագրելու համար և ընտրեք այլ դյուրանցում:"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Արագ գործողություններ"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-ի դյուրանցումներ"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-ի դյուրանցումներ"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Հավելել դյուրանցումներ բաց ներդիրներին:"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Արագ դիտում"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Որոնել Firefox֊ում"; +"TodayWidget.SearchInFirefoxV2" = "Որոնել Ecosia֊ում"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Որոնել\nգաղտնի ներդիրում"; diff --git a/firefox-ios/Shared/ia.lproj/Default Browser.strings b/firefox-ios/Shared/ia.lproj/Default Browser.strings index 9cbd202b438ef..4d0d30ad80714 100644 --- a/firefox-ios/Shared/ia.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ia.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Render Firefox tu navigator predefinite es un voto pro un internet aperte, accessibile."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Render Ecosia tu navigator predefinite es un voto pro un internet aperte, accessibile."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Predefinite pro un internet melior"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Apprende como"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Face que omne ligamines in sitos web, e-mail e Messages se aperi automaticamente in Firefox."; +"DefaultBrowserCard.Description" = "Face que omne ligamines in sitos web, e-mail e Messages se aperi automaticamente in Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Firefox como tu navigator predefinite pro render automatic velocitate, securitate e confidentialitate."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Elige Ecosia como tu navigator predefinite pro render automatic velocitate, securitate e confidentialitate."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Meliora le navigation quotidian"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Cata mense Firefox bloca in media plus de 3000 traciatores per usator. Rende nos tu navigator predefinite, pro confidentialitate e tranquillitate."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Cata mense Ecosia bloca in media plus de 3000 traciatores per usator. Rende nos tu navigator predefinite, pro confidentialitate e tranquillitate."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ha le protection de confidentialitate"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ha le protection de confidentialitate"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambiar de navigator predefinite"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tocca App navigator predefinite"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Elige Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Elige Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "App navigator predefinite"; diff --git a/firefox-ios/Shared/ia.lproj/Intro.strings b/firefox-ios/Shared/ia.lproj/Intro.strings index 2091173d52179..24369e78912e8 100644 --- a/firefox-ios/Shared/ia.lproj/Intro.strings +++ b/firefox-ios/Shared/ia.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Aperir session"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Benvenite in Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Benvenite in Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Comenciar a navigar"; diff --git a/firefox-ios/Shared/ia.lproj/Localizable.strings b/firefox-ios/Shared/ia.lproj/Localizable.strings index 8778a6df834b2..32961ea6cf0f5 100644 --- a/firefox-ios/Shared/ia.lproj/Localizable.strings +++ b/firefox-ios/Shared/ia.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Pote esser un mal configuration o un tentativa de attacco. Procede solmente si tu accepta le risco potential."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Le proprietario de %@ ha configurate su sito incorrectemente. Pro proteger tu informationes contra furto, Firefox non se ha connectite a iste sito web."; +"ErrorPages.CertWarning.Description" = "Le proprietario de %@ ha configurate su sito incorrectemente. Pro proteger tu informationes contra furto, Ecosia non se ha connectite a iste sito web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Iste connexion non es digne de fide"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pagina initial de Firefox"; +"Firefox.HomePage.Title" = "Pagina initial de Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalisar pagina initial"; @@ -377,7 +377,7 @@ "Forward" = "Avante"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Conto Firefox"; +"FxA.FirefoxAccount" = "Conto Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gerer le conto"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Accede con tu camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Aperi Firefox sur tu computator e va a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Aperi Ecosia sur tu computator e va a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Preste a scannar"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synchronisar ora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync se ha connectite a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync se ha connectite a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync connectite"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ se ha disconnectite correctemente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Iste apparato se ha disconnectite correctemente de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Iste apparato se ha disconnectite correctemente de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync disconnectite"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync disconnectite"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un apparato se ha disconnectite de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un apparato se ha disconnectite de Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo de Firefox, cambia le fundo del schermo."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo de Ecosia, cambia le fundo del schermo."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Adjuta"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Aperi session pro vider le lista de schedas de tu altere apparatos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Chronologia"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Pro saper plus"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Pro usar le function AutoFill pro Firefox, tu debe haber un codice-contrasigno de apparato activate"; +"Logins.PasscodeRequirement.Warning" = "Pro usar le function AutoFill pro Ecosia, tu debe haber un codice-contrasigno de apparato activate"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Porta tu contrasignos sempre con te"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill Firefox Passwords"; +"Logins.WelcomeView.Title2" = "AutoFill Ecosia Passwords"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activar AutoFill"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ACCESSOS SALVATE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Il sembla que Firefox collabeva antea. Desira tu restaurar tu schedas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Il sembla que Ecosia collabeva antea. Desira tu restaurar tu schedas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como legite"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Comenciar a navigar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! Firefox ha collabite"; +"Oops! Firefox crashed" = "Ups! Ecosia ha collabite"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Aperir le articulos in le vista de lectura toccante le icone del libro quando illo appare in le barra de titulo."; @@ -905,7 +905,7 @@ "Open Tabs" = "Schedas aperte"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox non pote aperir le pagina perque illo ha un adresse invalide."; +"OpenURL.Error.Message" = "Ecosia non pote aperir le pagina perque illo ha un adresse invalide."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Impossibile aperir le pagina"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Isto permitte que tu salva le imagine in tu rolo de camera."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox desira acceder a tu photos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia desira acceder a tu photos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Clauder"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Le datos es invalide"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Va a ‘Parametros’ > ‘Firefox’ del apparato. Permitte a Firefox de acceder al camera."; +"ScanQRCode.PermissionError.Message.v100" = "Va a ‘Parametros’ > ‘Ecosia’ del apparato. Permitte a Ecosia de acceder al camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scannar codice QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Mutar al scheda"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox suggere"; +"Search.SuggestSectionTitle.v102" = "Ecosia suggere"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Le nove motor de recerca apparera in le barra de recerca rapide."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Inviar un reporto de collapso a fin que Mozilla pote corriger le problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Inviar un reporto de collapso a fin que Ecosia pote corriger le problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Inviar tu opinion"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Inviar le scheda"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Tu non ha altere apparatos connectite a iste conto Firefox disponibile a synchronisar."; +"SendTo.NoDevicesFound.Message" = "Tu non ha altere apparatos connectite a iste conto Ecosia disponibile a synchronisar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Per favor aperi Firefox, va al parametros e accede pro continuar."; +"SendTo.NotSignedIn.Message" = "Per favor aperi Ecosia, va al parametros e accede pro continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Tu non ha aperite session in tu conto Firefox."; +"SendTo.NotSignedIn.Title" = "Tu non ha aperite session in tu conto Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Inviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tocca pro comenciar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vider"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gestion de datos"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox stoppara le synchronisation con tu conto, ma non delera alcun de tu datos de navigation sur iste apparato."; +"Settings.Disconnect.Body" = "Ecosia stoppara le synchronisation con tu conto, ma non delera alcun de tu datos de navigation sur iste apparato."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Disconnecter Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Parametros de Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Conto Firefox"; +"Settings.FxA.Title" = "Conto Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Eliger que monstrar como pagina initial."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Selige lo que includer sur le pagina initial de Firefox."; +"Settings.Home.Option.Description.v101" = "Selige lo que includer sur le pagina initial de Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Saltar retro"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pagina initial"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Elige que vider quando tu retorna a Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Elige que vider quando tu retorna a Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Ultime scheda"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fundo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fundo de Firefox, motivo amethysto."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fundo de Ecosia, motivo amethysto."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fundo de Firefox, motivo dunas de plagia."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fundo de Ecosia, motivo dunas de plagia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fundo de Firefox, motivo cerulee."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fundo de Ecosia, motivo cerulee."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fundo clar predefinite."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fundo de Firefox, motivo de sol levante."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fundo de Ecosia, motivo de sol levante."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Pagina principal button pro recyclar le fundos"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fundo de Firefox, motivo collinas crepuscular."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fundo de Ecosia, motivo collinas crepuscular."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ classic"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edition limitate"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia le fundo toccante le logo de Firefox in le pagina initial"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia le fundo toccante le logo de Ecosia in le pagina initial"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fundo"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalisate"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Pagina initial de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Pagina initial de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pagina initial"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blocar imagines"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Quando Firefox se aperi"; +"Settings.OfferClipboardBar.Status" = "Quando Ecosia se aperi"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Aperiente %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Quando Firefox se aperi"; +"Settings.OfferClipboardBar.StatusV2" = "Quando Ecosia se aperi"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Proponer aperir le ligamines copiate"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Saper plus."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se effortia a colliger solmente le informationes necessari pro fornir e meliorar Firefox pro totes."; +"Settings.SendUsage.Message" = "Ecosia se effortia a colliger solmente le informationes necessari pro fornir e meliorar Ecosia pro totes."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Inviar datos de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Aperir un nove scheda"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Usa le accessos directe de Siri pro aperir rapidemente Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Usa le accessos directe de Siri pro aperir rapidemente Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Accessos directe de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Saper plus."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox pote installar e conducer studios de tempore in tempore."; +"Settings.Studies.Toggle.Message" = "Ecosia pote installar e conducer studios de tempore in tempore."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studios"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar in secunde plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargante in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargante in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Aperir in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Aperir in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Cercar in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Cercar in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Collar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Cercar con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Cercar con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "non legite"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Heri"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Tu non ha schedas aperte in Firefox sur tu altere apparatos."; +"You don’t have any tabs open in Firefox on your other devices." = "Tu non ha schedas aperte in Ecosia sur tu altere apparatos."; /* Your Rights settings section title */ "Your Rights" = "Tu derectos"; diff --git a/firefox-ios/Shared/ia.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ia.lproj/PrivateBrowsing.strings index db96665eb59a1..1014f471fb2f8 100644 --- a/firefox-ios/Shared/ia.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ia.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Aperir in un nove scheda private"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox non recordara alcun chronologia o cookie, ma le nove marcapaginas essera salvate."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia non recordara alcun chronologia o cookie, ma le nove marcapaginas essera salvate."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saper plus"; diff --git a/firefox-ios/Shared/ia.lproj/Today.strings b/firefox-ios/Shared/ia.lproj/Today.strings index a0a62774fbdc1..de6c89b3b6b7c 100644 --- a/firefox-ios/Shared/ia.lproj/Today.strings +++ b/firefox-ios/Shared/ia.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Clauder schedas private"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Insere accessos directe a Firefox in tu schermo initial."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Insere accessos directe a Ecosia in tu schermo initial."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir al ligamine copiate"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nulle scheda aperte."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Aperir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Aperir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Recerca private"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Insere un accesso directe a Firefox in tu schermo initial. Post adder le elemento, tocca e mantene premite pro modificar lo e seliger un altere accesso directe."; +"TodayWidget.QuickActionGalleryDescription" = "Insere un accesso directe a Ecosia in tu schermo initial. Post adder le elemento, tocca e mantene premite pro modificar lo e seliger un altere accesso directe."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Actiones rapide"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Accessos directe de Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Accessos directe de Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Insere accessos directe a tu schedas aperte."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rapide"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Cercar in Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Cercar in Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Cercar in scheda private"; diff --git a/firefox-ios/Shared/id.lproj/Default Browser.strings b/firefox-ios/Shared/id.lproj/Default Browser.strings index 519fbab531ef2..4f823713a2388 100644 --- a/firefox-ios/Shared/id.lproj/Default Browser.strings +++ b/firefox-ios/Shared/id.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Menjadikan Firefox peramban baku Anda adalah pilihan untuk Internet yang terbuka dan mudah diakses."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Menjadikan Ecosia peramban baku Anda adalah pilihan untuk Internet yang terbuka dan mudah diakses."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Pilihan Baku untuk Internet yang Lebih Baik"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Pelajari Caranya"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Setel agar tautan dari situs web, surel, dan Pesan dibuka di Firefox secara otomatis."; +"DefaultBrowserCard.Description" = "Setel agar tautan dari situs web, surel, dan Pesan dibuka di Ecosia secara otomatis."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Pilih Firefox sebagai peramban baku Anda untuk menghadirkan kecepatan, keamanan, dan privasi otomatis."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Pilih Ecosia sebagai peramban baku Anda untuk menghadirkan kecepatan, keamanan, dan privasi otomatis."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Tingkatkan Penjelajahan Sehari-hari"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox rata-rata memblokir 3.000+ pelacak per pengguna setiap bulannya. Jadikan kami peramban baku Anda untuk privasi dan ketenangan pikiran."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia rata-rata memblokir 3.000+ pelacak per pengguna setiap bulannya. Jadikan kami peramban baku Anda untuk privasi dan ketenangan pikiran."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox Siap Lindungi Privasi"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia Siap Lindungi Privasi"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Alihkan Peramban Baku Anda"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Ketuk App Browser Default"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Pilih Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Pilih Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "App Browser Default"; diff --git a/firefox-ios/Shared/id.lproj/Intro.strings b/firefox-ios/Shared/id.lproj/Intro.strings index dffa7ad5d88b4..eb73dbd8db09d 100644 --- a/firefox-ios/Shared/id.lproj/Intro.strings +++ b/firefox-ios/Shared/id.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Masuk"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Selamat datang di Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Selamat datang di Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Mulai Menjelajah"; diff --git a/firefox-ios/Shared/id.lproj/Localizable.strings b/firefox-ios/Shared/id.lproj/Localizable.strings index e8c475060a381..ec0600d632b2a 100644 --- a/firefox-ios/Shared/id.lproj/Localizable.strings +++ b/firefox-ios/Shared/id.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Mungkin ini disebabkan konfigurasi yang salah atau sabotase penyerang. Lanjutkan hanya jika Anda siap menanggung risikonya."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Pemilik %@ telah mengonfigurasi situs webnya secara salah. Untuk melindungi pencurian informasi Anda, Firefox tidak menyambung ke situs web ini."; +"ErrorPages.CertWarning.Description" = "Pemilik %@ telah mengonfigurasi situs webnya secara salah. Untuk melindungi pencurian informasi Anda, Ecosia tidak menyambung ke situs web ini."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Sambungan Ini Tidak Terpercaya"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Beranda Firefox"; +"Firefox.HomePage.Title" = "Beranda Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Ubahsuai Halaman Beranda"; @@ -377,7 +377,7 @@ "Forward" = "Maju"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Account"; +"FxA.FirefoxAccount" = "Ecosia Account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Kelola Akun & Peranti"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Masuk Dengan Kamera Anda"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Di komputer Anda, buka Firefox dan buka firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Di komputer Anda, buka Ecosia dan buka ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Siap Memindai"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sinkronkan Sekarang"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync telah terhubung ke %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync telah terhubung ke %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Terhubung"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ telah berhasil terputus."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Peranti ini telah berhasil diputuskan dari Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Peranti ini telah berhasil diputuskan dari Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sinkronisasi Terputus"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync Terputus"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Peranti telah diputuskan dari Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Peranti telah diputuskan dari Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, ubah wallpaper."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, ubah wallpaper."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Bantuan"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Masuk untuk melihat daftar tab dari peranti Anda lainnya."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Riwayat"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Pelajari Lebih Lanjut"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Untuk menggunakan fitur Isi-Auto untuk Firefox, Anda harus mengaktifkan kode akses perangkat."; +"Logins.PasscodeRequirement.Warning" = "Untuk menggunakan fitur Isi-Auto untuk Ecosia, Anda harus mengaktifkan kode akses perangkat."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Bawa kata sandi Anda ke mana saja"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Isi Otomatis Sandi Firefox"; +"Logins.WelcomeView.Title2" = "Isi Otomatis Sandi Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Aktifkan Isi-Auto"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "INFO MASUK TERSIMPAN"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Tampaknya tadi Firefox mogok. Ingin memulihkan tab Anda?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Tampaknya tadi Ecosia mogok. Ingin memulihkan tab Anda?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Tandai sebagai Dibaca"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Mulai Menjelajah"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Waduh! Firefox mogok"; +"Oops! Firefox crashed" = "Waduh! Ecosia mogok"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Buka artikel di Tampilan Baca dengan mengetuk ikon buku saat tampil di bilah judul."; @@ -905,7 +905,7 @@ "Open Tabs" = "Buka Tab"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox tidak dapat membuka halaman karena alamatnya tidak valid."; +"OpenURL.Error.Message" = "Ecosia tidak dapat membuka halaman karena alamatnya tidak valid."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Tidak Dapat Membuka Laman"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Hal ini memungkinkan Anda menyimpan gambar ke Rol Kamera."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ingin mengakses Foto Anda"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ingin mengakses Foto Anda"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Tutup"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Data tidak valid"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Buka ‘Pengaturan‘ perangkat > ‘Firefox‘. Izinkan Firefox untuk mengakses kamera."; +"ScanQRCode.PermissionError.Message.v100" = "Buka ‘Pengaturan‘ perangkat > ‘Ecosia‘. Izinkan Ecosia untuk mengakses kamera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Pindai Kode QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Pindah ke tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Saran Firefox"; +"Search.SuggestSectionTitle.v102" = "Saran Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Mesin pencari baru akan muncul di bilah pencarian cepat."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Oke"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Kirimkan laporan mogok agar Mozilla bisa memperbaikinya?"; +"Send a crash report so Mozilla can fix the problem?" = "Kirimkan laporan mogok agar Ecosia bisa memperbaikinya?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Kirim Masukan"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Kirim Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Anda tidak memiliki perangkat lainnya yang terhubung dengan Firefox Account ini untuk dapat disinkronkan."; +"SendTo.NoDevicesFound.Message" = "Anda tidak memiliki perangkat lainnya yang terhubung dengan Ecosia Account ini untuk dapat disinkronkan."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Silakan buka Firefox, kunjungi Setelan, dan masuk untuk melanjutkan."; +"SendTo.NotSignedIn.Message" = "Silakan buka Ecosia, kunjungi Setelan, dan masuk untuk melanjutkan."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Anda belum masuk ke Akun Firefox."; +"SendTo.NotSignedIn.Title" = "Anda belum masuk ke Akun Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Kirim"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Ketuk untuk memulai"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Tampilkan"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Manajemen Data"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox akan berhenti menyinkronkan dengan akun Anda, tetapi tidak akan menghapus data penjelajahan Anda di peranti ini."; +"Settings.Disconnect.Body" = "Ecosia akan berhenti menyinkronkan dengan akun Anda, tetapi tidak akan menghapus data penjelajahan Anda di peranti ini."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Putuskan Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Setelan Sinkronisasi"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Account"; +"Settings.FxA.Title" = "Ecosia Account"; /* General settings section title */ "Settings.General.SectionName" = "Umum"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Pilih apa yang ditampilkan sebagai beranda."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Pilih apa yang disertakan di beranda Firefox."; +"Settings.Home.Option.Description.v101" = "Pilih apa yang disertakan di beranda Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Lompat Kembali"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Beranda"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Pilih yang Anda lihat ketika kembali ke Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Pilih yang Anda lihat ketika kembali ke Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Tab terakhir"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Wallpaper"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Wallpaper Firefox, pola ametis."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Wallpaper Ecosia, pola ametis."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Wallpaper Firefox, pola bukit pantai."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Wallpaper Ecosia, pola bukit pantai."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Wallpaper Firefox, pola biru langit."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Wallpaper Ecosia, pola biru langit."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Wallpaper bening bawaan."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Wallpaper Firefox, pola matahari terbit."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Wallpaper Ecosia, pola matahari terbit."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Beralih siklus wallpaper beranda"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Wallpaper Firefox, pola bukit senja."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Wallpaper Ecosia, pola bukit senja."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ Klasik"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edisi Terbatas"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ubah wallpaper dengan mengetuk logo beranda Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ubah wallpaper dengan mengetuk logo beranda Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Wallpaper"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Ubahsuai"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Beranda"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokir Gambar"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Ketika Membuka Firefox"; +"Settings.OfferClipboardBar.Status" = "Ketika Membuka Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Saat membuka %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Ketika Membuka Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Ketika Membuka Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Sarankan untuk Membuka Tautan Tersalin"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Pelajari Lebih Lanjut."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla berusaha untuk hanya mengumpulkan yang kami perlu untuk menghadirkan dan meningkatkan Firefox untuk setiap orang."; +"Settings.SendUsage.Message" = "Ecosia berusaha untuk hanya mengumpulkan yang kami perlu untuk menghadirkan dan meningkatkan Ecosia untuk setiap orang."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Kirim Data Penggunaan"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Buka Tab Baru"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Gunakan Siri shortcuts untuk membuat Firefox dengan cepat lewat Siri"; +"Settings.Siri.SectionDescription" = "Gunakan Siri shortcuts untuk membuat Ecosia dengan cepat lewat Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri Shortcuts"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Pelajari Lebih Lanjut."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox dapat memasang dan menjalankan kajian dari waktu ke waktu."; +"Settings.Studies.Toggle.Message" = "Ecosia dapat memasang dan menjalankan kajian dari waktu ke waktu."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Kajian"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Muat di Belakang"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Memuat di Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Memuat di Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Buka di Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Buka di Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Cari di Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Cari di Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Salin"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Tempel & Kunjungi"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Cari dengan Firefox"; +"UIMenuItem.SearchWithFirefox" = "Cari dengan Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "belum dibaca"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Kemarin"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Anda tidak memiliki tab terbuka di Firefox pada perangkat Anda lainnya."; +"You don’t have any tabs open in Firefox on your other devices." = "Anda tidak memiliki tab terbuka di Ecosia pada perangkat Anda lainnya."; /* Your Rights settings section title */ "Your Rights" = "Hak Anda"; diff --git a/firefox-ios/Shared/id.lproj/LoginManager.strings b/firefox-ios/Shared/id.lproj/LoginManager.strings index 1b7957a9aca62..fd92773677e7d 100644 --- a/firefox-ios/Shared/id.lproj/LoginManager.strings +++ b/firefox-ios/Shared/id.lproj/LoginManager.strings @@ -52,6 +52,9 @@ /* Label displayed when no logins are found after searching. */ "No logins found" = "Tidak ada info masuk ditemukan"; +/* Label displayed when no logins are found after searching. */ +"NoLoginsFound.Title.v122" = "Tidak ada sandi ditemukan"; + /* Open and Fill website text selection menu item */ "Open & Fill" = "Buka & Isi"; diff --git a/firefox-ios/Shared/id.lproj/Menu.strings b/firefox-ios/Shared/id.lproj/Menu.strings index 199b61cd0b60b..15fd5aebe3b21 100644 --- a/firefox-ios/Shared/id.lproj/Menu.strings +++ b/firefox-ios/Shared/id.lproj/Menu.strings @@ -4,6 +4,12 @@ /* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ "Menu.CopyLink.Title" = "Salin Tautan"; +/* Toast displayed to user after downlaod pdf was pressed. */ +"Menu.DownloadPDF.Confirm.v129" = "Berhasil Mengunduh PDF"; + +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "Unduh PDF"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "Temukan di Halaman"; diff --git a/firefox-ios/Shared/id.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/id.lproj/PrivateBrowsing.strings index e17a732598cb5..c7bb881e4270c 100644 --- a/firefox-ios/Shared/id.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/id.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Buka di Tab Pribadi Baru"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox tidak akan mengingat semua riwayat atau kuki, tetapi markah baru akan tetap disimpan."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia tidak akan mengingat semua riwayat atau kuki, tetapi markah baru akan tetap disimpan."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Pelajari Lebih Lanjut"; @@ -22,6 +22,9 @@ /* Accessibility label for toggling on/off private mode */ "Private Mode" = "Mode Pribadi"; +/* Accessibility label for toggling on/off private mode */ +"PrivateBrowsing.Toggle.A11y.Label.v132" = "Penjelajahan pribadi"; + /* Accessiblity hint for toggling on/off private mode */ "Turns private mode on or off" = "Aktifkan atau nonaktifkan mode pribadi"; diff --git a/firefox-ios/Shared/id.lproj/Search.strings b/firefox-ios/Shared/id.lproj/Search.strings index ad64e918f5d3e..20f9d48dec738 100644 --- a/firefox-ios/Shared/id.lproj/Search.strings +++ b/firefox-ios/Shared/id.lproj/Search.strings @@ -4,3 +4,6 @@ /* Label for search settings button. */ "Search Settings" = "Setelan Pencarian"; +/* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a label for sponsored Firefox suggestions. */ +"Search.SponsoredSuggestionDescription.v119" = "Bersponsor"; + diff --git a/firefox-ios/Shared/id.lproj/Today.strings b/firefox-ios/Shared/id.lproj/Today.strings index 2a04826897ab9..11aff743829dc 100644 --- a/firefox-ios/Shared/id.lproj/Today.strings +++ b/firefox-ios/Shared/id.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Tutup Tab Pribadi"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Tambahkan pintasan Firefox ke layar Beranda Anda."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Tambahkan pintasan Ecosia ke layar Beranda Anda."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Buka tautan yang disalin"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Tidak ada tab terbuka."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Buka Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Buka Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Pencarian Pribadi"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Tambahkan pintasan Firefox ke layar Beranda Anda. Setelah menambahkan widget, sentuh dan tahan untuk mengeditnya lalu pilih pintasan yang berbeda."; +"TodayWidget.QuickActionGalleryDescription" = "Tambahkan pintasan Ecosia ke layar Beranda Anda. Setelah menambahkan widget, sentuh dan tahan untuk mengeditnya lalu pilih pintasan yang berbeda."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Aksi Cepat"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Pintasan Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Pintasan Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Tambahkan pintasan ke tab terbuka Anda."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Lihat Sekilas"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Cari di Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Cari di Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Cari di Tab Pribadi"; diff --git a/firefox-ios/Shared/is.lproj/Default Browser.strings b/firefox-ios/Shared/is.lproj/Default Browser.strings index 543e6298740f6..1e21a8408a6ee 100644 --- a/firefox-ios/Shared/is.lproj/Default Browser.strings +++ b/firefox-ios/Shared/is.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Að gera Firefox að sjálfgefnum vafra er sama og greitt atkvæði um opið, aðgengilegt internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Að gera Ecosia að sjálfgefnum vafra er sama og greitt atkvæði um opið, aðgengilegt internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Sjálfgefið á betra interneti"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Læra hvernig"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Stilltu tengla frá vefsvæðum, tölvupósti og skilaboðum til að opna sjálfkrafa í Firefox."; +"DefaultBrowserCard.Description" = "Stilltu tengla frá vefsvæðum, tölvupósti og skilaboðum til að opna sjálfkrafa í Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Veldu Firefox sem sjálfgefinn vafra til að sjálfvirkt njóta hraða, öryggis og næðis."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Veldu Ecosia sem sjálfgefinn vafra til að sjálfvirkt njóta hraða, öryggis og næðis."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Lyftu daglegu vafri á annað stig"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox lokar á meira en 3.000 rekjara að meðaltali fyrir hvern notanda á mánuði. Gerðu hann að sjálfgefnum vafra til að styrkja friðhelgi einkalífsins."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia lokar á meira en 3.000 rekjara að meðaltali fyrir hvern notanda á mánuði. Gerðu hann að sjálfgefnum vafra til að styrkja friðhelgi einkalífsins."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox styður friðhelgi einkalífsins"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia styður friðhelgi einkalífsins"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Skiptu um sjálfgefinn vafra"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Ýttu á sjálfgefið vafraforrit"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Veldu Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Veldu Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Sjálfgefið vafraforrit"; diff --git a/firefox-ios/Shared/is.lproj/Intro.strings b/firefox-ios/Shared/is.lproj/Intro.strings index ac6fa79060250..7f2cecda4c5d4 100644 --- a/firefox-ios/Shared/is.lproj/Intro.strings +++ b/firefox-ios/Shared/is.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Innskráning"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Verið velkomin í Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Verið velkomin í Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Fara að vafra"; diff --git a/firefox-ios/Shared/is.lproj/Localizable.strings b/firefox-ios/Shared/is.lproj/Localizable.strings index d7a4884f2db61..102beb0fde034 100644 --- a/firefox-ios/Shared/is.lproj/Localizable.strings +++ b/firefox-ios/Shared/is.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Það gæti verið röng stilling eða ytri aðili er búinn að eiga við stillingarnar. Haltu áfram ef þú metur það öruggt."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Eigandi %@ hefur stillt vefsvæðið sitt rangt. Firefox mun ekki tengjast þessu vefsvæði til að vernda upplýsingar þínar."; +"ErrorPages.CertWarning.Description" = "Eigandi %@ hefur stillt vefsvæðið sitt rangt. Ecosia mun ekki tengjast þessu vefsvæði til að vernda upplýsingar þínar."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Þessi tenging er ótraust"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Upphafssíða Firefox"; +"Firefox.HomePage.Title" = "Upphafssíða Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Sérsníða upphafssíðu"; @@ -377,7 +377,7 @@ "Forward" = "Áfram"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox reikningur"; +"FxA.FirefoxAccount" = "Ecosia reikningur"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Sýsla með reikning og tæki"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Skráðu þig inn með myndavélinni þinni"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Opnaðu Firefox í tölvunni þinni og farðu á firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Opnaðu Ecosia í tölvunni þinni og farðu á ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Tilbúið til skönnunar"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Samstilla núna"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox samstillingin er tengd %@"; +"FxAPush_DeviceConnected_body" = "Ecosia samstillingin er tengd %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Samstilling tengd"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ aftengt giftusamlega."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Þetta tæki er aftengt Firefox samstillingu."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Þetta tæki er aftengt Ecosia samstillingu."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Samstilling ótengd"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Samstilling ótengd"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Tæki hefur aftengt Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Tæki hefur aftengt Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox merki - breyta bakgrunni."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia merki - breyta bakgrunni."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Hjálp"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Skráðu þig inn til að skoða lista af flipum frá öðrum tækjum."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox samstilling"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia samstilling"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Ferill"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Fræðast meira"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Til að nota sjálfvirka útfyllingareiginleikann fyrir Firefox verður þú að hafa aðgangskóða tækis virkan."; +"Logins.PasscodeRequirement.Warning" = "Til að nota sjálfvirka útfyllingareiginleikann fyrir Ecosia verður þú að hafa aðgangskóða tækis virkan."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Taktu lykilorðin þín hvert sem er"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Sjálfvirk útfylling lykilorða í Firefox"; +"Logins.WelcomeView.Title2" = "Sjálfvirk útfylling lykilorða í Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Kveikja á sjálfvirkri útfyllingu"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Lítur út fyrir að Firefox hafi hrunið. Viltu opna flipana þína aftur?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Lítur út fyrir að Ecosia hafi hrunið. Viltu opna flipana þína aftur?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Merkja sem lesið"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Fara að vafra"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Æi! Firefox hrundi"; +"Oops! Firefox crashed" = "Æi! Ecosia hrundi"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Opnaðu greinar í lesham með því að ýta á bókartáknið þegar það birtist í titilslá."; @@ -905,7 +905,7 @@ "Open Tabs" = "Opna flipa"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox getur ekki opnað þessa síðu vegna þess að vistfangið er ógilt."; +"OpenURL.Error.Message" = "Ecosia getur ekki opnað þessa síðu vegna þess að vistfangið er ógilt."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Geta ekki opnað síðu"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Þú getur vistað myndina í myndasafnið þitt."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vill fá aðgang að myndunum þínum"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vill fá aðgang að myndunum þínum"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Loka"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Gögnin eru ógild"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Farðu á tækinu í 'Stillingar' > 'Firefox'. Leyfðu Firefox að fá aðgang að myndavélinni."; +"ScanQRCode.PermissionError.Message.v100" = "Farðu á tækinu í 'Stillingar' > 'Ecosia'. Leyfðu Ecosia að fá aðgang að myndavélinni."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skanna QR-kóða"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Skipta yfir á flipa"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox tillögur"; +"Search.SuggestSectionTitle.v102" = "Ecosia tillögur"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nýja leitarvélin mun birtast í leitarsvæðinu"; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Í lagi"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Senda hrunaskýrslu svo Mozilla geti lagað vandamálið?"; +"Send a crash report so Mozilla can fix the problem?" = "Senda hrunaskýrslu svo Ecosia geti lagað vandamálið?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Senda endurgjöf"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Senda flipa"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Þú ert ekki með önnur tæki tengd við þennan Firefox reikning sem hægt er að samstilla."; +"SendTo.NoDevicesFound.Message" = "Þú ert ekki með önnur tæki tengd við þennan Ecosia reikning sem hægt er að samstilla."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Vinsamlegast opnaðu Firefox, farðu í stillingar og skráðu þig inn til að halda áfram."; +"SendTo.NotSignedIn.Message" = "Vinsamlegast opnaðu Ecosia, farðu í stillingar og skráðu þig inn til að halda áfram."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Þú ert ekki skráð/ur inn á Firefox reikninginn þinn."; +"SendTo.NotSignedIn.Title" = "Þú ert ekki skráð/ur inn á Ecosia reikninginn þinn."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Senda"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Ýttu á til að byrja"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox samstilling"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia samstilling"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Skoða"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gagnastýring"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox mun hætta að samstilla reikninginn þinn en mun ekki eyða neinum vafragögnum á þessu tæki."; +"Settings.Disconnect.Body" = "Ecosia mun hætta að samstilla reikninginn þinn en mun ekki eyða neinum vafragögnum á þessu tæki."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Aftengja samstillingu"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Stillingar samstillingar"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox reikningur"; +"Settings.FxA.Title" = "Ecosia reikningur"; /* General settings section title */ "Settings.General.SectionName" = "Almennt"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Veldu hvað birtist sem upphafssíða."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Veldu það efni sem þú sérð á upphafssíðu Firefox."; +"Settings.Home.Option.Description.v101" = "Veldu það efni sem þú sérð á upphafssíðu Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Hoppa aftur inn"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Upphafssíða"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Veldu hvað þú sérð þegar þú ferð aftur í Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Veldu hvað þú sérð þegar þú ferð aftur í Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Síðasti flipi"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Bakgrunnur"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox bakgrunnur, ametyst-mynstur."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia bakgrunnur, ametyst-mynstur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox bakgrunnur, strandhólar-mynstur."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia bakgrunnur, strandhólar-mynstur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox bakgrunnur, cerulean-mynstur."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia bakgrunnur, cerulean-mynstur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Sjálfgefinn glær bakgrunnur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox bakgrunnur, sólarupprás-mynstur."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia bakgrunnur, sólarupprás-mynstur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Víxla af/á að fletta í gegnum bakgrunna"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox bakgrunnur, hæðir-í-rökkri-mynstur."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia bakgrunnur, hæðir-í-rökkri-mynstur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassískur %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Takmörkuð útgáfa"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Skiptu um bakgrunn með því að smella á táknmynd Firefox-heimasíðunnar"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Skiptu um bakgrunn með því að smella á táknmynd Ecosia-heimasíðunnar"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Bakgrunnur"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Sérsniðið"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Upphafssíða Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Upphafssíða Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Upphafssíða"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Loka á myndir"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Þegar Firefox ræsir"; +"Settings.OfferClipboardBar.Status" = "Þegar Ecosia ræsir"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Þegar %@ er opnað"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Þegar Firefox ræsir"; +"Settings.OfferClipboardBar.StatusV2" = "Þegar Ecosia ræsir"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Bjóða uppá að opna afritaða hlekki"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Fræðast meira."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla safnar eingöngu þeim gögnum sem við þurfum til að veita og betrum bæta Firefox fyrir alla."; +"Settings.SendUsage.Message" = "Ecosia safnar eingöngu þeim gögnum sem við þurfum til að veita og betrum bæta Ecosia fyrir alla."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Senda notkunarupplýsingar"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Opna nýjan flipa"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Nota Siri flýtivísanir til að opna Firefox með Siri"; +"Settings.Siri.SectionDescription" = "Nota Siri flýtivísanir til að opna Ecosia með Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri flýtileiðir"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Fræðast meira."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox kann að setja upp og keyra rannsóknir af og til."; +"Settings.Studies.Toggle.Message" = "Ecosia kann að setja upp og keyra rannsóknir af og til."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Kannanir"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Hlaða í bakvinnslu"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Opnast í Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Opnast í Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Opna í Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Opna í Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Leita í Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Leita í Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Afrita"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Líma & áfram"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Leita með Firefox"; +"UIMenuItem.SearchWithFirefox" = "Leita með Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ólesið"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Í gær"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Þú ert ekki með neina Firefox flipa opna á hinum tækjunum þínum."; +"You don’t have any tabs open in Firefox on your other devices." = "Þú ert ekki með neina Ecosia flipa opna á hinum tækjunum þínum."; /* Your Rights settings section title */ "Your Rights" = "Réttindi þín"; diff --git a/firefox-ios/Shared/is.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/is.lproj/PrivateBrowsing.strings index 9f55c7c729288..f9dbec4e7a79c 100644 --- a/firefox-ios/Shared/is.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/is.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Opna í nýjum huliðsflipa"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox man hvorki ferilinn þinn né vefkökur en ný bókamerki munu vistast."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia man hvorki ferilinn þinn né vefkökur en ný bókamerki munu vistast."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Fræðast meira"; diff --git a/firefox-ios/Shared/is.lproj/Today.strings b/firefox-ios/Shared/is.lproj/Today.strings index 1847841b3663a..7c5c154a6f08c 100644 --- a/firefox-ios/Shared/is.lproj/Today.strings +++ b/firefox-ios/Shared/is.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Loka\nhuliðsflipum"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Bættu Firefox flýtileiðum við upphafsskjáinn þinn."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Bættu Ecosia flýtileiðum við upphafsskjáinn þinn."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Fara á afritaða tengilinn"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Engir opnir flipar."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Opna Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Opna Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Huliðsleit"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Bættu Firefox-flýtileið við upphafsskjáinn þinn. Eftir að græjunni hefur verið bætt við skaltu ýta og halda inni til að breyta henni og velja aðra flýtileið."; +"TodayWidget.QuickActionGalleryDescription" = "Bættu Ecosia-flýtileið við upphafsskjáinn þinn. Eftir að græjunni hefur verið bætt við skaltu ýta og halda inni til að breyta henni og velja aðra flýtileið."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Flýtiaðgerðir"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox flýtileiðir"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia flýtileiðir"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Bætta við flýtileiðum í opnu flipana þína."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Flýtiskoðun"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Leita í \nFirefox"; +"TodayWidget.SearchInFirefoxV2" = "Leita í \nEcosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Leita í\n huliðsflipa"; diff --git a/firefox-ios/Shared/it.lproj/Default Browser.strings b/firefox-ios/Shared/it.lproj/Default Browser.strings index 98390ad3ec439..7b6a1df4cff92 100644 --- a/firefox-ios/Shared/it.lproj/Default Browser.strings +++ b/firefox-ios/Shared/it.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Impostare Firefox come browser predefinito è un voto per un Internet aperto e accessibile."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Impostare Ecosia come browser predefinito è un voto per un Internet aperto e accessibile."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Scegli un Internet migliore"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Scopri come fare"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Utilizza automaticamente Firefox per aprire link in siti web, email e messaggi."; +"DefaultBrowserCard.Description" = "Utilizza automaticamente Ecosia per aprire link in siti web, email e messaggi."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Scegli Firefox come browser predefinito per rendere automatiche velocità, sicurezza e privacy."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Scegli Ecosia come browser predefinito per rendere automatiche velocità, sicurezza e privacy."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Migliora la navigazione quotidiana"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blocca in media oltre 3000 elementi traccianti per utente ogni mese. Imposta Firefox come browser predefinito per stare tranquillo riguardo alla tua privacy."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blocca in media oltre 3000 elementi traccianti per utente ogni mese. Imposta Ecosia come browser predefinito per stare tranquillo riguardo alla tua privacy."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox protegge la tua privacy"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia protegge la tua privacy"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Cambia il browser predefinito"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tocca App per il browser di default"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Seleziona Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Seleziona Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "App per il browser di default"; diff --git a/firefox-ios/Shared/it.lproj/Intro.strings b/firefox-ios/Shared/it.lproj/Intro.strings index b59bdea87ebbe..292bfc9ea6b29 100644 --- a/firefox-ios/Shared/it.lproj/Intro.strings +++ b/firefox-ios/Shared/it.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Accedi"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Benvenuto in Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Benvenuto in Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Inizia a navigare"; diff --git a/firefox-ios/Shared/it.lproj/Localizable.strings b/firefox-ios/Shared/it.lproj/Localizable.strings index fbd9e54af085e..31b6efc33263d 100644 --- a/firefox-ios/Shared/it.lproj/Localizable.strings +++ b/firefox-ios/Shared/it.lproj/Localizable.strings @@ -221,7 +221,7 @@ "Could not load page." = "Impossibile caricare la pagina."; /* Description for the new ETP mode i.e. standard vs strict */ -"CoverSheet.v24.ETP.Description" = "La protezione antitracciamento avanzata integrata in Firefox impedisce agli annunci pubblicitari di seguirti in giro. Per bloccare ancora più elementi traccianti, annunci pubblicitari e pop-up, attiva l’opzione Restrittiva."; +"CoverSheet.v24.ETP.Description" = "La protezione antitracciamento avanzata integrata in Ecosia impedisce agli annunci pubblicitari di seguirti in giro. Per bloccare ancora più elementi traccianti, annunci pubblicitari e pop-up, attiva l’opzione Restrittiva."; /* Text for the new ETP settings button */ "CoverSheet.v24.ETP.Settings.Button" = "Vai alle impostazioni"; @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Potrebbe trattarsi di una configurazione errata o di un tentativo di attacco. Procedi solo se sei consapevole dei potenziali rischi."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Il gestore di %@ ha configurato il sito in modo non corretto. Per evitare potenziali furti di informazioni Firefox ha interrotto la connessione."; +"ErrorPages.CertWarning.Description" = "Il gestore di %@ ha configurato il sito in modo non corretto. Per evitare potenziali furti di informazioni Ecosia ha interrotto la connessione."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Questa connessione non è affidabile"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pagina iniziale di Firefox"; +"Firefox.HomePage.Title" = "Pagina iniziale di Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizza pagina iniziale"; @@ -377,7 +377,7 @@ "Forward" = "Avanti"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Account Firefox"; +"FxA.FirefoxAccount" = "Account Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gestione account e dispositivi"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Accedi con la fotocamera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Apri Firefox sul tuo computer e vai all’indirizzo firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Apri Ecosia sul tuo computer e vai all’indirizzo ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pronto per la scansione"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizza adesso"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "%@ è ora connesso a Firefox Sync."; +"FxAPush_DeviceConnected_body" = "%@ è ora connesso a Ecosia Sync."; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync connesso"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ è stato disconnesso correttamente."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Questo dispositivo è stato disconnesso correttamente da Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Questo dispositivo è stato disconnesso correttamente da Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync disconnesso"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync disconnesso"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo è stato disconnesso da Firefox Sync."; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispositivo è stato disconnesso da Ecosia Sync."; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo di Firefox, cambia lo sfondo."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo di Ecosia, cambia lo sfondo."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Supporto"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Accedi per visualizzare l’elenco delle schede aperte sugli altri dispositivi connessi."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Cronologia"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Ulteriori informazioni"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Per utilizzare la funzione di compilazione automatica per Firefox, devi avere attivato un codice per il dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Per utilizzare la funzione di compilazione automatica per Ecosia, devi avere attivato un codice per il dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Porta le tue password sempre con te"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Compilazione automatica delle password di Firefox"; +"Logins.WelcomeView.Title2" = "Compilazione automatica delle password di Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Attiva la compilazione automatica"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ACCESSI SALVATI"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sembra che si sia verificato un arresto inatteso di Firefox. Ripristinare le schede?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sembra che si sia verificato un arresto inatteso di Ecosia. Ripristinare le schede?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Segna come letto"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Inizia a navigare"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox si è bloccato"; +"Oops! Firefox crashed" = "Oops! Ecosia si è bloccato"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Apri articoli in Modalità lettura toccando l’icona quando appare nella barra del titolo."; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Permette di salvare le immagini nel Rullino."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vorrebbe accedere alle foto del dispositivo"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vorrebbe accedere alle foto del dispositivo"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Chiudi"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Dati non validi"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Vai su “Impostazioni” del dispositivo > “Firefox”. Consenti a Firefox di accedere alla fotocamera."; +"ScanQRCode.PermissionError.Message.v100" = "Vai su “Impostazioni” del dispositivo > “Ecosia”. Consenti a Ecosia di accedere alla fotocamera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scansione codice QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Passa alla scheda"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Il nuovo motore di ricerca verrà visualizzato nella barra di ricerca rapida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Inviare una segnalazione del blocco a Mozilla in modo che possano risolvere il problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Inviare una segnalazione del blocco a Ecosia in modo che possano risolvere il problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Invia feedback"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Invia scheda"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Al momento non risultano disponibili per la sincronizzazione ulteriori dispositivi connessi a questo account Firefox."; +"SendTo.NoDevicesFound.Message" = "Al momento non risultano disponibili per la sincronizzazione ulteriori dispositivi connessi a questo account Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Apri le impostazioni di Firefox ed effettua l’accesso per continuare."; +"SendTo.NotSignedIn.Message" = "Apri le impostazioni di Ecosia ed effettua l’accesso per continuare."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Accesso all’account Firefox non effettuato."; +"SendTo.NotSignedIn.Title" = "Accesso all’account Ecosia non effettuato."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Invia"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tocca per iniziare"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Visualizza"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gestione dati"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "I dati di navigazione non verranno più sincronizzati con l’account Firefox, ma non saranno rimossi dal dispositivo in uso."; +"Settings.Disconnect.Body" = "I dati di navigazione non verranno più sincronizzati con l’account Ecosia, ma non saranno rimossi dal dispositivo in uso."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Disconnetti Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Impostazioni di sincronizzazione"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Account Firefox"; +"Settings.FxA.Title" = "Account Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Generale"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Scegli cosa visualizzare come pagina iniziale."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Scegli cosa includere nella pagina iniziale di Firefox."; +"Settings.Home.Option.Description.v101" = "Scegli cosa includere nella pagina iniziale di Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Torna a questa scheda"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pagina iniziale"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Scegli quali elementi visualizzare quando riapri Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Scegli quali elementi visualizzare quando riapri Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Ultima scheda"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Sfondo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Sfondo di Firefox, motivo ametista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Sfondo di Ecosia, motivo ametista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Sfondo di Firefox, motivo dune sulla spiaggia."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Sfondo di Ecosia, motivo dune sulla spiaggia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Sfondo di Firefox, motivo ceruleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Sfondo di Ecosia, motivo ceruleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Sfondo trasparente predefinito."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Sfondo di Firefox, motivo alba."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Sfondo di Ecosia, motivo alba."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Interruttore per attivare/disattivare il ciclo dello sfondo della pagina iniziale"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Sfondo di Firefox, motivo colline al tramonto."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Sfondo di Ecosia, motivo colline al tramonto."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ classico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edizione limitata"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia lo sfondo toccando il logo di Firefox nella pagina iniziale"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Cambia lo sfondo toccando il logo di Ecosia nella pagina iniziale"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Sfondo"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizzata"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Pagina iniziale di Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Pagina iniziale di Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pagina iniziale"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blocca immagini"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "All’apertura di Firefox"; +"Settings.OfferClipboardBar.Status" = "All’apertura di Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "All’apertura di %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "All’apertura di Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "All’apertura di Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Chiedi se aprire i link copiati"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Ulteriori informazioni."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla si impegna a raccogliere solo i dati necessari a fornire e migliorare Firefox per tutti gli utenti."; +"Settings.SendUsage.Message" = "Ecosia si impegna a raccogliere solo i dati necessari a fornire e migliorare Ecosia per tutti gli utenti."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Invia dati sull’uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Apri nuova scheda"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilizza Siri per aprire velocemente Firefox"; +"Settings.Siri.SectionDescription" = "Utilizza Siri per aprire velocemente Ecosia"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Comandi di Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Ulteriori informazioni."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox può installare e condurre degli studi di tanto in tanto."; +"Settings.Studies.Toggle.Message" = "Ecosia può installare e condurre degli studi di tanto in tanto."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studi"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Carica in background"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Caricamento in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Caricamento in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Apri in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Apri in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Cerca in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Cerca in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copia"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Incolla e Vai"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Cerca con Firefox"; +"UIMenuItem.SearchWithFirefox" = "Cerca con Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "non letto"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ieri"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Non risulta alcuna scheda aperta in Firefox negli altri dispositivi."; +"You don’t have any tabs open in Firefox on your other devices." = "Non risulta alcuna scheda aperta in Ecosia negli altri dispositivi."; /* Your Rights settings section title */ "Your Rights" = "I tuoi diritti"; diff --git a/firefox-ios/Shared/it.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/it.lproj/PrivateBrowsing.strings index e1d86828e7af2..9e91d15e3e038 100644 --- a/firefox-ios/Shared/it.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/it.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Apri in nuova scheda anonima"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox non salverà alcuna cronologia o cookie per questa finestra. I segnalibri aggiunti verranno comunque conservati sul dispositivo."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia non salverà alcuna cronologia o cookie per questa finestra. I segnalibri aggiunti verranno comunque conservati sul dispositivo."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Ulteriori informazioni"; diff --git a/firefox-ios/Shared/it.lproj/Today.strings b/firefox-ios/Shared/it.lproj/Today.strings index 3bd747838a6df..ec80a37e659bd 100644 --- a/firefox-ios/Shared/it.lproj/Today.strings +++ b/firefox-ios/Shared/it.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Chiudi le schede anonime"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Aggiungi collegamenti Firefox alla schermata principale"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Aggiungi collegamenti Ecosia alla schermata principale"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Apri il link copiato"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nessuna scheda aperta."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Apri Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Apri Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Ricerca anonima"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Aggiungi un collegamento a Firefox alla schermata principale. Dopo aver aggiunto il widget, toccalo tenendo premuto per modificarlo e selezionare un collegamento diverso."; +"TodayWidget.QuickActionGalleryDescription" = "Aggiungi un collegamento a Ecosia alla schermata principale. Dopo aver aggiunto il widget, toccalo tenendo premuto per modificarlo e selezionare un collegamento diverso."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Azioni rapide"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Scorciatoie di Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Scorciatoie di Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Aggiungi collegamenti alle tue schede aperte."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rapida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Cerca in Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Cerca in Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Cerca in scheda anonima"; diff --git a/firefox-ios/Shared/ja.lproj/Default Browser.strings b/firefox-ios/Shared/ja.lproj/Default Browser.strings index 0cceec534764b..bccf717052b0c 100644 --- a/firefox-ios/Shared/ja.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ja.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox をデフォルトウェブブラウザーにすることは、オープンでアクセシブルなインターネットに一票を投じることです。"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia をデフォルトウェブブラウザーにすることは、オープンでアクセシブルなインターネットに一票を投じることです。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "より良いインターネットを選択する"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "手順を見る"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "ウェブサイト、メール、メッセージのリンクを自動的に Firefox で開くように設定します。"; +"DefaultBrowserCard.Description" = "ウェブサイト、メール、メッセージのリンクを自動的に Ecosia で開くように設定します。"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "速さと安全性、プライバシー保護を自動化するには、Firefox をデフォルトウェブブラウザーに選んでください。"; +"DefaultBrowserCard.NextLevel.Description.v108" = "速さと安全性、プライバシー保護を自動化するには、Ecosia をデフォルトウェブブラウザーに選んでください。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "日々のブラウジングを向上させる"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox はユーザー一人当たり毎月平均 3000 件以上のトラッカーをブロックしています。プライバシー保護と安心のためにデフォルトウェブブラウザーに設定してください。"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia はユーザー一人当たり毎月平均 3000 件以上のトラッカーをブロックしています。プライバシー保護と安心のためにデフォルトウェブブラウザーに設定してください。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox にはプライバシー保護機能があります"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia にはプライバシー保護機能があります"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "デフォルトブラウザーを切り替えましょう"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. デフォルトブラウザーアプリをタップ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox を選択してください"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia を選択してください"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "デフォルトブラウザーアプリ"; diff --git a/firefox-ios/Shared/ja.lproj/Intro.strings b/firefox-ios/Shared/ja.lproj/Intro.strings index 2313251650180..20d7fb0215149 100644 --- a/firefox-ios/Shared/ja.lproj/Intro.strings +++ b/firefox-ios/Shared/ja.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ログイン"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox へようこそ"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia へようこそ"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ブラウジングをはじめる"; diff --git a/firefox-ios/Shared/ja.lproj/Localizable.strings b/firefox-ios/Shared/ja.lproj/Localizable.strings index be062e249796c..0ddbc36e86f57 100644 --- a/firefox-ios/Shared/ja.lproj/Localizable.strings +++ b/firefox-ios/Shared/ja.lproj/Localizable.strings @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox ホームページ"; +"Firefox.HomePage.Title" = "Ecosia ホームページ"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "ホームページをカスタマイズ"; @@ -377,7 +377,7 @@ "Forward" = "進む"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox アカウント"; +"FxA.FirefoxAccount" = "Ecosia アカウント"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "アカウントと端末の管理"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "カメラを使ってログイン"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "コンピューターの Firefox で firefox.com/pair を開いてください"; +"fxa.signin.qr-link-instruction" = "コンピューターの Ecosia で ecosia.org/pair を開いてください"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "スキャンの準備ができました"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "今すぐ同期"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync が %@ に接続しました"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync が %@ に接続しました"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync に接続しました"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ の接続が正常に解除されました。"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "この端末は Firefox Sync から正常に切断されました。"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "この端末は Ecosia Sync から正常に切断されました。"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync から切断されました"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync から切断されました"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "端末のひとつが Firefox Sync から切断されました"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "端末のひとつが Ecosia Sync から切断されました"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox ロゴ、壁紙を変更します。"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia ロゴ、壁紙を変更します。"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ヘルプ"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "他の端末で開いているタブの一覧を表示するにはログインしてください。"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "履歴"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "詳細情報"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox の自動入力機能を使用するには、端末のパスコードを有効にする必要があります。"; +"Logins.PasscodeRequirement.Warning" = "Ecosia の自動入力機能を使用するには、端末のパスコードを有効にする必要があります。"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "どこでもパスワードが使える"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox でパスワードを自動入力"; +"Logins.WelcomeView.Title2" = "Ecosia でパスワードを自動入力"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "自動入力をオンにする"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "保存されたログイン情報"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox は予期せず終了しました。タブを復元しますか?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia は予期せず終了しました。タブを復元しますか?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "既読にする"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "ブラウジングを開始"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Firefox の動作に問題が発生し、不正終了してしまいました"; +"Oops! Firefox crashed" = "Ecosia の動作に問題が発生し、不正終了してしまいました"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "タイトルバーに表示されたブックアイコンをタップすると、記事をリーダービューで開きます。"; @@ -905,7 +905,7 @@ "Open Tabs" = "タブを開く"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "アドレスが正しくないため、Firefox はこのページを開けません。"; +"OpenURL.Error.Message" = "アドレスが正しくないため、Ecosia はこのページを開けません。"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "ページを開けません"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "これは、画像を Camera Roll に保存することを許可します。"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox が写真データにアクセスしようとしています"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia が写真データにアクセスしようとしています"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "閉じる"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "データが無効です"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "端末の [設定] > [Firefox] を開き、Firefox にカメラへのアクセスを許可してください。"; +"ScanQRCode.PermissionError.Message.v100" = "端末の [設定] > [Ecosia] を開き、Ecosia にカメラへのアクセスを許可してください。"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR コードを読み取り"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "タブを表示"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "新規検索エンジンはクイック検索バーに表示されます。"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "タブを送る"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "この Firefox アカウントに接続された同期可能な端末はありません。"; +"SendTo.NoDevicesFound.Message" = "この Ecosia アカウントに接続された同期可能な端末はありません。"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "続けるには、Firefox を開いて環境設定からログインしてください。"; +"SendTo.NotSignedIn.Message" = "続けるには、Ecosia を開いて環境設定からログインしてください。"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Firefox アカウントにログインしていません。"; +"SendTo.NotSignedIn.Title" = "Ecosia アカウントにログインしていません。"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "送信"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "タップして開始する"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "表示"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "データ管理"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox はあなたのアカウントとの同期を中止しますが、この端末上の閲覧履歴は消去されません。"; +"Settings.Disconnect.Body" = "Ecosia はあなたのアカウントとの同期を中止しますが、この端末上の閲覧履歴は消去されません。"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync との接続を解除"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync 設定"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox アカウント"; +"Settings.FxA.Title" = "Ecosia アカウント"; /* General settings section title */ "Settings.General.SectionName" = "一般"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "ホームページに表示するものを選んでください。"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox ホームページに表示するものを選んでください。"; +"Settings.Home.Option.Description.v101" = "Ecosia ホームページに表示するものを選んでください。"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "以前表示したタブ"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "ホームページ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox へ戻った時に表示するものを選んでください。"; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia へ戻った時に表示するものを選んでください。"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "最後のタブ"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "壁紙"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox の壁紙、アメジストパターンです。"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia の壁紙、アメジストパターンです。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox の壁紙、砂浜の丘パターンです。"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia の壁紙、砂浜の丘パターンです。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox の壁紙、セルリアンパターンです。"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia の壁紙、セルリアンパターンです。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "デフォルトのすっきりした壁紙です。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox の壁紙、サンライズパターンです。"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia の壁紙、サンライズパターンです。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "ホームページの壁紙サイクルを切り替えます"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox の壁紙、夕暮れの丘パターンです。"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia の壁紙、夕暮れの丘パターンです。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ の定番"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "限定版"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox ホームページのロゴをタップして壁紙を変更する"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia ホームページのロゴをタップして壁紙を変更する"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "壁紙"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "カスタム"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox ホーム"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia ホーム"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ホームページ"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "画像をブロック"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox 起動時"; +"Settings.OfferClipboardBar.Status" = "Ecosia 起動時"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@ を開くとき"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox 起動時"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia 起動時"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "コピーしたリンクを開くか尋ねる"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "詳細情報。"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla は Firefox を皆さんに提供し改善するために必要な情報だけを収集するよう努めています。"; +"Settings.SendUsage.Message" = "Ecosia は Ecosia を皆さんに提供し改善するために必要な情報だけを収集するよう努めています。"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "利用統計データを送信"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "新規タブを開く"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri ショートカットを使用して Siri から Firefox を素早く開きましょう"; +"Settings.Siri.SectionDescription" = "Siri ショートカットを使用して Siri から Ecosia を素早く開きましょう"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri ショートカット"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "詳細情報"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox が調査をインストールして実行することがあります。"; +"Settings.Studies.Toggle.Message" = "Ecosia が調査をインストールして実行することがあります。"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "調査"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "バックグラウンドで読み込む"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox で読み込んでいます"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia で読み込んでいます"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox で開く"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia で開く"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox で検索"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia で検索"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "コピー"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "貼り付けて移動"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox で検索"; +"UIMenuItem.SearchWithFirefox" = "Ecosia で検索"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "未読"; @@ -1793,7 +1793,7 @@ "Yesterday" = "昨日"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "他の端末の Firefox で開いているタブはありません。"; +"You don’t have any tabs open in Firefox on your other devices." = "他の端末の Ecosia で開いているタブはありません。"; /* Your Rights settings section title */ "Your Rights" = "あなたの権利"; diff --git a/firefox-ios/Shared/ja.lproj/Today.strings b/firefox-ios/Shared/ja.lproj/Today.strings index 92d0cc2efcb1d..94883326d7a6e 100644 --- a/firefox-ios/Shared/ja.lproj/Today.strings +++ b/firefox-ios/Shared/ja.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "プライベートタブを閉じる"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "ホーム画面に Firefox のショートカットを追加します。"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "ホーム画面に Ecosia のショートカットを追加します。"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "コピーしたリンクを開く"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "開いたタブはありません。"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox を開く"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia を開く"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "プライベート検索"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Firefox のショートカットをホーム画面に追加します。ウィジェットを追加したら、長押しして編集し、別のショートカットを選択します。"; +"TodayWidget.QuickActionGalleryDescription" = "Ecosia のショートカットをホーム画面に追加します。ウィジェットを追加したら、長押しして編集し、別のショートカットを選択します。"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "クイックアクション"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox ショートカット"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia ショートカット"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "開いているタブへのショートカットを追加します。"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "クイックビュー"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox で検索"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia で検索"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "プライベートタブで検索"; diff --git a/firefox-ios/Shared/jv.lproj/Localizable.strings b/firefox-ios/Shared/jv.lproj/Localizable.strings index 7560866fe4de8..ccbc7954e8fb9 100644 --- a/firefox-ios/Shared/jv.lproj/Localizable.strings +++ b/firefox-ios/Shared/jv.lproj/Localizable.strings @@ -224,7 +224,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Mungkin ana ralat konfigurasi utawa rusak déning panyerang. Bacutaké yèn sampéyan nrima risikoné."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Wong sing duwèni %@ bar ngatur situsé kang ora bener. Kanggo ngamanaké informasi sampéyan bèn ora dicolong, Firefox medhot konèksi menyang situs iki."; +"ErrorPages.CertWarning.Description" = "Wong sing duwèni %@ bar ngatur situsé kang ora bener. Kanggo ngamanaké informasi sampéyan bèn ora dicolong, Ecosia medhot konèksi menyang situs iki."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Sambungan Iki ora Dipercaya"; @@ -245,13 +245,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Tepas Firefox"; +"Firefox.HomePage.Title" = "Tepas Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Bacutaké"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Akun Firefox"; +"FxA.FirefoxAccount" = "Akun Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Atur Akun & Piranti"; @@ -263,7 +263,7 @@ "FxA.SyncNow" = "Sync Saiki"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Sync Firefox wis nyambung karo %@"; +"FxAPush_DeviceConnected_body" = "Sync Ecosia wis nyambung karo %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Nyambung"; @@ -272,7 +272,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ wis suksès dipedhot."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Piranti iki suksès pisah saka Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Piranti iki suksès pisah saka Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync Dipedhot"; @@ -281,7 +281,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync Dipedhot"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Piranti wis dipedhot saka Sync Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Piranti wis dipedhot saka Sync Ecosia"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pitulung"; @@ -314,7 +314,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Mlebu kanggo ndeleng dhaptar tab saka piranti liyané."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Riwayat"; @@ -431,7 +431,7 @@ "LoginsList.Title" = "LOG MLEBU KASIMPEN"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox kayané bar mandheg. Apa sampéyan arep mbalèni ngemot tab sampéyan?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia kayané bar mandheg. Apa sampéyan arep mbalèni ngemot tab sampéyan?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Tengeri minangka Wis Diwaca"; @@ -512,7 +512,7 @@ "Okay" = "Oké"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Dhuh! Firefoxé mogok"; +"Oops! Firefox crashed" = "Dhuh! Ecosiaé mogok"; /* See http://mzl.la/1G7uHo7 */ "Open Settings" = "Bukak Setélan"; @@ -599,13 +599,13 @@ "SendTo.NavBar.Title" = "Kirim Tab"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Sampèyan durung mlebu menyang Akun Firefox."; +"SendTo.NotSignedIn.Title" = "Sampèyan durung mlebu menyang Akun Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Kirim"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Deleng"; @@ -677,7 +677,7 @@ "Settings.FxA.DeviceName" = "Jeneng Piranti"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Akun Firefox"; +"Settings.FxA.Title" = "Akun Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Umum"; @@ -686,7 +686,7 @@ "Settings.HomePage.SectionName" = "Kaca Utama"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Home"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Kaca Utama"; @@ -701,10 +701,10 @@ "Settings.NewTab.TopSectionName" = "Tuduhaké"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Nalika Mbukak Firefox"; +"Settings.OfferClipboardBar.Status" = "Nalika Mbukak Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Nalika Mbukak Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Nalika Mbukak Ecosia"; /* Label used as an item in Settings. When touched it will open a dialog to configure the open with (mail links) behavior. */ "Settings.OpenWith.SectionName" = "Aplikasi Mail"; @@ -743,13 +743,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Amot ing Latar"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Ngamot ing Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ngamot ing Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Bukak ing Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Bukak ing Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Golèk ing Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Golèk ing Ecosia"; /* Accessibility Label for the tab toolbar Stop button */ "Stop" = "Lèrèn"; diff --git a/firefox-ios/Shared/ka.lproj/Default Browser.strings b/firefox-ios/Shared/ka.lproj/Default Browser.strings index 48664bd8ac937..a7849ca9dcea2 100644 --- a/firefox-ios/Shared/ka.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ka.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox-ის ნაგულისხმევ ბრაუზერად დაყენება არის ღია, ხელმისაწვდომი ინტერნეტის მხარდაჭერა."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia-ის ნაგულისხმევ ბრაუზერად დაყენება არის ღია, ხელმისაწვდომი ინტერნეტის მხარდაჭერა."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "ნაგულისხმევად უკეთესი ინტერნეტი"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "იხილეთ, როგორ"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "შედეგად ბმულებს საიტებიდან, ელფოსტიდან, წერილებიდან, პირდაპირ გახსნის Firefox."; +"DefaultBrowserCard.Description" = "შედეგად ბმულებს საიტებიდან, ელფოსტიდან, წერილებიდან, პირდაპირ გახსნის Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "აირჩიეთ Firefox ნაგულისხმევ ბრაუზერად თავისთავადი სიჩქარის, უსაფრთხოებისა და პირადულობისთვის."; +"DefaultBrowserCard.NextLevel.Description.v108" = "აირჩიეთ Ecosia ნაგულისხმევ ბრაუზერად თავისთავადი სიჩქარის, უსაფრთხოებისა და პირადულობისთვის."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "შეუფერხებელი ინტერნეტმოგზაურობა"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ზღუდავს საშუალოდ 3000+ მეთვალყურეს ყოველთვიურად. უზრუნველყოფს ინტერნეტის უსაფრთხო და მშვიდ მოხმარებას."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ზღუდავს საშუალოდ 3000+ მეთვალყურეს ყოველთვიურად. უზრუნველყოფს ინტერნეტის უსაფრთხო და მშვიდ მოხმარებას."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox იცავს პირადულობას"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia იცავს პირადულობას"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "აქციეთ მთავარ ბრაუზერად"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. შეეხეთ ნაგულისხმევ ბრაუზერს"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. აირჩიეთ Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. აირჩიეთ Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "ნაგულისხმევი ბრაუზერი"; diff --git a/firefox-ios/Shared/ka.lproj/Intro.strings b/firefox-ios/Shared/ka.lproj/Intro.strings index 61826af201d95..b975c09fecaea 100644 --- a/firefox-ios/Shared/ka.lproj/Intro.strings +++ b/firefox-ios/Shared/ka.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "შესვლა"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "მოგესალმებათ Firefox"; +"Intro.Slides.Welcome.Title.v2" = "მოგესალმებათ Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "დაიწყეთ მონახულება"; diff --git a/firefox-ios/Shared/ka.lproj/Localizable.strings b/firefox-ios/Shared/ka.lproj/Localizable.strings index 7b461df8cae2a..a878d80d60cca 100644 --- a/firefox-ios/Shared/ka.lproj/Localizable.strings +++ b/firefox-ios/Shared/ka.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "შესაძლოა არაა სწორად გამართული, ან თავდამსხმელმა ხელყო. განაგრძეთ, საკუთარი რისკის ფასად."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@-ის მფლობელს არასათანადოდ აქვს ვებსაიტი გამართული. ინფორმაციის მოპარვისგან დაცვის მიზნით, Firefox ამ საიტს არ დაუკავშირდება."; +"ErrorPages.CertWarning.Description" = "%@-ის მფლობელს არასათანადოდ აქვს ვებსაიტი გამართული. ინფორმაციის მოპარვისგან დაცვის მიზნით, Ecosia ამ საიტს არ დაუკავშირდება."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "კავშირი სანდო არაა"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox საწყისი გვერდი"; +"Firefox.HomePage.Title" = "Ecosia საწყისი გვერდი"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "საწყისი გვერდის მორგება"; @@ -377,7 +377,7 @@ "Forward" = "მომდევნო"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-ანგარიში"; +"FxA.FirefoxAccount" = "Ecosia-ანგარიში"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ანგარიშის და მოწყობილობების მართვა"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "შედით კამერის მეშვეობით"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "გაუშვით კომპიუტერში Firefox და გახსენით გვერდი firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "გაუშვით კომპიუტერში Ecosia და გახსენით გვერდი ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "მზადაა წასაკითხად"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "დასინქრონება ახლავე"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync დაკავშირებულია %@-სთან"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync დაკავშირებულია %@-სთან"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync დაკავშირებულია"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ წარმატებით გამოითიშა."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "მოწყობილობა წარმატებით გამოითიშა Firefox Sync-იდან."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "მოწყობილობა წარმატებით გამოითიშა Ecosia Sync-იდან."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "დასინქრონება გათიშულია"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "დასინქრონება გათიშულია"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "მოწყობილობა გამოთიშულია Firefox-სინქრონიზაციიდან"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "მოწყობილობა გამოთიშულია Ecosia-სინქრონიზაციიდან"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-ლოგო, ფონის შეცვლა."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-ლოგო, ფონის შეცვლა."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "დახმარება"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "შედით თქვენი სხვა მოწყობილობებიდან ჩანართების სიის სანახავად."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ისტორია"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "ვრცლად"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "თვითშევსებით სარგებლობისთვის Firefox-ში, ჩართული უნდა გქონდეთ მოწყობილობის კოდი."; +"Logins.PasscodeRequirement.Warning" = "თვითშევსებით სარგებლობისთვის Ecosia-ში, ჩართული უნდა გქონდეთ მოწყობილობის კოდი."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "თან წაიყოლეთ თქვენი პაროლები ყველგან"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "პაროლების თვითშევსება Firefox-ში"; +"Logins.WelcomeView.Title2" = "პაროლების თვითშევსება Ecosia-ში"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "თვითშევსების ჩართვა"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "შენახული ანგარიშები"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "როგორც ჩანს, Firefox წინა ჯერზე უეცრად გაითიშა. გსურთ, ჩანართების აღდგენა?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "როგორც ჩანს, Ecosia წინა ჯერზე უეცრად გაითიშა. გსურთ, ჩანართების აღდგენა?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "წაკითხულად მონიშვნა"; @@ -890,7 +890,7 @@ "Onboarding.WhatsNew.Button.Title" = "დაიწყეთ მოგზაურობა"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ჰმ! Firefox უეცრად გაითიშა"; +"Oops! Firefox crashed" = "ჰმ! Ecosia უეცრად გაითიშა"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "სტატიების კითხვის რეჟიმში გასახსნელად შეეხეთ წიგნის ნიშანს, როცა მისამართების ველში გამოჩნდება."; @@ -902,7 +902,7 @@ "Open Tabs" = "გახსნილი ჩანართები"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ვერ ახერხებს გვერდის გახსნას, რადგან არასწორი მისამართი აქვს."; +"OpenURL.Error.Message" = "Ecosia ვერ ახერხებს გვერდის გახსნას, რადგან არასწორი მისამართი აქვს."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "გვერდი ვერ იხსნება"; @@ -914,7 +914,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "ამის საშუალებით, შეგეძლებათ სურათის ფოტოგალერეაში შენახვა."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ცდილობს თქვენს სურათებთან წვდომის მიღებას"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ცდილობს თქვენს სურათებთან წვდომის მიღებას"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "დახურვა"; @@ -1001,7 +1001,7 @@ "ScanQRCode.InvalidDataError.Message" = "მონაცემი არასწორია"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "გადადით მოწყობილობის „პარამეტრებში“ > „Firefox“. დართეთ Firefox-ს კამერასთან წვდომის ნება."; +"ScanQRCode.PermissionError.Message.v100" = "გადადით მოწყობილობის „პარამეტრებში“ > „Ecosia“. დართეთ Ecosia-ს კამერასთან წვდომის ნება."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR კოდის წაკითხვა"; @@ -1016,7 +1016,7 @@ "Search.Awesomebar.SwitchToTab" = "ჩანართზე გადასვლა"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-შეთავაზება"; +"Search.SuggestSectionTitle.v102" = "Ecosia-შეთავაზება"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "ახალი საძიებო სისტემა გამოჩნდება, სწრაფი ძიების ველში."; @@ -1052,7 +1052,7 @@ "Search.ThirdPartyEngines.OK" = "კარგი"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "მოგვახსენებთ, უეცარი გათიშვის შესახებ, რომ Mozilla-მ შეძლოს ხარვეზის აღმოფხვრა?"; +"Send a crash report so Mozilla can fix the problem?" = "მოგვახსენებთ, უეცარი გათიშვის შესახებ, რომ Ecosia-მ შეძლოს ხარვეზის აღმოფხვრა?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "გამოხმაურება"; @@ -1082,13 +1082,13 @@ "SendTo.NavBar.Title" = "ჩანართის გაგზავნა"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Firefox-ანგარიშთან, არცერთი სხვა მოწყობილობა არ გაქვთ დაკავშირებული, დასინქრონებისთვის."; +"SendTo.NoDevicesFound.Message" = "Ecosia-ანგარიშთან, არცერთი სხვა მოწყობილობა არ გაქვთ დაკავშირებული, დასინქრონებისთვის."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "გასაგრძელებლად გთხოვთ, გახსნათ Firefox, გადახვიდეთ პარამეტრებზე და შეხვიდეთ ანგარიშზე."; +"SendTo.NotSignedIn.Message" = "გასაგრძელებლად გთხოვთ, გახსნათ Ecosia, გადახვიდეთ პარამეტრებზე და შეხვიდეთ ანგარიშზე."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "თქვენ არ ხართ შესული Firefox-ანგარიშზე."; +"SendTo.NotSignedIn.Title" = "თქვენ არ ხართ შესული Ecosia-ანგარიშზე."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "გაგზავნა"; @@ -1109,7 +1109,7 @@ "SentTab.NoTabArrivingNotification.body" = "შეეხეთ დასაწყებად"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ჩვენება"; @@ -1169,7 +1169,7 @@ "Settings.DataManagement.Title" = "მონაცემთა მართვა"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox შეწყვეტს ამ ანგარიშის სინქრონიზაციას, მაგრამ ამ მოწყობილობიდან დათვალიერების მონაცემები არ წაიშლება."; +"Settings.Disconnect.Body" = "Ecosia შეწყვეტს ამ ანგარიშის სინქრონიზაციას, მაგრამ ამ მოწყობილობიდან დათვალიერების მონაცემები არ წაიშლება."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "გამოთიშვა სინქრონიზაციიდან"; @@ -1235,7 +1235,7 @@ "Settings.FxA.Sync.SectionName" = "სინქრონიზაციის პარამეტრები"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-ანგარიში"; +"Settings.FxA.Title" = "Ecosia-ანგარიში"; /* General settings section title */ "Settings.General.SectionName" = "მთავარი"; @@ -1244,7 +1244,7 @@ "Settings.Home.Current.Description.v101" = "აირჩიეთ, რა გამოჩნდეს საწყის გვერდზე."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "აირჩიეთ, რა დაემატოს Firefox-ის საწყის გვერდზე."; +"Settings.Home.Option.Description.v101" = "აირჩიეთ, რა დაემატოს Ecosia-ის საწყის გვერდზე."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "წინაზე გადასვლა"; @@ -1268,7 +1268,7 @@ "Settings.Home.Option.StartAtHome.Always" = "საწყისი გვერდი"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "აირჩიეთ, რა გამოჩნდეს Firefox-ში დაბრუნებისას."; +"Settings.Home.Option.StartAtHome.Description" = "აირჩიეთ, რა გამოჩნდეს Ecosia-ში დაბრუნებისას."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "ბოლო ჩანართი"; @@ -1283,25 +1283,25 @@ "Settings.Home.Option.Wallpaper" = "ფონი"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-ფონი, ამეთვისტოს მოხატულობა."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-ფონი, ამეთვისტოს მოხატულობა."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-ფონი, სანაპირო მაღლობების მოხატულობა."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-ფონი, სანაპირო მაღლობების მოხატულობა."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-ფონი, ლაჟვარდოვანი მოხატულობა."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-ფონი, ლაჟვარდოვანი მოხატულობა."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "ნაგულისხმევი სუფთა ფონი."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox-ფონი, რიჟრაჟის მოხატულობა."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia-ფონი, რიჟრაჟის მოხატულობა."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "საწყისი გვერდის ფონების წრიულად ცვლა"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-ფონი, შებინდებული მაღლობების მოხატულობა."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-ფონი, შებინდებული მაღლობების მოხატულობა."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "ჩვეული %@"; @@ -1322,7 +1322,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "შეზღუდული გამოცემა"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "შეცვალეთ ფონი Firefox-ის საწყისი გვერდის ნიშანზე შეხებით"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "შეცვალეთ ფონი Ecosia-ის საწყისი გვერდის ნიშანზე შეხებით"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "ფონი"; @@ -1376,7 +1376,7 @@ "Settings.NewTab.Option.Custom" = "მორგებული"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox საწყისი"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia საწყისი"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "საწყისი გვერდი"; @@ -1397,10 +1397,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "სურათების შეზღუდვა"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox-ის გახსნისას"; +"Settings.OfferClipboardBar.Status" = "Ecosia-ის გახსნისას"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox-ის გახსნისას"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia-ის გახსნისას"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "აღებულ ბმულებზე გადასვლის შემოთავაზება"; @@ -1436,7 +1436,7 @@ "Settings.SendUsage.Link" = "ვრცლად."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla აღრიცხავს მხოლოდ იმ მონაცემებს, რომლებიც საჭიროა Firefox-ის გასაუმჯობესებლად."; +"Settings.SendUsage.Message" = "Ecosia აღრიცხავს მხოლოდ იმ მონაცემებს, რომლებიც საჭიროა Ecosia-ის გასაუმჯობესებლად."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "მონაცემთა აღრიცხვა"; @@ -1457,7 +1457,7 @@ "Settings.Siri.OpenTabShortcut" = "ახალი ჩანართის გახსნა"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri-ს მალსახმობების გამოყენება, Firefox-ის სწრაფად გახსნისთვის"; +"Settings.Siri.SectionDescription" = "Siri-ს მალსახმობების გამოყენება, Ecosia-ის სწრაფად გახსნისთვის"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-ს მალსახმობები"; @@ -1466,7 +1466,7 @@ "Settings.Studies.Toggle.Link" = "იხილეთ ვრცლად."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox დროდადრო აყენებს და ატარებს კვლევებს."; +"Settings.Studies.Toggle.Message" = "Ecosia დროდადრო აყენებს და ატარებს კვლევებს."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "კვლევები"; @@ -1583,13 +1583,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ფონურად ჩატვირთვა"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "იტვირთება Firefox-ში"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "იტვირთება Ecosia-ში"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox-ში გახსნა"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia-ში გახსნა"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox-ში მოძიება"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia-ში მოძიება"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "ასლი"; @@ -1736,7 +1736,7 @@ "UIMenuItem.PasteGo" = "ჩასმა და გადასვლა"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox-ში მოძიება"; +"UIMenuItem.SearchWithFirefox" = "Ecosia-ში მოძიება"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "წასაკითხი"; @@ -1784,7 +1784,7 @@ "Yesterday" = "გუშინ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "თქვენ არ გაქვთ, Firefox-ის გახსნილი ჩანართები, სხვა მოწყობილობებზე."; +"You don’t have any tabs open in Firefox on your other devices." = "თქვენ არ გაქვთ, Ecosia-ის გახსნილი ჩანართები, სხვა მოწყობილობებზე."; /* Your Rights settings section title */ "Your Rights" = "თქვენი უფლებები"; diff --git a/firefox-ios/Shared/ka.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ka.lproj/PrivateBrowsing.strings index 72f759fba1295..0ae6ff3720520 100644 --- a/firefox-ios/Shared/ka.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ka.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "გახსნა ახალ პირად ჩანართში"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox არ დაიმახსოვრებს მონახულებულ გვერდებსა და ფუნთუშებს, მაგრამ ბოლოს ჩანიშნული გვერდები შენარჩუნდება."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia არ დაიმახსოვრებს მონახულებულ გვერდებსა და ფუნთუშებს, მაგრამ ბოლოს ჩანიშნული გვერდები შენარჩუნდება."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "ვრცლად"; diff --git a/firefox-ios/Shared/ka.lproj/Today.strings b/firefox-ios/Shared/ka.lproj/Today.strings index 0bf78f1d5bbe3..73f9ca68e3340 100644 --- a/firefox-ios/Shared/ka.lproj/Today.strings +++ b/firefox-ios/Shared/ka.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "პირადი ჩანართების\nდახურვა"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "დაამატეთ სხვადასხვა Firefox-მალსახმობი მთავარ ეკრანზე."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "დაამატეთ სხვადასხვა Ecosia-მალსახმობი მთავარ ეკრანზე."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "აღებულ ბმულზე გადასვლა"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "გახსნილი ჩანართები არაა."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "გახსენით Firefox"; +"TodayWidget.OpenFirefoxLabel" = "გახსენით Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "პირადი ძიება"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "დაამატეთ Firefox-მალსახმობი მთავარ ეკრანზე. დამატების შემდგომ, დიდხანს შეეხეთ, ჩასასწორებლად ან სხვა მალსახმობის შესარჩევად."; +"TodayWidget.QuickActionGalleryDescription" = "დაამატეთ Ecosia-მალსახმობი მთავარ ეკრანზე. დამატების შემდგომ, დიდხანს შეეხეთ, ჩასასწორებლად ან სხვა მალსახმობის შესარჩევად."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "სწრაფი მოქმედებები"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox მალსახმობები"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia მალსახმობები"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "გახსნილი ჩანართების მალსახმობების დამატება."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "სწრაფი ხილვა"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox-ში მოძიება"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia-ში მოძიება"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "პირად ჩანართში\nმოძიება"; diff --git a/firefox-ios/Shared/kab.lproj/Default Browser.strings b/firefox-ios/Shared/kab.lproj/Default Browser.strings index 97db296238432..1b70c18a296ac 100644 --- a/firefox-ios/Shared/kab.lproj/Default Browser.strings +++ b/firefox-ios/Shared/kab.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Tiririt n Firefox d iminig-ik·im s wudem amezwer, d afran n Internet yeldin i yal yiwen."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Tiririt n Ecosia d iminig-ik·im s wudem amezwer, d afran n Internet yeldin i yal yiwen."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "S wudem amezwer ɣer Internet ifazen"; @@ -11,16 +11,16 @@ "DefaultBrowserCard.Description" = "Sbadu iseɣwan seg yismal web, seg yimaylen d yiznan i twaledyawt s wudem awurman deg Firefo."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Fren Firefox d iminig amezwer i wakken ad yuɣal arured, taɣellist d uqadar n tudert tabaḍnit s wudem awurman."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Fren Ecosia d iminig amezwer i wakken ad yuɣal arured, taɣellist d uqadar n tudert tabaḍnit s wudem awurman."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Snerni tunigin-ik·im n yal ass"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox issewḥal ugar n 3,000 yineḍfaren i useqdac deg wayyur. Err-it d iminig-ik·imamezwer i uḍman n tudert-ik·im tabaḍnit d lehna n uqerru."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia issewḥal ugar n 3,000 yineḍfaren i useqdac deg wayyur. Err-it d iminig-ik·imamezwer i uḍman n tudert-ik·im tabaḍnit d lehna n uqerru."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox yettqadar tudert-ik·im tabaḍnit"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia yettqadar tudert-ik·im tabaḍnit"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Beddel iminig-ik·im amezwer"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Sit ɣef usnas n yiminig amezwer"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Fren Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Fren Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Asnas n yiminig amezwer"; diff --git a/firefox-ios/Shared/kab.lproj/Intro.strings b/firefox-ios/Shared/kab.lproj/Intro.strings index e5717b0b434d7..ce1deabe72a9c 100644 --- a/firefox-ios/Shared/kab.lproj/Intro.strings +++ b/firefox-ios/Shared/kab.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Kcem"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Anṣuf ar Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Anṣuf ar Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Bdu tunigin"; diff --git a/firefox-ios/Shared/kab.lproj/Localizable.strings b/firefox-ios/Shared/kab.lproj/Localizable.strings index 8d15d58f4a659..16f0d6334d50a 100644 --- a/firefox-ios/Shared/kab.lproj/Localizable.strings +++ b/firefox-ios/Shared/kab.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Ahat d tawila ur nelhi ara neɣ d aɛraḍ n ukellex sɣur aẓeddam. Kemmel ma yella tegziḍ uguren i izemren ad ḍrun."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Imawlan n %@ ur sewlen ara asmel-nsen web akken iwata. Akken ad tettummesten teɣlut-inek seg takwerḍa, Firefox ur itteqqen ara ɣer usmel-a web."; +"ErrorPages.CertWarning.Description" = "Imawlan n %@ ur sewlen ara asmel-nsen web akken iwata. Akken ad tettummesten teɣlut-inek seg takwerḍa, Ecosia ur itteqqen ara ɣer usmel-a web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Tuqqna-a ur tettwaman ara"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Asebter agejdan n Firefox"; +"Firefox.HomePage.Title" = "Asebter agejdan n Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Sagen asebter agejdan"; @@ -377,7 +377,7 @@ "Forward" = "Ɣer zdat"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Amiḍan Firefox"; +"FxA.FirefoxAccount" = "Amiḍan Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Sefrek amiḍan & ibenkan"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Kcem s tkamirat-ik"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Deg uselkim-ik ldi Firefox terzuḍ ɣer firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Deg uselkim-ik ldi Ecosia terzuḍ ɣer ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Iheyya i usnirem"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Mtawi tura"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox yeqqen ar %@"; +"FxAPush_DeviceConnected_body" = "Ecosia yeqqen ar %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Synch yeqqen"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ur yeqqin ara."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ibenk-a yeffeɣ akken iwata seg tuqqna n Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ibenk-a yeffeɣ akken iwata seg tuqqna n Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Synch yeffeɣ seg tuqqna"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Synch yeffeɣ seg tuqqna"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ibenk yeffeɣ si tuqqna n Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ibenk yeffeɣ si tuqqna n Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Alugu n Firefox, beddel agilal."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Alugu n Ecosia, beddel agilal."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Tallalt"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Qqen akken ad d-tsekneḍ tabdart n waccaren seg yibenkan-ik-nniḍen."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Amazray"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Issin ugar"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "I useqdec n tmahilt AutoFill i Firefox, ilaq ad tesεuḍ tangalt n uεeddi n yibenk yettwarmed."; +"Logins.PasscodeRequirement.Warning" = "I useqdec n tmahilt AutoFill i Ecosia, ilaq ad tesεuḍ tangalt n uεeddi n yibenk yettwarmed."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Awi awalen uffiren anda teddiḍ"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Taččart tawurmant n wawalen uffiren n Firefox"; +"Logins.WelcomeView.Title2" = "Taččart tawurmant n wawalen uffiren n Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Rmed taččart tawurmant"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "A yettban dakken Firefox yeɣli yakan. Tebɣiḍ ad d-terreḍ icarren-inek?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "A yettban dakken Ecosia yeɣli yakan. Tebɣiḍ ad d-terreḍ icarren-inek?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Creḍ amzun yettwaɣra"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Bdu tunigin"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ayhu! Firefox yeɣli"; +"Oops! Firefox crashed" = "Ayhu! Ecosia yeɣli"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Ad d-yeldi imagraden deg tmeẓri n umeɣri s usenned ɣef tignit n udlis ticki tban-d deg ufeggag n uzwel."; @@ -905,7 +905,7 @@ "Open Tabs" = "Lddi icarren"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ur yezmir ara ad yeldi asebter acku tansa-nni ur tmeɣtu ara."; +"OpenURL.Error.Message" = "Ecosia ur yezmir ara ad yeldi asebter acku tansa-nni ur tmeɣtu ara."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Ur yezmir ara ad yeldi asebter"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Aya ad isireg asekles n tugna ɣer uzmam n tkamiṛat."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox yebɣa ad yekcem ɣer tewlafin-inek"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia yebɣa ad yekcem ɣer tewlafin-inek"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Mdel"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Isefka arameγtu"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Rzu ɣer 'Iɣewwaṛen' n yibenk > 'Firefox’. Sireg Firefox akken ad yekcem ɣer tkamirat."; +"ScanQRCode.PermissionError.Message.v100" = "Rzu ɣer 'Iɣewwaṛen' n yibenk > 'Ecosia’. Sireg Ecosia akken ad yekcem ɣer tkamirat."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Semḍen Tangalt QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Uɣal ɣer yiccer"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox isumer-d"; +"Search.SuggestSectionTitle.v102" = "Ecosia isumer-d"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Amsedday n unadi amaynut ad d-iban deg ufeggag n unadi arurad."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Ih"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Azen aneqqis n uɣelluy akken Mozilla ad tefru ugur?"; +"Send a crash report so Mozilla can fix the problem?" = "Azen aneqqis n uɣelluy akken Ecosia ad tefru ugur?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Azen tikti"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Azen iccer"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Ur ɣur-k ara ibenkan-nniḍen yeqqnen ɣer umiḍan-inek Firefox u i iwejden i usemtawi."; +"SendTo.NoDevicesFound.Message" = "Ur ɣur-k ara ibenkan-nniḍen yeqqnen ɣer umiḍan-inek Ecosia u i iwejden i usemtawi."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Ma ulac aɣilif ldi Firefox, ddu ɣer yiɣewwaren sakin kcem akken ad tkemmled."; +"SendTo.NotSignedIn.Message" = "Ma ulac aɣilif ldi Ecosia, ddu ɣer yiɣewwaren sakin kcem akken ad tkemmled."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Aql-ak ur teqqineḍ ara ɣer umiḍan-inek Firefox."; +"SendTo.NotSignedIn.Title" = "Aql-ak ur teqqineḍ ara ɣer umiḍan-inek Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Azen"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Senned akken ad tebḍuḍ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Sken"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Asefrek n isefka"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ad iseḥbes amtawi d umiḍan-inek, acukan ur ittekkes ara isefka-inek n tunigin seg uselkim-a."; +"Settings.Disconnect.Body" = "Ecosia ad iseḥbes amtawi d umiḍan-inek, acukan ur ittekkes ara isefka-inek n tunigin seg uselkim-a."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Ffeɣ si Sych"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Iγewwaṛen n umtawi"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Amiḍan Firefox"; +"Settings.FxA.Title" = "Amiḍan Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Amatu"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Fren acu ara d-yettwaskanen am usebter agejdan."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Fren acu ara yeddun ɣef usebter agejdan n Firefox."; +"Settings.Home.Option.Description.v101" = "Fren acu ara yeddun ɣef usebter agejdan n Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Uɣal ɣer deffir"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Asebter agejdan"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Fren acu ara d-ibanen mi ara tuɣaleḍ ad teldiḍ Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Fren acu ara d-ibanen mi ara tuɣaleḍ ad teldiḍ Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Iccer aneggaru"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Tugna n ugilal"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tugna n ugilal n Firefox, taneɣruft n amethyst."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tugna n ugilal n Ecosia, taneɣruft n amethyst."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tugna n ugilal n Firefox, taneɣruft n tewririn n teftist."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tugna n ugilal n Ecosia, taneɣruft n tewririn n teftist."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tugna n ugilal n Firefox, taneɣruft cerulean."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tugna n ugilal n Ecosia, taneɣruft cerulean."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Agilal aceɛlal amezwer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tugna n ugilal n Firefox, taneɣruft n walluy n yiṭij."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tugna n ugilal n Ecosia, taneɣruft n walluy n yiṭij."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Adrurem n twinest n tugna n ugilal n usebter agejdan"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tugna n ugilal n Firefox, taneɣruft n tewririn yilemẓen."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tugna n ugilal n Ecosia, taneɣruft n tewririn yilemẓen."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ aklasik"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Taẓrigt s talast"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Beddel tugna n ugilal s tira n ulugu n usebter agejdan n Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Beddel tugna n ugilal s tira n ulugu n usebter agejdan n Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Tugna n ugilal"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Udmawan"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Asebter agejdan Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Asebter agejdan Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Asebter agejdan"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Sewḥel tugniwin"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Ticki yeldi-d Firefox"; +"Settings.OfferClipboardBar.Status" = "Ticki yeldi-d Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Mi ara yeldi %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Ticki yeldi-d Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Ticki yeldi-d Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Sumer tulya n iseɣwan yettwaneɣlen"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Issin ugar."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla tettennaɣ akken ad telqeḍ kan ayen tesra i usnulfu d usnerni n Firefox i yal yiwen"; +"Settings.SendUsage.Message" = "Ecosia tettennaɣ akken ad telqeḍ kan ayen tesra i usnulfu d usnerni n Ecosia i yal yiwen"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Azen isefka n useqdec"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Ldi iccer amaynut"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Seqdec Siri akken ad teldiḍ Firefox s wudem arurad"; +"Settings.Siri.SectionDescription" = "Seqdec Siri akken ad teldiḍ Ecosia s wudem arurad"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Inegzumen Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Issin ugar."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox yezmer ad isebded yerna ad iseddu leqraya seg wakud ɣer wayeḍ."; +"Settings.Studies.Toggle.Message" = "Ecosia yezmer ad isebded yerna ad iseddu leqraya seg wakud ɣer wayeḍ."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Leqraya"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Sali-d deffir ugilal"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Sali-d deg Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Sali-d deg Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Ldi deg Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ldi deg Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Nadi deg firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Nadi deg Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Nɣel"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Senṭeḍ & Ldi"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Nadi s Firefox"; +"UIMenuItem.SearchWithFirefox" = "Nadi s Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ur yettwaɣri ara"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Iḍelli"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Ur ɣur-k ara icarren yeldin deg Firefox deg yibenkan-inek-nniḍen."; +"You don’t have any tabs open in Firefox on your other devices." = "Ur ɣur-k ara icarren yeldin deg Ecosia deg yibenkan-inek-nniḍen."; /* Your Rights settings section title */ "Your Rights" = "Izerfan-inek"; diff --git a/firefox-ios/Shared/kab.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/kab.lproj/PrivateBrowsing.strings index 34f9c77984d25..9918e14cea2de 100644 --- a/firefox-ios/Shared/kab.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/kab.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Ldi deg iccer uslig amaynut"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ur iceffu ara ɣef umazray akked yinagan n tuqqna-inek, acukan ticraḍ n yisebtar ad ttwakelsent."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ur iceffu ara ɣef umazray akked yinagan n tuqqna-inek, acukan ticraḍ n yisebtar ad ttwakelsent."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Issin ugar"; diff --git a/firefox-ios/Shared/kab.lproj/Today.strings b/firefox-ios/Shared/kab.lproj/Today.strings index ef7b3086ffee7..f107eca4aab72 100644 --- a/firefox-ios/Shared/kab.lproj/Today.strings +++ b/firefox-ios/Shared/kab.lproj/Today.strings @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ulac iccaren yeldin,"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Ldi Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Ldi Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Anadi uslig"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Rnu anegzum n Firefox ɣer ugdil-ik·im agejdan. Seld timerna n uwiǧit, sit s useɣzef i wakken ad t-tẓergeḍ syen ad tferneḍ anegzum-nniḍen."; +"TodayWidget.QuickActionGalleryDescription" = "Rnu anegzum n Ecosia ɣer ugdil-ik·im agejdan. Seld timerna n uwiǧit, sit s useɣzef i wakken ad t-tẓergeḍ syen ad tferneḍ anegzum-nniḍen."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Tigawin tirurad"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Inegzumen n Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Inegzumen n Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Rnu inegzumen ɣer waccaren-ik·im i yeldin."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Taskant taruradt"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Nadi deg firefox"; +"TodayWidget.SearchInFirefoxV2" = "Nadi deg Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Nadi deg yiccer uslig"; diff --git a/firefox-ios/Shared/kk.lproj/Default Browser.strings b/firefox-ios/Shared/kk.lproj/Default Browser.strings index 02bef13acca88..59b41fee44c85 100644 --- a/firefox-ios/Shared/kk.lproj/Default Browser.strings +++ b/firefox-ios/Shared/kk.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox-ты негізгі браузер ету – ашық, қолжетімді интернетке дауыс беру."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia-ты негізгі браузер ету – ашық, қолжетімді интернетке дауыс беру."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Жақсырақ интернет үшін негізгі қылу"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Қалай екенін білу"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Веб-сайттар, эл. пошта хаттары және хабарламалардан сілтемелерді Firefox-та автоматты түрде ашылатындай етіп баптау."; +"DefaultBrowserCard.Description" = "Веб-сайттар, эл. пошта хаттары және хабарламалардан сілтемелерді Ecosia-та автоматты түрде ашылатындай етіп баптау."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Жылдамдықты, қауіпсіздікті және жекелікті автоматты ету үшін Firefox браузерін неігізгі браузер ретінде таңдаңыз."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Жылдамдықты, қауіпсіздікті және жекелікті автоматты ету үшін Ecosia браузерін неігізгі браузер ретінде таңдаңыз."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Күнделікті шолуды жоғарылатыңыз"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox орта есеппен ай сайын бір пайдаланушыға 3000+ трекерді блоктайды. Жекелік тыныштығы үшін бізді негізгі браузер етіңіз."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia орта есеппен ай сайын бір пайдаланушыға 3000+ трекерді блоктайды. Жекелік тыныштығы үшін бізді негізгі браузер етіңіз."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox жекелікті қорғайды"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia жекелікті қорғайды"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Негізгі браузерды ауыстыру"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Негізгі браузер қолданбасын шерту"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox таңдау"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia таңдау"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Негізгі браузер қолданбасы"; diff --git a/firefox-ios/Shared/kk.lproj/Intro.strings b/firefox-ios/Shared/kk.lproj/Intro.strings index a0aa263e2e2ca..955167463d8b3 100644 --- a/firefox-ios/Shared/kk.lproj/Intro.strings +++ b/firefox-ios/Shared/kk.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Кіру"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox қолданбасына қош келдіңіз"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia қолданбасына қош келдіңіз"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Шолуды бастау"; diff --git a/firefox-ios/Shared/kk.lproj/Localizable.strings b/firefox-ios/Shared/kk.lproj/Localizable.strings index 0a1917b7e647e..c5f8d2c732577 100644 --- a/firefox-ios/Shared/kk.lproj/Localizable.strings +++ b/firefox-ios/Shared/kk.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Бұл қате баптаулар немесе шабуылшының зиянкес әрекеттері болуы мүмкін. Өз тәуекеліңізге қарай жалғастырыңыз."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ иесі өз сайтын дұрыс емес баптаған. Ақпаратыңызды ұрланудан қорғау үшін, Firefox веб сайтпен байланыспады."; +"ErrorPages.CertWarning.Description" = "%@ иесі өз сайтын дұрыс емес баптаған. Ақпаратыңызды ұрланудан қорғау үшін, Ecosia веб сайтпен байланыспады."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Бұл байланыс сенімсіз"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox үй парағы"; +"Firefox.HomePage.Title" = "Ecosia үй парағы"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Үй бетін баптау"; @@ -377,7 +377,7 @@ "Forward" = "Алға"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox тіркелгісі"; +"FxA.FirefoxAccount" = "Ecosia тіркелгісі"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Тіркелгі және құрылғыларды басқару"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Камерамен кіріңіз"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Компьютеріңізде Firefox ашып, https://firefox.com/pair адресіне өтіңіз"; +"fxa.signin.qr-link-instruction" = "Компьютеріңізде Ecosia ашып, https://ecosia.org/pair адресіне өтіңіз"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Сканерлеуге дайын"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Қазір синхрондау"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox синхрондауға %@ байланысты"; +"FxAPush_DeviceConnected_body" = "Ecosia синхрондауға %@ байланысты"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Синхрондау байланысқан"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ сәтті ажыратылды."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Бұл құрылғы Firefox синхрондауынан ажыратылды."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Бұл құрылғы Ecosia синхрондауынан ажыратылды."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Синхрондау байланыспаған"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Синхрондау байланыспаған"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Құрылғы Firefox синхрондауынан ажыратылды"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Құрылғы Ecosia синхрондауынан ажыратылды"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox логотипі - тұсқағазды өзгерту."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia логотипі - тұсқағазды өзгерту."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Көмек"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Басқа құрылғыларыңыздан беттер тізімдерін қарау үшін кіріңіз."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Тарихы"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Көбірек білу"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox үшін автотолтыру мүмкіндігін пайдалану үшін құрылғының рұқсат коды іске қосылған болуы керек."; +"Logins.PasscodeRequirement.Warning" = "Ecosia үшін автотолтыру мүмкіндігін пайдалану үшін құрылғының рұқсат коды іске қосылған болуы керек."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Парольдеріңізді өзіңізбен бірге алып жүріңіз"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox парольдерін автотолтыру"; +"Logins.WelcomeView.Title2" = "Ecosia парольдерін автотолтыру"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Автотолтыруды іске қосу"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "САҚТАЛҒАН ЛОГИНДЕР"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox алдында құлаған сияқты. Беттерді қалпына келтіруді қалайсыз ба?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia алдында құлаған сияқты. Беттерді қалпына келтіруді қалайсыз ба?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Оқылған ретінде белгілеу"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Шолуды бастау"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Қап! Firefox құлаған"; +"Oops! Firefox crashed" = "Қап! Ecosia құлаған"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Мақалаларды оқу режимінде ашу үшін, атау жолағындағы кітапша таңбашасы пайда болған кезде, оған шертіңіз."; @@ -905,7 +905,7 @@ "Open Tabs" = "Ашық беттер"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox бұл парақты аша алмайды, өйткені оның адресі қате."; +"OpenURL.Error.Message" = "Ecosia бұл парақты аша алмайды, өйткені оның адресі қате."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Парақты ашу мүмкін емес"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Бұл арқылы сіз суретті Фототаспаға сақтай аласыз."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox суреттеріңізге қатынағысы келеді"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia суреттеріңізге қатынағысы келеді"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Жабу"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Деректер жарамсыз"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Құрылғының \"Баптаулар\" > \"Firefox\" бетін ашыңыз. Firefox үшін камераға қатынауға рұқсат етіңіз."; +"ScanQRCode.PermissionError.Message.v100" = "Құрылғының \"Баптаулар\" > \"Ecosia\" бетін ашыңыз. Ecosia үшін камераға қатынауға рұқсат етіңіз."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR кодын сканерлеу"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Бетке ауысу"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox ұсынысы"; +"Search.SuggestSectionTitle.v102" = "Ecosia ұсынысы"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Жаңа іздеу жүйесі жылдам іздеу жолағында көрсетілетін болады."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "ОК"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozilla бұл мәселені шеше алатындай құлау хабарламасын жіберу керек пе?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosia бұл мәселені шеше алатындай құлау хабарламасын жіберу керек пе?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Кері байланыс хабарламасын жіберу"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Бетті жіберу"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Бұл Firefox тіркелгісіне байланысқан және синхрондау үшін қолжетерлік басқа құрылғыларыңыз жоқ."; +"SendTo.NoDevicesFound.Message" = "Бұл Ecosia тіркелгісіне байланысқан және синхрондау үшін қолжетерлік басқа құрылғыларыңыз жоқ."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Жалғастыру үшін Firefox-ты ашып, баптауларға өтіңіз және тіркелгіге кіріңіз."; +"SendTo.NotSignedIn.Message" = "Жалғастыру үшін Ecosia-ты ашып, баптауларға өтіңіз және тіркелгіге кіріңіз."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Сіз өз Firefox тіркелгіңізге кірген жоқсыз."; +"SendTo.NotSignedIn.Title" = "Сіз өз Ecosia тіркелгіңізге кірген жоқсыз."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Жіберу"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Бастау үшін шертіңіз"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox синхрондау"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia синхрондау"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Қарап шығу"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Деректерді басқару"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox тіркелгіңізбен синхрондауды тоқтатады, бірақ, бұл құрылғыда барлық шолу деректері қалады."; +"Settings.Disconnect.Body" = "Ecosia тіркелгіңізбен синхрондауды тоқтатады, бірақ, бұл құрылғыда барлық шолу деректері қалады."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Синхрондаудан ажырату"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Синхрондау баптаулары"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox тіркелгісі"; +"Settings.FxA.Title" = "Ecosia тіркелгісі"; /* General settings section title */ "Settings.General.SectionName" = "Жалпы"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Басты бет ретінде не көрсетілетінін таңдаңыз."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox басты бетінде көретін мазмұнды таңдаңыз."; +"Settings.Home.Option.Description.v101" = "Ecosia басты бетінде көретін мазмұнды таңдаңыз."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Кері оралу"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Үй парағы"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox-қа оралғанда не көретініңізді таңдаңыз."; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia-қа оралғанда не көретініңізді таңдаңыз."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Соңғы бет"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Түсқағаз"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox тұсқағазы, аметист үлгісі."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia тұсқағазы, аметист үлгісі."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox тұсқағазы, жағажай белестерінің үлгісі."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia тұсқағазы, жағажай белестерінің үлгісі."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox тұсқағазы, көгілдір үлгісі."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia тұсқағазы, көгілдір үлгісі."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Бастапқы, ашық тұсқағаз."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox тұсқағазы, күншығыс үлгісі."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia тұсқағазы, күншығыс үлгісі."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Үй беттің тұсқағаз циклін ауыстырғышы"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox тұсқағазы, ымырт белестерінің үлгісі."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia тұсқағазы, ымырт белестерінің үлгісі."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Классикалық %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Шектеулі таралым"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox басты бетінің логотипін түрту арқылы тұсқағазды өзгертіңіз"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia басты бетінің логотипін түрту арқылы тұсқағазды өзгертіңіз"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Түсқағаз"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Таңдауыңызша"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox үй парағы"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia үй парағы"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Үй парағы"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Суреттерді блоктау"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox-ты ашу кезінде"; +"Settings.OfferClipboardBar.Status" = "Ecosia-ты ашу кезінде"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@ ашқанда"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox-ты ашу кезінде"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia-ты ашу кезінде"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Көшіріп алынған сілтемелерді ашуды ұсыну"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Көбірек білу."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla тек Firefox қолданбасын әркім үшін ұсыну және жақсарту мақсатында керек деректерді жинауға тырысады."; +"Settings.SendUsage.Message" = "Ecosia тек Ecosia қолданбасын әркім үшін ұсыну және жақсарту мақсатында керек деректерді жинауға тырысады."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Қолдану деректерін жіберу"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Жаңа бетті ашу"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri арқылы Firefox-ты жылдам ашу үшін Siri жарлықтарын қолдану"; +"Settings.Siri.SectionDescription" = "Siri арқылы Ecosia-ты жылдам ашу үшін Siri жарлықтарын қолдану"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri жарлықтары"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Көбірек білу."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox кейде зерттеулерді орнатып, орындай алады."; +"Settings.Studies.Toggle.Message" = "Ecosia кейде зерттеулерді орнатып, орындай алады."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Зерттеулер"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Фонда жүктеу"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox-та жүктеу"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia-та жүктеу"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox көмегімен ашу"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia көмегімен ашу"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox ішінен іздеу"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia ішінен іздеу"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Көшіріп алу"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Кірістіру және өту"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox көмегімен іздеу"; +"UIMenuItem.SearchWithFirefox" = "Ecosia көмегімен іздеу"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "оқылмаған"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Кеше"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Басқа құрылғыларыңыздағы Firefox-та ашық беттер сізде жоқ."; +"You don’t have any tabs open in Firefox on your other devices." = "Басқа құрылғыларыңыздағы Ecosia-та ашық беттер сізде жоқ."; /* Your Rights settings section title */ "Your Rights" = "Құқықтарыңыз"; diff --git a/firefox-ios/Shared/kk.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/kk.lproj/PrivateBrowsing.strings index afc81a663ab77..48a0bf6e65295 100644 --- a/firefox-ios/Shared/kk.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/kk.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Жаңа жекелік бетте ашу"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox сіздің шолу тарихыңызды немесе cookies сақтамайды, бірақ, жаңа бетбелгілер сақталатын болады."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia сіздің шолу тарихыңызды немесе cookies сақтамайды, бірақ, жаңа бетбелгілер сақталатын болады."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Көбірек білу"; diff --git a/firefox-ios/Shared/kk.lproj/Today.strings b/firefox-ios/Shared/kk.lproj/Today.strings index 2646dee7105f6..279d90e3b1335 100644 --- a/firefox-ios/Shared/kk.lproj/Today.strings +++ b/firefox-ios/Shared/kk.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Жекелік беттерді жабу"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefox жарлықтарын үй экранына қосыңыз"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosia жарлықтарын үй экранына қосыңыз"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Көшірілген сілтемеге өту"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ашық беттер жоқ."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox-ты ашу"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia-ты ашу"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Жеке іздеу"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Firefox жарлығын үй экранына қосыңыз. Виджетті қосқаннан кейін, оны түзету үшін тиіп, басулы ұстаңыз және басқа жарлықты таңдаңыз."; +"TodayWidget.QuickActionGalleryDescription" = "Ecosia жарлығын үй экранына қосыңыз. Виджетті қосқаннан кейін, оны түзету үшін тиіп, басулы ұстаңыз және басқа жарлықты таңдаңыз."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Жылдам әрекеттер"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox жарлықтары"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia жарлықтары"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Ашық беттерге жарлықтарды қосу."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Жылдам қарау"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox ішінен іздеу"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia ішінен іздеу"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Жекелік бетте іздеу"; diff --git a/firefox-ios/Shared/km.lproj/Default Browser.strings b/firefox-ios/Shared/km.lproj/Default Browser.strings index 01ff241915552..b2e9119c9f47c 100644 --- a/firefox-ios/Shared/km.lproj/Default Browser.strings +++ b/firefox-ios/Shared/km.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "ស្វែងយល់​របៀប"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "កំណត់​តំណ​ពី​គេហទំព័រ អ៊ីមែល និង Messages ដើម្បីបើក​នៅក្នុង Firefox ដោយ​ស្វ័យប្រវត្តិ។"; +"DefaultBrowserCard.Description" = "កំណត់​តំណ​ពី​គេហទំព័រ អ៊ីមែល និង Messages ដើម្បីបើក​នៅក្នុង Ecosia ដោយ​ស្វ័យប្រវត្តិ។"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "ប្ដូរ​កម្មវិធី​រុករក​តាមអ៊ីនធឺណិត​លំនាំដើម​របស់​អ្នក"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. ចុច​កម្មវិធី​រុករក​តាម​អ៊ីនធឺណិត​លំនាំដើម"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. ជ្រើសរើស Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. ជ្រើសរើស Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "កម្មវិធី​រុករក​តាម​អ៊ីនធឺណិត​លំនាំដើម"; diff --git a/firefox-ios/Shared/km.lproj/Intro.strings b/firefox-ios/Shared/km.lproj/Intro.strings index 32d4ef02c3a6a..90c551b2f5b60 100644 --- a/firefox-ios/Shared/km.lproj/Intro.strings +++ b/firefox-ios/Shared/km.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ចូល"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "សូម​ស្វាគមន៍​មកកាន់ Firefox"; +"Intro.Slides.Welcome.Title.v2" = "សូម​ស្វាគមន៍​មកកាន់ Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ចាប់ផ្ដើម​រក​មើល"; diff --git a/firefox-ios/Shared/km.lproj/Localizable.strings b/firefox-ios/Shared/km.lproj/Localizable.strings index d46674cc720b2..4bc31b70b0397 100644 --- a/firefox-ios/Shared/km.lproj/Localizable.strings +++ b/firefox-ios/Shared/km.lproj/Localizable.strings @@ -284,7 +284,7 @@ "ErrorPages.AdvancedWarning2.Text" = "វា​អាច​ជា​ការ​កំណត់​រចនាសម្ព័ន្ធ​ខុស ឬ​​​ការ​លួច​ផ្លាស់ប្ដូរ​ដោយ​អ្នក​វាយប្រហារ។ ចាប់ផ្ដើម​ដំណើរការ បើ​អ្នក​ព្រម​ទទួល​ហានិភ័យ​ដ៏​ខ្លាំង​នេះ។"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "ម្ចាស់​នៃ %@ បាន​កំណត់​រចនាសម្ព័ន្ធ​​វេបសាយ​​របស់​ពួកគេ​មិន​សមរម្យ។ ដើម្បី​ការពារ​ព័ត៌មាន​របស់​អ្នក​ពី​ការ​លួច Firefox មិន​បាន​ភ្ជាប់​ទៅ​វេបសាយ​នេះ​ទេ។"; +"ErrorPages.CertWarning.Description" = "ម្ចាស់​នៃ %@ បាន​កំណត់​រចនាសម្ព័ន្ធ​​វេបសាយ​​របស់​ពួកគេ​មិន​សមរម្យ។ ដើម្បី​ការពារ​ព័ត៌មាន​របស់​អ្នក​ពី​ការ​លួច Ecosia មិន​បាន​ភ្ជាប់​ទៅ​វេបសាយ​នេះ​ទេ។"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "ការ​តភ្ជាប់​នេះ​មិន​ត្រូវ​បាន​ជឿ​ទុកចិត្ត​ឡើយ"; @@ -305,7 +305,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "ទំព័រដើម Firefox"; +"Firefox.HomePage.Title" = "ទំព័រដើម Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "កែ​គេហទំព័រ​តាមបំណង"; @@ -314,7 +314,7 @@ "Forward" = "បញ្ជូន​បន្ត"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "គណនី Firefox"; +"FxA.FirefoxAccount" = "គណនី Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "គ្រប់គ្រងគណនី និង​ឧបករណ៍"; @@ -326,7 +326,7 @@ "FxA.SyncNow" = "ធ្វើសមកាលកម្មឥឡូវនេះ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "ការធ្វើសមកាលកម្ម Firefox បានតភ្ជាប់ទៅ %@"; +"FxAPush_DeviceConnected_body" = "ការធ្វើសមកាលកម្ម Ecosia បានតភ្ជាប់ទៅ %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "បានតភ្ជាប់ការធ្វើសមកាលកម្ម"; @@ -335,7 +335,7 @@ "FxAPush_DeviceDisconnected_body" = "បានផ្តាច់ %@ ដោយជោគជ័យ។"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ឧបករណ៍នេះត្រូវបានផ្តាច់ពី Firefox Sync ដោយជោគជ័យ។"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ឧបករណ៍នេះត្រូវបានផ្តាច់ពី Ecosia Sync ដោយជោគជ័យ។"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "បានផ្តាច់ការធ្វើសមកាលកម្ម"; @@ -344,7 +344,7 @@ "FxAPush_DeviceDisconnected_title" = "បានផ្តាច់ការធ្វើសមកាលកម្ម"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ឧបករណ៍មួយបានផ្តាច់ពី Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ឧបករណ៍មួយបានផ្តាច់ពី Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ជំនួយ"; @@ -377,7 +377,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "ចូល ដើម្បី​មើល​បញ្ជី​ផ្ទាំង​ពី​ឧបករណ៍​ផ្សេងៗ​របស់​អ្នក។"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "ការ​ធ្វើ​សមកាលកម្ម Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "ការ​ធ្វើ​សមកាលកម្ម Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ប្រវត្តិ​"; @@ -515,7 +515,7 @@ "LoginsList.Title" = "ការចូល​ដែល​បាន​រក្សាទុក"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox ដូចជា​បាន​គាំង​កាល​ពី​មុន។ តើ​អ្នក​ចង់​ស្ដារ​ផ្ទាំង​របស់​អ្នក​មែន​ទេ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia ដូចជា​បាន​គាំង​កាល​ពី​មុន។ តើ​អ្នក​ចង់​ស្ដារ​ផ្ទាំង​របស់​អ្នក​មែន​ទេ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "សម្គាល់​ថា​អាន"; @@ -638,7 +638,7 @@ "Okay" = "យល់ព្រម"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "អូ! Firefox បាន​គាំង​ហើយ"; +"Oops! Firefox crashed" = "អូ! Ecosia បាន​គាំង​ហើយ"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "បើក​អត្ថបទ​នៅ​ក្នុង​​ទិដ្ឋភាព​អ្នក​អាន​ដោយ​ប៉ះ​រូបតំណាង​សៀវភៅ​នៅ​ពេល​វា​បង្ហាញ​នៅ​ក្នុង​របារ​ចំណងជើង។"; @@ -650,7 +650,7 @@ "Open Tabs" = "បើកផ្ទាំង"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "កម្មវិធី Firefox មិនអាចបើកទំព័របានទេ ដោយសារ​វាមានអាសយដ្ឋានមិនត្រឹមត្រូវ។"; +"OpenURL.Error.Message" = "កម្មវិធី Ecosia មិនអាចបើកទំព័របានទេ ដោយសារ​វាមានអាសយដ្ឋានមិនត្រឹមត្រូវ។"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "មិនអាចបើកទំព័រ​បាន​ទេ"; @@ -662,7 +662,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "វា​អនុញ្ញាត​ឱ្យ​អ្នក​អាច​រក្សាទុក​រូបភាព​ទៅ​ក្នុង​របុំ​កាមេរ៉ា​របស់អ្នក។"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ចង់​ចូល​​​ប្រើ​រូបថត​របស់​អ្នក"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ចង់​ចូល​​​ប្រើ​រូបថត​របស់​អ្នក"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "បិទ"; @@ -776,7 +776,7 @@ "Search.ThirdPartyEngines.OK" = "យល់ព្រម​"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "ផ្ញើ​របាយការណ៍​ជាប់​គាំង ​ដូច្នេះ Mozilla អាច​ដោះស្រាយ​បញ្ហា​បាន?"; +"Send a crash report so Mozilla can fix the problem?" = "ផ្ញើ​របាយការណ៍​ជាប់​គាំង ​ដូច្នេះ Ecosia អាច​ដោះស្រាយ​បញ្ហា​បាន?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "ផ្ញើ​មតិត្រឡប់"; @@ -803,13 +803,13 @@ "SendTo.NavBar.Title" = "ផ្ញើ​ផ្ទាំង"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "អ្នក​មិន​មាន​ឧបករណ៍​ផ្សេង​ណា​មួយ​បាន​ភ្ជាប់​ទៅ​គណនី Firefox ដែល​អាច​ប្រើ ​​ដើម្បី​ធ្វើ​សមកាលកម្ម​​បាននោះ​ទេ។"; +"SendTo.NoDevicesFound.Message" = "អ្នក​មិន​មាន​ឧបករណ៍​ផ្សេង​ណា​មួយ​បាន​ភ្ជាប់​ទៅ​គណនី Ecosia ដែល​អាច​ប្រើ ​​ដើម្បី​ធ្វើ​សមកាលកម្ម​​បាននោះ​ទេ។"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "សូម​បើក​កម្មវិធី Firefox រួច​ទៅ​កាន់​ ការ​កំណត់ និង​ចូល​ដើម្បី​បន្ត។"; +"SendTo.NotSignedIn.Message" = "សូម​បើក​កម្មវិធី Ecosia រួច​ទៅ​កាន់​ ការ​កំណត់ និង​ចូល​ដើម្បី​បន្ត។"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "អ្នក​មិន​បាន​ចូល​ទៅ​គណនី Firefox របស់​អ្នក​ទេ។"; +"SendTo.NotSignedIn.Title" = "អ្នក​មិន​បាន​ចូល​ទៅ​គណនី Ecosia របស់​អ្នក​ទេ។"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ផ្ញើ"; @@ -830,7 +830,7 @@ "SentTab.NoTabArrivingNotification.body" = "ចុច ដើម្បីចាប់ផ្ដើម"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "មើល"; @@ -887,7 +887,7 @@ "Settings.DataManagement.Title" = "ការ​គ្រប់​គ្រង​ទិន្នន័យ"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox នឹង​ឈប់​ធ្វើ​សមកាលកម្ម​ជាមួយ​គណនី​របស់​អ្នក ប៉ុន្តែ​នឹង​មិន​លុប​ទិន្នន័យ​​ដែលរុករក​​​របស់​អ្នក​​​នៅ​លើ​ឧបករណ៍​នេះ​ទេ។"; +"Settings.Disconnect.Body" = "Ecosia នឹង​ឈប់​ធ្វើ​សមកាលកម្ម​ជាមួយ​គណនី​របស់​អ្នក ប៉ុន្តែ​នឹង​មិន​លុប​ទិន្នន័យ​​ដែលរុករក​​​របស់​អ្នក​​​នៅ​លើ​ឧបករណ៍​នេះ​ទេ។"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ផ្តាច់​ការធ្វើ​សមកាលកម្ម"; @@ -950,7 +950,7 @@ "Settings.FxA.Sync.SectionName" = "ធ្វើសមកាលកម្ម​ការកំណត់"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "គណនី Firefox"; +"Settings.FxA.Title" = "គណនី Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "ទូទៅ"; @@ -974,7 +974,7 @@ "Settings.NewTab.Option.BlankPage" = "ទំព័រទទេ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "ទំព័រដើម Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "ទំព័រដើម Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ទំព័រ​ដើម"; @@ -992,10 +992,10 @@ "Settings.NewTab.TopSectionNameFooter" = "ជ្រើសរើសអ្វីដែលត្រូវផ្ទុកនៅពេលបើកផ្ទាំងថ្មី"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "នៅពេលបើក Firefox"; +"Settings.OfferClipboardBar.Status" = "នៅពេលបើក Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "នៅពេលបើក Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "នៅពេលបើក Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "ផ្តល់ជូនឲ្យបើកតំណដែលបានចម្លង"; @@ -1019,7 +1019,7 @@ "Settings.SendUsage.Link" = "ស្វែងយល់បន្ថែម។"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ខិតខំប្រមូលតែអ្វីដែល​យើងត្រូវការដើម្បីផ្តល់ និងធ្វើឱ្យ Firefox ប្រសើរឡើងសម្រាប់អ្នកគ្រប់គ្នា។"; +"Settings.SendUsage.Message" = "Ecosia ខិតខំប្រមូលតែអ្វីដែល​យើងត្រូវការដើម្បីផ្តល់ និងធ្វើឱ្យ Ecosia ប្រសើរឡើងសម្រាប់អ្នកគ្រប់គ្នា។"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ផ្ញើទិន្នន័យការប្រើប្រាស់"; @@ -1040,7 +1040,7 @@ "Settings.Siri.OpenTabShortcut" = "បើក​ផ្ទាំង​ថ្មី"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "ប្រើផ្លូវកាត់ Siri ដើម្បីបើក Firefox តាមរយៈកម្មវិធី Siri"; +"Settings.Siri.SectionDescription" = "ប្រើផ្លូវកាត់ Siri ដើម្បីបើក Ecosia តាមរយៈកម្មវិធី Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "ផ្លូវកាត់ Siri"; @@ -1049,7 +1049,7 @@ "Settings.Studies.Toggle.Link" = "ស្វែងយល់បន្ថែម។"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox អាចតំឡើងនិងដំណើរការការសិក្សាម្តងម្កាល។"; +"Settings.Studies.Toggle.Message" = "Ecosia អាចតំឡើងនិងដំណើរការការសិក្សាម្តងម្កាល។"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "ការសិក្សា"; @@ -1109,13 +1109,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ផ្ទុកនៅផ្ទៃខាងក្រោយ"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "កំពុងផ្ទុកក្នុង Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "កំពុងផ្ទុកក្នុង Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "បើកក្នុង Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "បើកក្នុង Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "ស្វែងរកក្នុង Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "ស្វែងរកក្នុង Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "បង្ហាញ​​សំណើ​ស្វែងរក"; @@ -1214,7 +1214,7 @@ "UIMenuItem.PasteGo" = "ដាក់ចូល និង​ទៅ"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "ស្វែងរកជាមួយ Firefox"; +"UIMenuItem.SearchWithFirefox" = "ស្វែងរកជាមួយ Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "មិន​ទាន់​អាន"; @@ -1241,7 +1241,7 @@ "Yesterday" = "ម្សិលមិញ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "អ្នក​មិន​មាន​ផ្ទាំង​ណា​មួយ​បើក​ក្នុង Firefox លើ​ឧបករណ៍​ផ្សេង​របស់​អ្នក​ទេ។"; +"You don’t have any tabs open in Firefox on your other devices." = "អ្នក​មិន​មាន​ផ្ទាំង​ណា​មួយ​បើក​ក្នុង Ecosia លើ​ឧបករណ៍​ផ្សេង​របស់​អ្នក​ទេ។"; /* Your Rights settings section title */ "Your Rights" = "សិទ្ធិ​របស់​អ្នក"; diff --git a/firefox-ios/Shared/km.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/km.lproj/PrivateBrowsing.strings index da33a6e43197a..3af85afcf6c68 100644 --- a/firefox-ios/Shared/km.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/km.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "បើក​នៅ​ក្នុង​ផ្ទាំង​ឯកជន​ថ្មី"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox នឹង​មិន​ចងចាំ​ប្រវត្តិ ឬ​ខូគី​ណា​មួយ​ឡើយ ប៉ុន្តែ​​ចំណាំ​ថ្មី​នឹង​ត្រូវ​រក្សាទុក។"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia នឹង​មិន​ចងចាំ​ប្រវត្តិ ឬ​ខូគី​ណា​មួយ​ឡើយ ប៉ុន្តែ​​ចំណាំ​ថ្មី​នឹង​ត្រូវ​រក្សាទុក។"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "ស្វែងយល់​បន្ថែម"; diff --git a/firefox-ios/Shared/kn.lproj/Localizable.strings b/firefox-ios/Shared/kn.lproj/Localizable.strings index 5c198d64551ea..08c9284bd73c8 100644 --- a/firefox-ios/Shared/kn.lproj/Localizable.strings +++ b/firefox-ios/Shared/kn.lproj/Localizable.strings @@ -209,7 +209,7 @@ "ErrorPages.Advanced.Button" = "ಸುಧಾರಿತ"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ ನ ಮಾಲಿಕ ಅವರ ಜಾಲತಾಣವನ್ನು ಸರಿಯಾಗಿ ಸಂರಚಿಸಿಲ್ಲ. ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಕಳ್ಳತನದಿಂದ ಸುರಕ್ಷಿತವಾಗಿರಿಸಲು, Firefox ಈ ಜಾಲತಾಣದೊಂದಿಗೆ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ."; +"ErrorPages.CertWarning.Description" = "%@ ನ ಮಾಲಿಕ ಅವರ ಜಾಲತಾಣವನ್ನು ಸರಿಯಾಗಿ ಸಂರಚಿಸಿಲ್ಲ. ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಕಳ್ಳತನದಿಂದ ಸುರಕ್ಷಿತವಾಗಿರಿಸಲು, Ecosia ಈ ಜಾಲತಾಣದೊಂದಿಗೆ ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಲ್ಲ."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "ಈ ಸಂಪರ್ಕ ನಂಬಲರ್ಹವಲ್ಲ"; @@ -221,13 +221,13 @@ "Facebook" = "ಫೇಸ್‌ಬುಕ್‌"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox ಮುಖ್ಯ ಪುಟ"; +"Firefox.HomePage.Title" = "Ecosia ಮುಖ್ಯ ಪುಟ"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "ಮುಂದೆ"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox ಖಾತೆ"; +"FxA.FirefoxAccount" = "Ecosia ಖಾತೆ"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ಖಾತೆ ಮತ್ತು ಸಾಧನಗಳನ್ನು ನಿರ್ವಹಿಸು‍"; @@ -263,7 +263,7 @@ "HistoryPanel.EmptySyncedTabsNullState.Description" = "ನಿಮ್ಮ ಬೇರೆ ಸಾಧನಗಳ ಟ್ಯಾಬ್‍‍ಗಳು ಇಲ್ಲಿ ಕಾಣಿಸಿಕೊಳ್ಳುತ್ತವೆ."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox ಸಿಂಕ್"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia ಸಿಂಕ್"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ಇತಿಹಾಸ"; @@ -446,7 +446,7 @@ "Okay" = "ಸರಿ"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "‍ಓಹ್ ಓ! ‍Firefox ಕುಸಿತಗೊಂಡಿದೆ"; +"Oops! Firefox crashed" = "‍ಓಹ್ ಓ! ‍Ecosia ಕುಸಿತಗೊಂಡಿದೆ"; /* See http://mzl.la/1G7uHo7 */ "Open Settings" = "ಸಿದ್ಧತೆಗಳನ್ನು ತೆರೆ"; @@ -563,7 +563,7 @@ "SentTab.NoTabArrivingNotification.body" = "ಪ್ರಾರಂಭಿಸಲು ಮೆಲ್ಲಗೆ ತಟ್ಟಿ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox ಸಿಂಕ್"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia ಸಿಂಕ್"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ನೋಟ"; @@ -632,7 +632,7 @@ "Settings.FxA.Sync.SectionName" = "ಸಿಂಕ್‍ನ ಸಿದ್ಧತೆಗಳು"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox ಖಾತೆ"; +"Settings.FxA.Title" = "Ecosia ಖಾತೆ"; /* General settings section title */ "Settings.General.SectionName" = "ಸಾಮಾನ್ಯ"; diff --git a/firefox-ios/Shared/ko.lproj/Default Browser.strings b/firefox-ios/Shared/ko.lproj/Default Browser.strings index a4ab9ce53eecd..f66d0a0bfe311 100644 --- a/firefox-ios/Shared/ko.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ko.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox를 기본 브라우저로 설정하는 것은 개방되고 접근 가능한 인터넷에 대한 지원입니다."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia를 기본 브라우저로 설정하는 것은 개방되고 접근 가능한 인터넷에 대한 지원입니다."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "더 나은 인터넷을 기본 값으로 설정"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "방법 알아보기"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Firefox에서 자동으로 열리도록 웹 사이트, 이메일 및 메시지의 링크를 설정합니다."; +"DefaultBrowserCard.Description" = "Ecosia에서 자동으로 열리도록 웹 사이트, 이메일 및 메시지의 링크를 설정합니다."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Firefox를 기본 브라우저로 선택하고 자동으로 속도, 안전 및 개인 정보를 지키세요."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Ecosia를 기본 브라우저로 선택하고 자동으로 속도, 안전 및 개인 정보를 지키세요."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "일상적인 브라우징 향상"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox는 매달 사용자당 평균 3,000개 이상의 트래커를 차단합니다. 안심할 수 있는 개인 정보 보호를 위해 기본 브라우저로 설정하세요."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia는 매달 사용자당 평균 3,000개 이상의 트래커를 차단합니다. 안심할 수 있는 개인 정보 보호를 위해 기본 브라우저로 설정하세요."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox는 개인 정보 보호를 보장합니다."; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia는 개인 정보 보호를 보장합니다."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "기본 브라우저 전환"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. 기본 브라우저 앱을 탭"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox 선택"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia 선택"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "기본 브라우저 앱"; diff --git a/firefox-ios/Shared/ko.lproj/Intro.strings b/firefox-ios/Shared/ko.lproj/Intro.strings index ab67e22c286f6..f5a41b0247c48 100644 --- a/firefox-ios/Shared/ko.lproj/Intro.strings +++ b/firefox-ios/Shared/ko.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "로그인"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox를 선택해 주셔서 감사합니다"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia를 선택해 주셔서 감사합니다"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "사용하기"; diff --git a/firefox-ios/Shared/ko.lproj/Localizable.strings b/firefox-ios/Shared/ko.lproj/Localizable.strings index 4ebae26aeaf47..674e2e3bc7816 100644 --- a/firefox-ios/Shared/ko.lproj/Localizable.strings +++ b/firefox-ios/Shared/ko.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "설정이 잘 못 되었거나 공격자의 간섭일 수 있습니다. 잠재적인 위험을 감수한다면 진행하세요."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@의 소유자가 웹사이트를 적절하게 설정하지 않았습니다. 정보를 보호하기 위해서 Firefox가 웹사이트에 접속을 하지 않았습니다."; +"ErrorPages.CertWarning.Description" = "%@의 소유자가 웹사이트를 적절하게 설정하지 않았습니다. 정보를 보호하기 위해서 Ecosia가 웹사이트에 접속을 하지 않았습니다."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "연결이 안전하지 않음"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox 홈 페이지"; +"Firefox.HomePage.Title" = "Ecosia 홈 페이지"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "홈페이지 개인화"; @@ -377,7 +377,7 @@ "Forward" = "앞으로"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox 계정"; +"FxA.FirefoxAccount" = "Ecosia 계정"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "계정과 기기 관리"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "카메라로 로그인"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "컴퓨터에서 Firefox를 열고 firefox.com/pair 로 이동하세요"; +"fxa.signin.qr-link-instruction" = "컴퓨터에서 Ecosia를 열고 ecosia.org/pair 로 이동하세요"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "스캔 준비 완료"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "지금 동기화"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync가 %@와 연결됨"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync가 %@와 연결됨"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync 연결됨"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@가 성공적으로 연결이 해제되었습니다."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "기기는 Firefox Sync와 연결이 끊어졌습니다."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "기기는 Ecosia Sync와 연결이 끊어졌습니다."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "동기화 연결끊김"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "동기화 연결끊김"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "기기가 Firefox Sync와 연결이 끊김"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "기기가 Ecosia Sync와 연결이 끊김"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox 로고, 배경 화면 변경"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia 로고, 배경 화면 변경"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "도움말"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "다른 기기의 탭 목록을 보기 위해서 로그인 하세요."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "방문목록"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "자세히 알아보기"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox의 자동 완성 기능을 사용하려면 기기 암호가 활성화되어 있어야 합니다."; +"Logins.PasscodeRequirement.Warning" = "Ecosia의 자동 완성 기능을 사용하려면 기기 암호가 활성화되어 있어야 합니다."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "어디에서나 비밀번호를 사용하세요."; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox 비밀번호 자동 완성"; +"Logins.WelcomeView.Title2" = "Ecosia 비밀번호 자동 완성"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "자동 완성 켜기"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "저장된 로그인"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "이전에 Firefox가 강제 종료되었습니다. 탭을 복구하시겠습니까?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "이전에 Ecosia가 강제 종료되었습니다. 탭을 복구하시겠습니까?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "읽음으로 표시"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "사용하기"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "이런! Firefox가 종료됐습니다."; +"Oops! Firefox crashed" = "이런! Ecosia가 종료됐습니다."; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "제목줄 옆에 나오는 책모양 아이콘을 눌러서 읽기 모드로 글을 열어보세요."; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "카메라 롤에 사진을 저장할 수 있습니다."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox가 사진 정보에 접근하고자 합니다."; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia가 사진 정보에 접근하고자 합니다."; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "닫기"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "올바르지 않은 데이터"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "기기의 ‘설정’ > ‘Firefox’에서 Firefox가 카메라에 접근할 수 있게 허용해 주세요."; +"ScanQRCode.PermissionError.Message.v100" = "기기의 ‘설정’ > ‘Ecosia’에서 Ecosia가 카메라에 접근할 수 있게 허용해 주세요."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR 코드 스캔"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "탭으로 전환"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox 제안"; +"Search.SuggestSectionTitle.v102" = "Ecosia 제안"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "새 검색 엔진이 빠른 검색창에 나타날 것입니다."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "확인"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozilla가 문제를 고칠 수 있도록 보고서를 전송하시겠습니까?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosia가 문제를 고칠 수 있도록 보고서를 전송하시겠습니까?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "의견 보내기"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "탭 보내기"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "이 Firefox Account에 동기화 할 수 있는 다른 기기가 연결되어 있지 않습니다."; +"SendTo.NoDevicesFound.Message" = "이 Ecosia Account에 동기화 할 수 있는 다른 기기가 연결되어 있지 않습니다."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Firefox를 열고 설정으로 이동 한 다음 로그인하여 계속하세요."; +"SendTo.NotSignedIn.Message" = "Ecosia를 열고 설정으로 이동 한 다음 로그인하여 계속하세요."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Firefox Account에 로그인하지 않았습니다."; +"SendTo.NotSignedIn.Title" = "Ecosia Account에 로그인하지 않았습니다."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "보내기"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "탭해서 시작"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "보기"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "데이터 관리"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox가 계정의 동기화를 중지하지만 기기의 브라우징 데이터는 삭제하지 않습니다."; +"Settings.Disconnect.Body" = "Ecosia가 계정의 동기화를 중지하지만 기기의 브라우징 데이터는 삭제하지 않습니다."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "동기화 끊기"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "동기화 설정"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox 계정"; +"Settings.FxA.Title" = "Ecosia 계정"; /* General settings section title */ "Settings.General.SectionName" = "일반"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "홈페이지로 표시할 항목을 선택합니다."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox 홈페이지에 포함된 것을 선택하십시오."; +"Settings.Home.Option.Description.v101" = "Ecosia 홈페이지에 포함된 것을 선택하십시오."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "다시 돌아오기"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "홈페이지"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox로 돌아왔을 때 표시할 항목을 선택하세요."; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia로 돌아왔을 때 표시할 항목을 선택하세요."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "마지막 탭"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "배경 화면"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox 배경화면, 자수정 패턴"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia 배경화면, 자수정 패턴"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox 배경화면, 비치 힐 패턴."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia 배경화면, 비치 힐 패턴."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox 배경화면, 세룰리안 패턴"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia 배경화면, 세룰리안 패턴"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "기본 클리어 배경화면"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox 배경화면, 일출 패턴"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia 배경화면, 일출 패턴"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "홈페이지 배경화면 순환 토글"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox 배경화면, 트와일라잇 힐 패턴."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia 배경화면, 트와일라잇 힐 패턴."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "클래식 %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "한정판"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox 홈페이지 로고를 눌러서 배경화면을 변경"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia 홈페이지 로고를 눌러서 배경화면을 변경"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "배경 화면"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "사용자 정의"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox 홈"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia 홈"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "홈페이지"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "자세히 알아보기"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla는 Firefox를 개선하기 위한 정보만 수집하기 위해 노력합니다."; +"Settings.SendUsage.Message" = "Ecosia는 Ecosia를 개선하기 위한 정보만 수집하기 위해 노력합니다."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "사용현황 데이터 보내기"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "새 탭 열기"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri를 통해 Firefox를 빠르게 열기 위한 Siri 단축키 사용"; +"Settings.Siri.SectionDescription" = "Siri를 통해 Ecosia를 빠르게 열기 위한 Siri 단축키 사용"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri 단축키"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "자세히 알아보기"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox는 때때로 연구를 설치하고 실행할 수 있습니다."; +"Settings.Studies.Toggle.Message" = "Ecosia는 때때로 연구를 설치하고 실행할 수 있습니다."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "연구"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "백그라운드에서 로드"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox에서 로딩"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia에서 로딩"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox에서 열기"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia에서 열기"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox에서 검색"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia에서 검색"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "복사"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "붙여넣고 바로 가기"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox에서 검색"; +"UIMenuItem.SearchWithFirefox" = "Ecosia에서 검색"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "않읽음"; diff --git a/firefox-ios/Shared/ko.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ko.lproj/PrivateBrowsing.strings index 1dd6b59416221..cf3c8be2d48f9 100644 --- a/firefox-ios/Shared/ko.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ko.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "새 사생활 보호 탭에서 열기"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox가 탐색 기록이나 쿠키는 저장하지 않지만 신규 북마크는 저장합니다."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia가 탐색 기록이나 쿠키는 저장하지 않지만 신규 북마크는 저장합니다."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "자세히 보기"; diff --git a/firefox-ios/Shared/ko.lproj/Today.strings b/firefox-ios/Shared/ko.lproj/Today.strings index 3912b4365001d..5205bda02fb1a 100644 --- a/firefox-ios/Shared/ko.lproj/Today.strings +++ b/firefox-ios/Shared/ko.lproj/Today.strings @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "열린 탭이 없습니다."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox 열기"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia 열기"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "사생활 보호 검색"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "홈 화면에 Firefox 바로 가기를 추가하세요. 위젯을 추가한 후, 길게 터치하여 수정 및 다른 바로 가기를 선택하세요."; +"TodayWidget.QuickActionGalleryDescription" = "홈 화면에 Ecosia 바로 가기를 추가하세요. 위젯을 추가한 후, 길게 터치하여 수정 및 다른 바로 가기를 선택하세요."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "빠른 작업"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox 단축키"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia 단축키"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "열려있는 탭에 바로 가기를 추가합니다."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "빠르게 보기"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox에서 검색"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia에서 검색"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "사생활 보호 탭에서 검색"; diff --git a/firefox-ios/Shared/lo.lproj/Default Browser.strings b/firefox-ios/Shared/lo.lproj/Default Browser.strings index cb940b8949c82..0c92a9e6573e7 100644 --- a/firefox-ios/Shared/lo.lproj/Default Browser.strings +++ b/firefox-ios/Shared/lo.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "ການເຮັດໃຫ້ Firefox ເປັນຕົວທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານເປັນການລົງຄະແນນສຽງສໍາລັບອິນເຕີເນັດທີ່ເປີດ, ເຂົ້າເຖິງໄດ້."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "ການເຮັດໃຫ້ Ecosia ເປັນຕົວທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານເປັນການລົງຄະແນນສຽງສໍາລັບອິນເຕີເນັດທີ່ເປີດ, ເຂົ້າເຖິງໄດ້."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "ຄ່າເລີ່ມຕົ້ນເປັນອິນເຕີເນັດທີ່ດີກວ່າ"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "ຮຽນຮູ້ວິທີການ"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "ຕັ້ງໃຫ້ເປີດລີ້ງຈາກເວັບໄຊທ, ອີເມລ ແລະ ຂໍ້ຄວາມແບບອັດຕະໂນມັດໃນ Firefox."; +"DefaultBrowserCard.Description" = "ຕັ້ງໃຫ້ເປີດລີ້ງຈາກເວັບໄຊທ, ອີເມລ ແລະ ຂໍ້ຄວາມແບບອັດຕະໂນມັດໃນ Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "ເລືອກ Firefox ເປັນຕົວທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານເພື່ອເຮັດໃຫ້ຄວາມໄວ, ຄວາມປອດໄພ, ແລະຄວາມເປັນສ່ວນຕົວອັດຕະໂນມັດ."; +"DefaultBrowserCard.NextLevel.Description.v108" = "ເລືອກ Ecosia ເປັນຕົວທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານເພື່ອເຮັດໃຫ້ຄວາມໄວ, ຄວາມປອດໄພ, ແລະຄວາມເປັນສ່ວນຕົວອັດຕະໂນມັດ."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "ຍົກລະດັບການຊອກຫາປະຈໍາວັນ"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ບລັອກ 3,000+ ຕົວຕິດຕາມຕໍ່ຜູ້ໃຊ້ໃນແຕ່ລະເດືອນໂດຍສະເລ່ຍ. ເຮັດໃຫ້ພວກເຮົາເປັນຕົວທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານເພື່ອຄວາມສະຫງົບດ້ານຄວາມເປັນສ່ວນຕົວຂອງຈິດໃຈ."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ບລັອກ 3,000+ ຕົວຕິດຕາມຕໍ່ຜູ້ໃຊ້ໃນແຕ່ລະເດືອນໂດຍສະເລ່ຍ. ເຮັດໃຫ້ພວກເຮົາເປັນຕົວທ່ອງເວັບເລີ່ມຕົ້ນຂອງທ່ານເພື່ອຄວາມສະຫງົບດ້ານຄວາມເປັນສ່ວນຕົວຂອງຈິດໃຈ."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ມີການປົກຫຸ້ມຂອງຄວາມເປັນສ່ວນຕົວ"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ມີການປົກຫຸ້ມຂອງຄວາມເປັນສ່ວນຕົວ"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "ປ່ຽນບຣາວເຊີຫລັກຂອງທ່ານ"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. ແຕະໃສ່ແອັບບຣາວເຊີຫລັກ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. ເລືອກ Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. ເລືອກ Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "ບຣາວເຊີຫລັກ"; diff --git a/firefox-ios/Shared/lo.lproj/Intro.strings b/firefox-ios/Shared/lo.lproj/Intro.strings index 57ce86777dbf2..6c65a5fd4c1d7 100644 --- a/firefox-ios/Shared/lo.lproj/Intro.strings +++ b/firefox-ios/Shared/lo.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ເຂົ້າ​ສູ່​ລະ​ບົບ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "ຍິນດີຕ້ອນຮັບສູ່ Firefox"; +"Intro.Slides.Welcome.Title.v2" = "ຍິນດີຕ້ອນຮັບສູ່ Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ເລີ່ມການທ່ອງເວັບ"; diff --git a/firefox-ios/Shared/lo.lproj/Localizable.strings b/firefox-ios/Shared/lo.lproj/Localizable.strings index de606a50095fe..cb44290845e47 100644 --- a/firefox-ios/Shared/lo.lproj/Localizable.strings +++ b/firefox-ios/Shared/lo.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "ມັນອາດຈະເກີດມາຈາກການຕັ້ງຄ່າຜິດພາດ ຫລື ເປັນການດັດແປງໂດຍນັກເຈາະຂໍ້ມູນ. ດຳເນີນການຕໍ່ໄປຖ້າຫາກວ່າທ່ານຍອມຮັບຄວາມສ່ຽງທີ່ອາດເກີດຂື້ນນີ້ໄດ້."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "ເຈົ້າຂອງ %@ ໄດ້ຕັ້ງຄ່າເວັບໄຊທ໌ຂອງພວກເຂົາບໍ່ເຫມາະສົມ. ເພື່ອເປັນການປົກປ້ອງຂໍ້ມູນຂອງທ່ານຈາກການຖືກໂຈລະກຳ, Firefox ຈຶ່ງໄດ້ຕັດການເຊື່ອມຕໍ່ໄປຫາເວັບໄຊທ໌ນີ້."; +"ErrorPages.CertWarning.Description" = "ເຈົ້າຂອງ %@ ໄດ້ຕັ້ງຄ່າເວັບໄຊທ໌ຂອງພວກເຂົາບໍ່ເຫມາະສົມ. ເພື່ອເປັນການປົກປ້ອງຂໍ້ມູນຂອງທ່ານຈາກການຖືກໂຈລະກຳ, Ecosia ຈຶ່ງໄດ້ຕັດການເຊື່ອມຕໍ່ໄປຫາເວັບໄຊທ໌ນີ້."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "ການເຊື່ອມຕໍ່ນີ້ບໍ່ນ່າເຊື່ອຖື"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "ຫນ້າທຳອິດຂອງ Firefox"; +"Firefox.HomePage.Title" = "ຫນ້າທຳອິດຂອງ Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "ປັບແຕ່ງຫນ້າທຳອິດ"; @@ -377,7 +377,7 @@ "Forward" = "ໄປຂ້າງຫນ້າ"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "ບັນຊີ Firefox"; +"FxA.FirefoxAccount" = "ບັນຊີ Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ຈັດການບັນຊີ ແລະ ອຸປະກອນ"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "ເຂົ້າລະບົບດ້ວຍກ້ອງຂອງທ່ານ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "ເປີດ Firefox ໃນຄອມພິວເຕີຂອງທ່ານ ແລະ ໄປທີ່ firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "ເປີດ Ecosia ໃນຄອມພິວເຕີຂອງທ່ານ ແລະ ໄປທີ່ ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "ກຽມພ້ອມທີ່ຈະສະແກນ"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sync ດຽວນີ້"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync ໄດ້ເຊື່ອມຕໍ່ກັບ %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync ໄດ້ເຊື່ອມຕໍ່ກັບ %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "ເຊື່ອມຕໍກັບ Sync ແລ້ວ"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ໄດ້ຕັດການເຊື່ອມຕໍ່ແລ້ວ."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ອຸປະກອນນີ້ໄດ້ຕັດການເຊື່ອຕໍ່ກັບ Firefox Sync ແລ້ວ."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ອຸປະກອນນີ້ໄດ້ຕັດການເຊື່ອຕໍ່ກັບ Ecosia Sync ແລ້ວ."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ຕັດການເຊື່ອມຕໍກັບ Sync ແລ້ວ"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "ຕັດການເຊື່ອມຕໍກັບ Sync ແລ້ວ"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ອຸປະກອນບໍ່ໄດ້ເຊື່ອມຕໍ່ກັບ Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ອຸປະກອນບໍ່ໄດ້ເຊື່ອມຕໍ່ກັບ Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "ໂລໂກ້ Firefox, ປ່ຽນພາບຫນ້າຈໍ."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "ໂລໂກ້ Ecosia, ປ່ຽນພາບຫນ້າຈໍ."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ວິທີໃຊ້"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "ເຂົ້າສູ່ລະບົບເພື່ອເບິງລາຍການແທັບທີ່ເປີດຢູ່ໃນອຸປະກອນອື່ນໆຂອງທ່ານ."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ປະຫວັດການໃຊ້ງານ"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "ຮຽນຮູ້ເພີ່ມເຕີມ"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "ເພື່ອນຳໃຊ້ການຕື່ມໃສ່ອັດຕະໂນມັດສຳລັບ Firefox ທ່ານຕ້ອງໄດ້ເປີດນຳໃຊ້ພາສໂຄດ."; +"Logins.PasscodeRequirement.Warning" = "ເພື່ອນຳໃຊ້ການຕື່ມໃສ່ອັດຕະໂນມັດສຳລັບ Ecosia ທ່ານຕ້ອງໄດ້ເປີດນຳໃຊ້ພາສໂຄດ."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "ນຳເອົາລະຫັດຜ່ານຂອງທ່ານໄປນຳທຸກບ່ອນ"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "ປ້ອນລະຫັດຜ່ານ Firefox ແບບອັດຕະໂນມັດ"; +"Logins.WelcomeView.Title2" = "ປ້ອນລະຫັດຜ່ານ Ecosia ແບບອັດຕະໂນມັດ"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "ເປີດການຕື່ມໃສ່ອັດຕະໂນມັດ"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ບັກທຶກການເຂົ້າສູ່ລະບົບໄວ້"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ເບິງຄ້າຍຄືວ່າ Firefox ເກີດມີຂໍ້ຜິດພາດກ່ອນຫນ້ານີ້. ທ່ານຕ້ອງການກູ້ຄືນແທັບຂອງທ່ານຫລືບໍ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ເບິງຄ້າຍຄືວ່າ Ecosia ເກີດມີຂໍ້ຜິດພາດກ່ອນຫນ້ານີ້. ທ່ານຕ້ອງການກູ້ຄືນແທັບຂອງທ່ານຫລືບໍ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "ໝາຍໄວ້ວ່າໄດ້ອ່ານແລ້ວ"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "ເລີ່ມການທ່ອງເວັບ"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox ມີຂໍ້ຜິດພາດ"; +"Oops! Firefox crashed" = "Oops! Ecosia ມີຂໍ້ຜິດພາດ"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "ເປີດຫົວຂໍ້ນີ້ໃນ Reader View ໂດຍການເຕະໄອຄອນປື້ມເມື່ອມັນປະກົດຂື້ນໃນ Title bar."; @@ -905,7 +905,7 @@ "Open Tabs" = "ເປີດແທັບ"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ບໍ່ສາມາດເປີດຫນ້ານີ້ໄດ້ເພາະວ່າມັນມີທີ່ຢູ່ທີບໍ່ຖືກຕ້ອງ."; +"OpenURL.Error.Message" = "Ecosia ບໍ່ສາມາດເປີດຫນ້ານີ້ໄດ້ເພາະວ່າມັນມີທີ່ຢູ່ທີບໍ່ຖືກຕ້ອງ."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "ບໍ່ສາມາດເປີດຫນ້ານີ້ໄດ້."; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "ສິ່ງນີ້ອະນຸຍາດໃຫ້ທ່ານບັນທຶກຮູບພາບໄປໄວ້ໃນທີ່ເກັບຮູບພາບຂອງທ່ານ."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ຕ້ອງການເຂົ້າເຖິງຮູບພາບຂອງທ່ານ"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ຕ້ອງການເຂົ້າເຖິງຮູບພາບຂອງທ່ານ"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "ປິດ"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "ຂໍ້ມູນບໍ່ຖືກຕ້ອງ"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "ໄປທີ່ອຸປະກອນ ‘ການຕັ້ງຄ່າ’> ‘Firefox’. ອະນຸຍາດໃຫ້ Firefox ເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ."; +"ScanQRCode.PermissionError.Message.v100" = "ໄປທີ່ອຸປະກອນ ‘ການຕັ້ງຄ່າ’> ‘Ecosia’. ອະນຸຍາດໃຫ້ Ecosia ເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "ສະແກນລະຫັດ QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "ປ່ຽນໄປຫາແທັບ"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox ແນະນໍາ"; +"Search.SuggestSectionTitle.v102" = "Ecosia ແນະນໍາ"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "ເຄື່ອງມືການຄົ້ນຫາໃຫມ່ຈະປະກົດຢູ່ໃນບາຂອງການຄົ້ນຫາ."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "ຕົກລົງ"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "ຕ້ອງການສົ່ງລາຍງານຂໍ້ຜິດພາດນີ້ໄປເພື່ອເຮັດໃຫ້ Mozilla ສາມາດແກ້ໄຂບັນຫານີ້ໄດ້ບໍ?"; +"Send a crash report so Mozilla can fix the problem?" = "ຕ້ອງການສົ່ງລາຍງານຂໍ້ຜິດພາດນີ້ໄປເພື່ອເຮັດໃຫ້ Ecosia ສາມາດແກ້ໄຂບັນຫານີ້ໄດ້ບໍ?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "ສົ່ງຄຳຄິດເຫັນຂອງທ່ານ"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "ສົ່ງແທັບ"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "ທ່ານບໍ່ມີອຸປະກອນໃດໆທີ່ເຊື່ອມຕໍ່ກັບບັນຊີ Firefox ນີ້ຢູ່ເພື່ອສາມາດໃຊ້ການ Sync ໄດ້."; +"SendTo.NoDevicesFound.Message" = "ທ່ານບໍ່ມີອຸປະກອນໃດໆທີ່ເຊື່ອມຕໍ່ກັບບັນຊີ Ecosia ນີ້ຢູ່ເພື່ອສາມາດໃຊ້ການ Sync ໄດ້."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "ກະລຸນາເປີດ Firefox, ໄປທີ່ການຕັ້ງຄ່າ ແລະ ລົງຊື່ເຂົ້າໃຊ້ເພື່ອດຳເນີນການຕໍໄປ."; +"SendTo.NotSignedIn.Message" = "ກະລຸນາເປີດ Ecosia, ໄປທີ່ການຕັ້ງຄ່າ ແລະ ລົງຊື່ເຂົ້າໃຊ້ເພື່ອດຳເນີນການຕໍໄປ."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "ທ່ານຍັງບໍ່ໄດ້ລົງຊື່ເຂົ້າໃຊ້ບັນຊີ Firefox ຂອງທ່ານເທືອ."; +"SendTo.NotSignedIn.Title" = "ທ່ານຍັງບໍ່ໄດ້ລົງຊື່ເຂົ້າໃຊ້ບັນຊີ Ecosia ຂອງທ່ານເທືອ."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ສົ່ງ"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "ແຕະເພື່ອເລີ່ມຕົ້ນ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ມຸມມອງ"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "ການຈັດການຂໍ້ມູນ"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ຈະຢຸດການ Sync ກັບບັນຊີຂອງທ່ານ, ແຕວ່າຈະບໍ່ລຶບປະຫວັດການຄົ້ນຫາຂອງທ່ານໃນອຸປະກອນນີ້."; +"Settings.Disconnect.Body" = "Ecosia ຈະຢຸດການ Sync ກັບບັນຊີຂອງທ່ານ, ແຕວ່າຈະບໍ່ລຶບປະຫວັດການຄົ້ນຫາຂອງທ່ານໃນອຸປະກອນນີ້."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ຕັດການເຊື່ອມຕໍ່"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "ການຕັ້ງຄ່າ Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "ບັນຊີ Firefox"; +"Settings.FxA.Title" = "ບັນຊີ Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "ທົ່ວໄປ"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "ເລືອກສິ່ງທີ່ສະແດງເປັນຫນ້າທໍາອິດ."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "ເລືອກສິ່ງທີ່ຢູ່ໃນຫນ້າທໍາອິດຂອງ Firefox."; +"Settings.Home.Option.Description.v101" = "ເລືອກສິ່ງທີ່ຢູ່ໃນຫນ້າທໍາອິດຂອງ Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "ກັບໄປໃນ"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "ຫນ້າທຳອິດ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "ເລືອກສິ່ງທີ່ທ່ານເຫັນເມື່ອທ່ານກັບຄືນໄປຫາ Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "ເລືອກສິ່ງທີ່ທ່ານເຫັນເມື່ອທ່ານກັບຄືນໄປຫາ Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "ແທັບສຸດທ້າຍ"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "ພາບພື້ນຫຼັງ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "ຮູບວໍເປເປີ Firefox, ຮູບແບບ amethyst."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "ຮູບວໍເປເປີ Ecosia, ຮູບແບບ amethyst."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "ຮູບວໍເປເປີ Firefox, ຮູບແບບເນີນພູຫາດຊາຍ."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "ຮູບວໍເປເປີ Ecosia, ຮູບແບບເນີນພູຫາດຊາຍ."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "ຮູບວໍເປເປີ Firefox, ຮູບແບບ cerulean."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "ຮູບວໍເປເປີ Ecosia, ຮູບແບບ cerulean."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "ພາບພື້ນຫຼັງທີ່ຊັດເຈນເປັນຄ່າເລີ່ມຕົ້ນ."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "ຮູບວໍເປເປີ Firefox, ຮູບແບບຕາເວັນຂຶ້ນ."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "ຮູບວໍເປເປີ Ecosia, ຮູບແບບຕາເວັນຂຶ້ນ."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "ປ່ຽນພາບພື້ນຫຼັງໜ້າທຳອິດແບບໝູນວຽນ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "ຮູບວໍເປເປີ Firefox, ຮູບແບບເນີນພູ twilight."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "ຮູບວໍເປເປີ Ecosia, ຮູບແບບເນີນພູ twilight."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "ຄລາດສິກ %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "ມີ​ຈໍາ​ນວນ​ຈໍາ​ກັດ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "ປ່ຽນຮູບວໍເປເປີໂດຍການແຕະໃສ່ໂລໂກ້ຂອງໜ້າຫຼັກຂອງ Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "ປ່ຽນຮູບວໍເປເປີໂດຍການແຕະໃສ່ໂລໂກ້ຂອງໜ້າຫຼັກຂອງ Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "ພາບພື້ນຫຼັງ"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "ປັບແຕ່ງ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "ຫນ້າຫລັກຂອງ Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "ຫນ້າຫລັກຂອງ Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ຫນ້າທຳອິດ"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "ບັອກຮູບພາບ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "ເມື່ອເປີດ Firefox"; +"Settings.OfferClipboardBar.Status" = "ເມື່ອເປີດ Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "ເມື່ອເປີດ %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "ເມື່ອເປີດ Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "ເມື່ອເປີດ Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "ສະເຫນີໃຫ້ເປີດລີ້ງທີ່ໄດ້ສຳເນົາໄວ້"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "ຮຽນຮູ້ເພີ່ມເຕີມ."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ພະຍາຍາມທີ່ຈະເກັບຮວບຮ່ວມສະເພາະສິ່ງທີ່ຈຳເປັນຕໍ່ການປັບປູງ Firefox ສຳລັບທຸກໆຄົນ."; +"Settings.SendUsage.Message" = "Ecosia ພະຍາຍາມທີ່ຈະເກັບຮວບຮ່ວມສະເພາະສິ່ງທີ່ຈຳເປັນຕໍ່ການປັບປູງ Ecosia ສຳລັບທຸກໆຄົນ."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ສົ່ງຂໍ້ມູນການນຳໃຊ້"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "ເປີດແທັບໃໝ່"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "ໃຊ້ທາງລັດ Siri ເພື່ອເປີດ Firefox ຢ່າງວ່ອງໄວຜ່ານ Siri"; +"Settings.Siri.SectionDescription" = "ໃຊ້ທາງລັດ Siri ເພື່ອເປີດ Ecosia ຢ່າງວ່ອງໄວຜ່ານ Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "ທາງລັດ Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "ຮຽນຮູ້ເພີ່ມເຕີມ."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox ອາດຈະຕິດຕັ້ງ ແລະ ເອີ້ນນຳໃຊ້ການສຶກສາເປັນບາງຄັ້ງ."; +"Settings.Studies.Toggle.Message" = "Ecosia ອາດຈະຕິດຕັ້ງ ແລະ ເອີ້ນນຳໃຊ້ການສຶກສາເປັນບາງຄັ້ງ."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "ການສຶກສາ"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ໂຫລດໃນເບື້ອງຫລັງ"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "ກຳລັງໂຫລດໃນ Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "ກຳລັງໂຫລດໃນ Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "ເປີດໃນ Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "ເປີດໃນ Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "ຊອກຫາໃນ Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "ຊອກຫາໃນ Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "ສຳເນົາ"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "ວາງ & ໄປ"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "ຄົ້ນຫາດ້ວຍ Firefox"; +"UIMenuItem.SearchWithFirefox" = "ຄົ້ນຫາດ້ວຍ Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ຍັງບໍ່ທັນອ່ານ"; @@ -1793,7 +1793,7 @@ "Yesterday" = "ມື້ວານ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "ທ່ານບໍ່ມີແທັບໃດທີ່ເປີດໃນ Firefox ໃນອຸປະກອນອື່ນໆຂອງທ່ານ."; +"You don’t have any tabs open in Firefox on your other devices." = "ທ່ານບໍ່ມີແທັບໃດທີ່ເປີດໃນ Ecosia ໃນອຸປະກອນອື່ນໆຂອງທ່ານ."; /* Your Rights settings section title */ "Your Rights" = "ສິດທິຂອງທ່ານ"; diff --git a/firefox-ios/Shared/lo.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/lo.lproj/PrivateBrowsing.strings index ecad8d54d81a0..c25811da4a100 100644 --- a/firefox-ios/Shared/lo.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/lo.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "ເປີດໃນແທັບສ່ວນໂຕໃຫມ່"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ຈະບໍ່ບັນທຶກທຸກໆປະຫວັດການໃຊ້ງານ ຫລື ຄຸກກີ້ຂອງທ່ານ, ແຕ່ວ່າຈະບັນທຶກບຸກມາກໃຫມ່ໄວ້."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ຈະບໍ່ບັນທຶກທຸກໆປະຫວັດການໃຊ້ງານ ຫລື ຄຸກກີ້ຂອງທ່ານ, ແຕ່ວ່າຈະບັນທຶກບຸກມາກໃຫມ່ໄວ້."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "ຮຽນຮູ້ເພີ່ມເຕີມ"; diff --git a/firefox-ios/Shared/lo.lproj/Today.strings b/firefox-ios/Shared/lo.lproj/Today.strings index 2b967cdd0f245..e5cd0d4ef0923 100644 --- a/firefox-ios/Shared/lo.lproj/Today.strings +++ b/firefox-ios/Shared/lo.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "ປິດແທັບສ່ວນຕົວ"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "ເພີ່ມທາງລັດເຂົ້າ Firefox ໄປຫນ້າຈໍຫລັກຂອງທ່ານ"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "ເພີ່ມທາງລັດເຂົ້າ Ecosia ໄປຫນ້າຈໍຫລັກຂອງທ່ານ"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "ໄປທີ່ລີ້ງໄດ້ກັອບປີ້ໄວ້"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "ບໍ່ມີແທັບທີ່ເປີດຢູ່"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "ເປີດ Firefox"; +"TodayWidget.OpenFirefoxLabel" = "ເປີດ Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "ຄົ້ນຫາແບບສ່ວນຕົວ"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "ເພີ່ມທາງລັດເຂົ້າ Firefox ໄປໄວ້ໃນຫນ້າທຳອິດ. ຫລັງຈາກການເພີ່ມ widget ແລ້ວ ໃຫ້ແຕະຄ້າງໄວ້ເພື່ອແກ້ໄຂ ແລະ ເລືອກທາງລັດອື່ນ."; +"TodayWidget.QuickActionGalleryDescription" = "ເພີ່ມທາງລັດເຂົ້າ Ecosia ໄປໄວ້ໃນຫນ້າທຳອິດ. ຫລັງຈາກການເພີ່ມ widget ແລ້ວ ໃຫ້ແຕະຄ້າງໄວ້ເພື່ອແກ້ໄຂ ແລະ ເລືອກທາງລັດອື່ນ."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "ການກະທຳດ່ວນ"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "ທາງລັດເຂົ້າ Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "ທາງລັດເຂົ້າ Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "ເພີ່ມທາງລັດໄປຫາແທັບທີ່ເປີດຢູ່ຂອງທ່ານ."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "ການເບິງແບບດ່ວນ"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "ຄົ້ນຫາໃນ\nFirefox"; +"TodayWidget.SearchInFirefoxV2" = "ຄົ້ນຫາໃນ\nEcosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "ຄົ້ນຫາໃນ\nແທັບສ່ວນຕົວ"; diff --git a/firefox-ios/Shared/lt.lproj/Default Browser.strings b/firefox-ios/Shared/lt.lproj/Default Browser.strings index 312b1fcd7534c..d26dd32e3da27 100644 --- a/firefox-ios/Shared/lt.lproj/Default Browser.strings +++ b/firefox-ios/Shared/lt.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "Sužinoti kaip"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Leiskite saitams iš svetainių, el. laiškų, ir „Messages“ būti automatiškai atvertiems per „Firefox“."; +"DefaultBrowserCard.Description" = "Leiskite saitams iš svetainių, el. laiškų, ir „Messages“ būti automatiškai atvertiems per „Ecosia“."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Pakeiskite numatytąją naršyklę"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Bakstelėkite „Numatytoji naršyklė“"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Pasirinkite „Firefox“"; +"DefaultBrowserOnboarding.Description3" = "3. Pasirinkite „Ecosia“"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Numatytoji naršyklė"; diff --git a/firefox-ios/Shared/lt.lproj/Intro.strings b/firefox-ios/Shared/lt.lproj/Intro.strings index 2d38f52218c06..b82de1c3be70d 100644 --- a/firefox-ios/Shared/lt.lproj/Intro.strings +++ b/firefox-ios/Shared/lt.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Prisijungti"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Sveiki! Tai „Firefox“"; +"Intro.Slides.Welcome.Title.v2" = "Sveiki! Tai „Ecosia“"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Pradėti naršymą"; diff --git a/firefox-ios/Shared/lt.lproj/Localizable.strings b/firefox-ios/Shared/lt.lproj/Localizable.strings index bbd1b2f41227e..acc2a975505d3 100644 --- a/firefox-ios/Shared/lt.lproj/Localizable.strings +++ b/firefox-ios/Shared/lt.lproj/Localizable.strings @@ -293,7 +293,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Taip gali būti dėl netinkamos konfigūracijos arba piktavalio „kišamų nagų“. Tęskite, jei sutinkate su galima rizika."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ savininkas netinkamai sukonfigūravo savo svetainę. „Firefox“ nesijungė prie šios svetainės, kad apsaugotų jūsų duomenis nuo perėmimo."; +"ErrorPages.CertWarning.Description" = "%@ savininkas netinkamai sukonfigūravo savo svetainę. „Ecosia“ nesijungė prie šios svetainės, kad apsaugotų jūsų duomenis nuo perėmimo."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Šis ryšys nepatikimas"; @@ -314,7 +314,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "„Firefox“ pradžios tinklalapis"; +"Firefox.HomePage.Title" = "„Ecosia“ pradžios tinklalapis"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Tvarkyti pradžios tinklalapį"; @@ -332,7 +332,7 @@ "Forward" = "Pirmyn"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "„Firefox“ paskyra"; +"FxA.FirefoxAccount" = "„Ecosia“ paskyra"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Tvarkyti paskyrą ir įrenginius"; @@ -344,7 +344,7 @@ "fxa.signin.camera-signin" = "Prisijunkite su savo kamera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Atverkite „Firefox“ savo kompiuteryje, ir eikite į firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Atverkite „Ecosia“ savo kompiuteryje, ir eikite į ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pasiruošti nuskaitymui"; @@ -356,7 +356,7 @@ "FxA.SyncNow" = "Sinchronizuoti dabar"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "„Firefox Sync“ prisijungė prie „%@“"; +"FxAPush_DeviceConnected_body" = "„Ecosia Sync“ prisijungė prie „%@“"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "„Sync“ prisijungė"; @@ -365,7 +365,7 @@ "FxAPush_DeviceDisconnected_body" = "„%@“ buvo sėkmingai atjungtas."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Šis įrenginys buvo sėkmingai atjungtas nuo „Firefox Sync“."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Šis įrenginys buvo sėkmingai atjungtas nuo „Ecosia Sync“."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "„Sync“ atsijungė"; @@ -374,7 +374,7 @@ "FxAPush_DeviceDisconnected_title" = "„Sync“ atsijungė"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Įrenginys buvo atjungtas nuo „Firefox Sync“"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Įrenginys buvo atjungtas nuo „Ecosia Sync“"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Žinynas"; @@ -407,7 +407,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Prisijunkite, norėdami peržiūrėti kituose įrenginiuose esančias korteles."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "„Firefox Sync“"; +"HistoryPanel.EmptySyncedTabsState.Title" = "„Ecosia Sync“"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Žurnalas"; @@ -566,13 +566,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Sužinoti daugiau"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Norėdami naudoti automatinio užpildymo funkciją su „Firefox“, turite įjungti įrenginio apsaugos kodą."; +"Logins.PasscodeRequirement.Warning" = "Norėdami naudoti automatinio užpildymo funkciją su „Ecosia“, turite įjungti įrenginio apsaugos kodą."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Turėkite savo slaptažodžius visur"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Automatiškai užpildykite „Firefox“ slaptažodžius"; +"Logins.WelcomeView.Title2" = "Automatiškai užpildykite „Ecosia“ slaptažodžius"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Įjungti automatinį užpildymą"; @@ -617,7 +617,7 @@ "LoginsList.Title" = "ĮRAŠYTI PRISIJUNGIMAI"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Panašu, kad „Firefox“ praėjusį kartą užstrigo. Ar norite atkurti savo korteles?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Panašu, kad „Ecosia“ praėjusį kartą užstrigo. Ar norite atkurti savo korteles?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Pažymėti kaip skaitytą"; @@ -749,7 +749,7 @@ "Onboarding.WhatsNew.Button.Title" = "Pradėti naršymą"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oi! „Firefox“ užstrigo"; +"Oops! Firefox crashed" = "Oi! „Ecosia“ užstrigo"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Atverkite straipsnius skaitymo veiksenoje, bakstelėdami knygos piktogramą, pasirodančią antraštės juostoje."; @@ -761,7 +761,7 @@ "Open Tabs" = "Atvertos kortelės"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "„Firefox“ negali atverti nurodyto tinklalapio, nes jo adresas nėra tinkamas."; +"OpenURL.Error.Message" = "„Ecosia“ negali atverti nurodyto tinklalapio, nes jo adresas nėra tinkamas."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nepavyko atverti tinklalapio."; @@ -773,7 +773,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Tai jums leis paveikslą įrašyti į „Camera Roll“."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "„Firefox“ prašo prieigos prie jūsų nuotraukų"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "„Ecosia“ prašo prieigos prie jūsų nuotraukų"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Užverti"; @@ -899,7 +899,7 @@ "Search.ThirdPartyEngines.OK" = "Gerai"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Siųsti strigties pranešimą, kad „Mozilla“ galėtų išspręsti problemą?"; +"Send a crash report so Mozilla can fix the problem?" = "Siųsti strigties pranešimą, kad „Ecosia“ galėtų išspręsti problemą?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Siųsti atsiliepimą"; @@ -926,13 +926,13 @@ "SendTo.NavBar.Title" = "Nusiųsti kortelę"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Su šia „Firefox“ paskyra nesate susieję jokių kitų sinchronizuoti galinčių įrenginių."; +"SendTo.NoDevicesFound.Message" = "Su šia „Ecosia“ paskyra nesate susieję jokių kitų sinchronizuoti galinčių įrenginių."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Jei norite tęsti, atverkite „Firefox“, eikite į nuostatas ir prisijunkite."; +"SendTo.NotSignedIn.Message" = "Jei norite tęsti, atverkite „Ecosia“, eikite į nuostatas ir prisijunkite."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Jūs nesate prisijungę prie savo „Firefox“ paskyros."; +"SendTo.NotSignedIn.Title" = "Jūs nesate prisijungę prie savo „Ecosia“ paskyros."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Siųsti"; @@ -953,7 +953,7 @@ "SentTab.NoTabArrivingNotification.body" = "Bakstelėkite pradėjimui"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Rodyti"; @@ -1013,7 +1013,7 @@ "Settings.DataManagement.Title" = "Duomenų tvarkymas"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "„Firefox“ nebesinchronizuos duomenų su jūsų paskyra, tačiau šiame įrenginyje esančių naršymo duomenų nepašalins."; +"Settings.Disconnect.Body" = "„Ecosia“ nebesinchronizuos duomenų su jūsų paskyra, tačiau šiame įrenginyje esančių naršymo duomenų nepašalins."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Atjungti „Sync“"; @@ -1076,7 +1076,7 @@ "Settings.FxA.Sync.SectionName" = "Sinchronizavimo nuostatos"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "„Firefox“ paskyra"; +"Settings.FxA.Title" = "„Ecosia“ paskyra"; /* General settings section title */ "Settings.General.SectionName" = "Bendrosios"; @@ -1103,7 +1103,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pradžios tinklalapį"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Pasirinkite, ką matysite sugrįžę į „Firefox“."; +"Settings.Home.Option.StartAtHome.Description" = "Pasirinkite, ką matysite sugrįžę į „Ecosia“."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Paskutinę kortelę"; @@ -1148,7 +1148,7 @@ "Settings.NewTab.Option.BlankPage" = "Tuščias puslapis"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "„Firefox“ pradžios tinklalapis"; +"Settings.NewTab.Option.FirefoxHome" = "„Ecosia“ pradžios tinklalapis"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pradžios tinklalapis"; @@ -1166,10 +1166,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Pasirinkite, ką įkelti atveriant naują kortelę"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Kai atveriama „Firefox“"; +"Settings.OfferClipboardBar.Status" = "Kai atveriama „Ecosia“"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Kai atveriama „Firefox“"; +"Settings.OfferClipboardBar.StatusV2" = "Kai atveriama „Ecosia“"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Siūlyti atverti nukopijuotus saitus"; @@ -1193,7 +1193,7 @@ "Settings.SendUsage.Link" = "Sužinoti daugiau."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "„Mozilla“ siekia rinkti tik tai, ko reikia norint pagerinti „Firefox“ visiems."; +"Settings.SendUsage.Message" = "„Ecosia“ siekia rinkti tik tai, ko reikia norint pagerinti „Ecosia“ visiems."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Siųsti naudojimo duomenis"; @@ -1214,7 +1214,7 @@ "Settings.Siri.OpenTabShortcut" = "Atverti naują kortelę"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Naudoti „Siri“ leistukus norint greitai atverti „Firefox“"; +"Settings.Siri.SectionDescription" = "Naudoti „Siri“ leistukus norint greitai atverti „Ecosia“"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "„Siri“ leistukai"; @@ -1223,7 +1223,7 @@ "Settings.Studies.Toggle.Link" = "Sužinoti daugiau."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "„Firefox“ retkarčiais gali įdiegti ir vykdyti tyrimus."; +"Settings.Studies.Toggle.Message" = "„Ecosia“ retkarčiais gali įdiegti ir vykdyti tyrimus."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Tyrimai"; @@ -1316,13 +1316,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Įkelti fone"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Įkeliama per „Firefox“"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Įkeliama per „Ecosia“"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Atverti per „Firefox“"; +"ShareExtension.OpenInFirefoxAction.Title" = "Atverti per „Ecosia“"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Ieškoti per „Firefox“"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ieškoti per „Ecosia“"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Rodyti paieškos žodžių siūlymus"; @@ -1448,7 +1448,7 @@ "UIMenuItem.PasteGo" = "Įdėti ir atverti"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Ieškoti per „Firefox“"; +"UIMenuItem.SearchWithFirefox" = "Ieškoti per „Ecosia“"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "neskaityta"; @@ -1478,7 +1478,7 @@ "Yesterday" = "Vakar"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Kituose savo įrenginiuose su „Firefox“ neturite atvertų kortelių."; +"You don’t have any tabs open in Firefox on your other devices." = "Kituose savo įrenginiuose su „Ecosia“ neturite atvertų kortelių."; /* Your Rights settings section title */ "Your Rights" = "Jūsų teisės"; diff --git a/firefox-ios/Shared/lt.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/lt.lproj/PrivateBrowsing.strings index ce703c25ba962..7fd24dd7cd743 100644 --- a/firefox-ios/Shared/lt.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/lt.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Atverti naujoje privačioje kortelėje"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "„Firefox“ nekaups jūsų naršymo žurnalo ir neįsimins slapukų, tačiau nauji adresyno įrašai bus išsaugoti."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "„Ecosia“ nekaups jūsų naršymo žurnalo ir neįsimins slapukų, tačiau nauji adresyno įrašai bus išsaugoti."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Sužinoti daugiau"; diff --git a/firefox-ios/Shared/lt.lproj/Today.strings b/firefox-ios/Shared/lt.lproj/Today.strings index b61d79d37982b..0f5bf7df24170 100644 --- a/firefox-ios/Shared/lt.lproj/Today.strings +++ b/firefox-ios/Shared/lt.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Užverti privačiąsias korteles"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Pridėkite „Firefox“ leistukų į savo pradžios ekraną."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Pridėkite „Ecosia“ leistukų į savo pradžios ekraną."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Atverti nukopijuotą saitą"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nėra atvertų kortelių."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Atverti „Firefox“"; +"TodayWidget.OpenFirefoxLabel" = "Atverti „Ecosia“"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privačioji paieška"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Pridėkite „Firefox“ leistuką į savo pradžios ekraną. Pridėję valdiklį, palieskite ir palaikykite norėdami jį keisti, ir pasirinkti kitą leistuką."; +"TodayWidget.QuickActionGalleryDescription" = "Pridėkite „Ecosia“ leistuką į savo pradžios ekraną. Pridėję valdiklį, palieskite ir palaikykite norėdami jį keisti, ir pasirinkti kitą leistuką."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Spartieji veiksmai"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "„Firefox“ leistukai"; +"TodayWidget.QuickActionsGalleryTitleV2" = "„Ecosia“ leistukai"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Pridėkite leistukų į savo atvertas korteles."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Sparti peržiūra"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Ieškoti per „Firefox“"; +"TodayWidget.SearchInFirefoxV2" = "Ieškoti per „Ecosia“"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Atverti privačiojoje kortelėje"; diff --git a/firefox-ios/Shared/lv.lproj/Localizable.strings b/firefox-ios/Shared/lv.lproj/Localizable.strings index 1042e4253a6d3..244aef5ccbedc 100644 --- a/firefox-ios/Shared/lv.lproj/Localizable.strings +++ b/firefox-ios/Shared/lv.lproj/Localizable.strings @@ -206,7 +206,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Tā varētu būt nepareizas uzstādīšanas vai uzbrukuma pazīme. Turpiniet tikai ja jūs pieņemat iespējamo risku."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ īpašnieks ir nepareizi uzstādījis savu mājaslapu. Lai aizsargātu jūsu informāciju no zagšanas, Firefox nav pieslēdzies šai mājaslapai."; +"ErrorPages.CertWarning.Description" = "%@ īpašnieks ir nepareizi uzstādījis savu mājaslapu. Lai aizsargātu jūsu informāciju no zagšanas, Ecosia nav pieslēdzies šai mājaslapai."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Šis savienojums nav drošs"; @@ -224,13 +224,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox sākuma lapu"; +"Firefox.HomePage.Title" = "Ecosia sākuma lapu"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Pārsūtīt"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox konts"; +"FxA.FirefoxAccount" = "Ecosia konts"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Pārvaldīt kontu un ierīces"; @@ -242,7 +242,7 @@ "FxA.SyncNow" = "Sinhronizēt"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync ir pievienots %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync ir pievienots %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync pievienots"; @@ -251,7 +251,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ir veiksmīgi atienota."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Šī ierīce ir veiksmīgi atvienota no Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Šī ierīce ir veiksmīgi atvienota no Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync atvienots"; @@ -260,7 +260,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync atvienots"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ierīce ir atvienota no Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ierīce ir atvienota no Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Palīdzība"; @@ -290,7 +290,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Pierakstieties, lai redzētu šeit cilnes no šīm ierīcēm."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Vēsture"; @@ -404,7 +404,7 @@ "LoginsList.LoginsListSearchPlaceholder" = "Filtrs"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Izskatās, ka Firefox iepriekš avarēja. Vai vēlaties atjaunot cilnes?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Izskatās, ka Ecosia iepriekš avarēja. Vai vēlaties atjaunot cilnes?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Atzīmēt kā lasītu"; @@ -467,7 +467,7 @@ "Okay" = "Labi"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ak vai! Firefox avarēja"; +"Oops! Firefox crashed" = "Ak vai! Ecosia avarēja"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Atveriet rakstus lasīšanas režīmā pieskaroties grāmatas ikonai, kad tā parādās virsraksta joslā."; @@ -479,7 +479,7 @@ "Open Tabs" = "Atvērtās cilnes"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox nevar atvērt lapu, jo tai ir nederīga adrese."; +"OpenURL.Error.Message" = "Ecosia nevar atvērt lapu, jo tai ir nederīga adrese."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nevar atvērt lapu"; @@ -584,7 +584,7 @@ "Search.ThirdPartyEngines.OK" = "Labi"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Vai sūtīt avārijas ziņojumu lai Mozilla var salabot problēmu?"; +"Send a crash report so Mozilla can fix the problem?" = "Vai sūtīt avārijas ziņojumu lai Ecosia var salabot problēmu?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Nosūtīt atsauksmi"; @@ -629,7 +629,7 @@ "SentTab.NoTabArrivingNotification.body" = "Pieskarieties, lai sāktu"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Aplūkot"; @@ -674,7 +674,7 @@ "Settings.DataManagement.SearchLabel" = "Filtrēt vietnes"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox pārstās sinhronizēt ar jūsu kontu, bet esošie dati no šīs ierīces netiks izdzēsti."; +"Settings.Disconnect.Body" = "Ecosia pārstās sinhronizēt ar jūsu kontu, bet esošie dati no šīs ierīces netiks izdzēsti."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Atvienoties"; @@ -710,7 +710,7 @@ "Settings.FxA.Sync.SectionName" = "Sync iestatījumi"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox konts"; +"Settings.FxA.Title" = "Ecosia konts"; /* General settings section title */ "Settings.General.SectionName" = "Vispārīgi"; @@ -728,7 +728,7 @@ "Settings.NewTab.Option.BlankPage" = "Tukša lapa"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox sākumlapa"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia sākumlapa"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Mājas lapa"; @@ -746,10 +746,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Izvēlieties ko rādīt atverot jaunu cilni"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Atverot Firefox"; +"Settings.OfferClipboardBar.Status" = "Atverot Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Atverot Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Atverot Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Piedāvāt atvērt nokopētās saites"; @@ -770,7 +770,7 @@ "Settings.SendUsage.Link" = "Uzzināt vairāk."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla cenšas vākt tikai tos datus, kas ir nepieciešami, lai uzlabotu Firefox ikvienam."; +"Settings.SendUsage.Message" = "Ecosia cenšas vākt tikai tos datus, kas ir nepieciešami, lai uzlabotu Ecosia ikvienam."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Sūtīt izmantošanas datus"; @@ -779,7 +779,7 @@ "Settings.Siri.OpenTabShortcut" = "Atvērt jaunu cilni"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Izmantojiet Siri saīsnes, lai ātri atvērtu Firefox caur Siri"; +"Settings.Siri.SectionDescription" = "Izmantojiet Siri saīsnes, lai ātri atvērtu Ecosia caur Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri saīsnes"; @@ -812,13 +812,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Ielādēt fonā"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Ielādē Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ielādē Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Atvērt ar Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Atvērt ar Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Meklēt ar Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Meklēt ar Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Rādīt meklēšanas ieteikumus"; @@ -914,7 +914,7 @@ "UIMenuItem.PasteGo" = "Ievietot un atvērt"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Meklēt ar Firefox"; +"UIMenuItem.SearchWithFirefox" = "Meklēt ar Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "nelasīts"; @@ -941,7 +941,7 @@ "Yesterday" = "Vakar"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Citu ierīču Firefox nav atvērta neviena cilne."; +"You don’t have any tabs open in Firefox on your other devices." = "Citu ierīču Ecosia nav atvērta neviena cilne."; /* Your Rights settings section title */ "Your Rights" = "Jūsu tiesības"; diff --git a/firefox-ios/Shared/lv.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/lv.lproj/PrivateBrowsing.strings index c98b16df786df..4672cea1e731d 100644 --- a/firefox-ios/Shared/lv.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/lv.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Atvērt jaunā privātā cilnē"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox neatcerēsies apmeklēto lapu vēsturi un sīkdatnes, bet jaunās grāmatzīmes tiks saglabātas jūsu ierīcē."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia neatcerēsies apmeklēto lapu vēsturi un sīkdatnes, bet jaunās grāmatzīmes tiks saglabātas jūsu ierīcē."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Uzzināt vairāk"; diff --git a/firefox-ios/Shared/mr.lproj/Intro.strings b/firefox-ios/Shared/mr.lproj/Intro.strings index 228f1e642c7f9..082f0626a2a9a 100644 --- a/firefox-ios/Shared/mr.lproj/Intro.strings +++ b/firefox-ios/Shared/mr.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "साइन इन"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox मध्ये स्वागत आहे"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia मध्ये स्वागत आहे"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ब्राउजिंग सुरु करा"; diff --git a/firefox-ios/Shared/mr.lproj/Localizable.strings b/firefox-ios/Shared/mr.lproj/Localizable.strings index 10a3641f8c420..44197e70a5826 100644 --- a/firefox-ios/Shared/mr.lproj/Localizable.strings +++ b/firefox-ios/Shared/mr.lproj/Localizable.strings @@ -233,7 +233,7 @@ "ErrorPages.AdvancedWarning2.Text" = "ही चुकीची संरचना किंवा आक्रमणकर्त्यातर्फे केलेली छेडछाड असू शकते. संभाव्य धोका पत्कारात असाल तर पुढे जा."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ च्या मालकाने त्यांचे संकेतस्थळ चुकीच्या रितीने संरचीत केले आहे. आपली महिती चोरीला जाऊ नये म्हणून, Firefox ने या संकेतस्थळाशी जुळवणी केली नाही."; +"ErrorPages.CertWarning.Description" = "%@ च्या मालकाने त्यांचे संकेतस्थळ चुकीच्या रितीने संरचीत केले आहे. आपली महिती चोरीला जाऊ नये म्हणून, Ecosia ने या संकेतस्थळाशी जुळवणी केली नाही."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "ही जोडणी अविश्वासार्ह आहे"; @@ -254,13 +254,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox मुख्य पृष्ठ"; +"Firefox.HomePage.Title" = "Ecosia मुख्य पृष्ठ"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "पुढे"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox खाते"; +"FxA.FirefoxAccount" = "Ecosia खाते"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "खाते व उपकरणे व्यवस्थापित करा"; @@ -272,7 +272,7 @@ "FxA.SyncNow" = "आत्ता ताळमेळ करा"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox ताळमेळ %@ ला जोडलेला आहे"; +"FxAPush_DeviceConnected_body" = "Ecosia ताळमेळ %@ ला जोडलेला आहे"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "ताळमेळ जोडले"; @@ -281,7 +281,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ यशस्वीरित्या डिस्कनेक्ट केले."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "हे उपकरण यशस्वीरित्या Firefox Sync मधुन डिस्कनेक्ट केले आहे."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "हे उपकरण यशस्वीरित्या Ecosia Sync मधुन डिस्कनेक्ट केले आहे."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ताळमेळ खंडीत"; @@ -290,7 +290,7 @@ "FxAPush_DeviceDisconnected_title" = "ताळमेळ खंडीत"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "उपकरण Firefox Sync मधुन डिस्कनेक्ट केले आहे"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "उपकरण Ecosia Sync मधुन डिस्कनेक्ट केले आहे"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "मदत"; @@ -323,7 +323,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "इतर उपकरणांवरील आपले टॅब पाहण्यासाठी साइन इन करा."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox ताळमेळ"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia ताळमेळ"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "इतिहास"; @@ -440,7 +440,7 @@ "LoginsList.Title" = "जतन केलेले लॉगिन"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox याआधी बंद पडला असे दिसते. आपले टॅब परत मिळवायचे का?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia याआधी बंद पडला असे दिसते. आपले टॅब परत मिळवायचे का?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "वाचले असे नोंद करा"; @@ -542,7 +542,7 @@ "Okay" = "ठीक आहे"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ओह! Firefox बंद पडला"; +"Oops! Firefox crashed" = "ओह! Ecosia बंद पडला"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "शीर्षक पट्टीमध्ये दिसल्यानंतर पुस्तक चिन्हावर टॅप करून लेख उघडा."; @@ -554,7 +554,7 @@ "Open Tabs" = "टॅब्स उघडा"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox पृष्ठ उघडू शकत नाही कारण त्यामध्ये एक अवैध पत्ता आहे."; +"OpenURL.Error.Message" = "Ecosia पृष्ठ उघडू शकत नाही कारण त्यामध्ये एक अवैध पत्ता आहे."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "पृष्ठ उघडू शकत नाही"; @@ -566,7 +566,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "हे आपल्याला प्रतिमा कॅमेरा रोल मध्ये जतन करू देते."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox आपली छायाचित्रे वापरु इच्छितो"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia आपली छायाचित्रे वापरु इच्छितो"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "बंद करा"; @@ -674,7 +674,7 @@ "Search.ThirdPartyEngines.OK" = "ठीक आहे"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "त्रुटी सुधारण्यासाठी Mozilla ला क्रॅश अहवाल पाठवायचा का?"; +"Send a crash report so Mozilla can fix the problem?" = "त्रुटी सुधारण्यासाठी Ecosia ला क्रॅश अहवाल पाठवायचा का?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "प्रतिक्रिया पाठवा"; @@ -701,13 +701,13 @@ "SendTo.NavBar.Title" = "टॅब पाठवा"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "या Firefox खात्यासोबत सिंक करण्यासाठी आपल्यासोबत इतर कोणतीही उपकरणे जोडलेली नाहीत."; +"SendTo.NoDevicesFound.Message" = "या Ecosia खात्यासोबत सिंक करण्यासाठी आपल्यासोबत इतर कोणतीही उपकरणे जोडलेली नाहीत."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "कृपया Firefox उघडा, सेटिंगवर जा आणि सुरू ठेवण्यासाठी साइन इन करा."; +"SendTo.NotSignedIn.Message" = "कृपया Ecosia उघडा, सेटिंगवर जा आणि सुरू ठेवण्यासाठी साइन इन करा."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "आपण Firefox खात्यात साइन इन केलेले नाही."; +"SendTo.NotSignedIn.Title" = "आपण Ecosia खात्यात साइन इन केलेले नाही."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "पाठवा"; @@ -728,7 +728,7 @@ "SentTab.NoTabArrivingNotification.body" = "सुरु करण्यासाठी टॅप करा"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox ताळमेळ"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia ताळमेळ"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "पहा"; @@ -785,7 +785,7 @@ "Settings.DataManagement.Title" = "माहिती व्यवस्थापन"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox आपल्या खात्यासोबत ताळमेळ करणे थांबवेल, पण या डिव्हाइस वरील आपला कुठलाही ब्राऊझिंग डेटा हटविणार नाही."; +"Settings.Disconnect.Body" = "Ecosia आपल्या खात्यासोबत ताळमेळ करणे थांबवेल, पण या डिव्हाइस वरील आपला कुठलाही ब्राऊझिंग डेटा हटविणार नाही."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ताळमेळ जोडणी बंद करा"; @@ -848,7 +848,7 @@ "Settings.FxA.Sync.SectionName" = "ताळमेळ सेटिंग्ज"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox खाते"; +"Settings.FxA.Title" = "Ecosia खाते"; /* General settings section title */ "Settings.General.SectionName" = "सर्वसाधारण"; @@ -872,7 +872,7 @@ "Settings.NewTab.Option.BlankPage" = "रिक्त पृष्ठ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox मुख्यपृष्ठ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia मुख्यपृष्ठ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "मुख्यपृष्ठ"; @@ -890,10 +890,10 @@ "Settings.NewTab.TopSectionNameFooter" = "नवीन टॅब उघडताना काय लोड करायचे ते निवडा"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox उघडताना"; +"Settings.OfferClipboardBar.Status" = "Ecosia उघडताना"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox उघडताना"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia उघडताना"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "प्रत बनवलेल्या दुवा उघडण्याचा प्रस्ताव द्या"; @@ -917,7 +917,7 @@ "Settings.SendUsage.Link" = "अधिक जाणा."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "सर्वांसाठी Firefox उपलब्ध करण्यास व सुधारण्यास जी माहिती गरजेची आहे फक्त तीच जमा करण्यास Mozilla तत्पर आहे."; +"Settings.SendUsage.Message" = "सर्वांसाठी Ecosia उपलब्ध करण्यास व सुधारण्यास जी माहिती गरजेची आहे फक्त तीच जमा करण्यास Ecosia तत्पर आहे."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "वापर माहिती पाठवा"; @@ -929,7 +929,7 @@ "Settings.Siri.OpenTabShortcut" = "नवीन टॅब उघडा"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri द्वारे Firefox त्वरीत उघडण्यासाठी Siri शॉर्टकट वापरा"; +"Settings.Siri.SectionDescription" = "Siri द्वारे Ecosia त्वरीत उघडण्यासाठी Siri शॉर्टकट वापरा"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri शॉर्टकट"; @@ -980,13 +980,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "पार्श्वभूमीवर लोड करा"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox मध्ये लोड होत आहे"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia मध्ये लोड होत आहे"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox मध्ये उघडा"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia मध्ये उघडा"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox मध्ये शोधा"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia मध्ये शोधा"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "शोध सूचना दाखवा"; @@ -1085,7 +1085,7 @@ "UIMenuItem.PasteGo" = "चिकटवा व जा"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox सह शोधा"; +"UIMenuItem.SearchWithFirefox" = "Ecosia सह शोधा"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "न वाचलेले"; @@ -1112,7 +1112,7 @@ "Yesterday" = "काल"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "इतर डिव्हाइस वर Firefox मध्ये आपल्या कुठल्याही टॅब उघड्या नाहीत."; +"You don’t have any tabs open in Firefox on your other devices." = "इतर डिव्हाइस वर Ecosia मध्ये आपल्या कुठल्याही टॅब उघड्या नाहीत."; /* Your Rights settings section title */ "Your Rights" = "आपले हक्क"; diff --git a/firefox-ios/Shared/mr.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/mr.lproj/PrivateBrowsing.strings index 28c645f6a8ee5..dcb0f7ae3cb29 100644 --- a/firefox-ios/Shared/mr.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/mr.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "नवीन खाजगी टॅब मध्ये उघडा"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox आपला इतिहास किंवा कुकीज लक्षात ठेवणार नाही, पण नवीन वाचनखुणा जतन केल्या जातील."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia आपला इतिहास किंवा कुकीज लक्षात ठेवणार नाही, पण नवीन वाचनखुणा जतन केल्या जातील."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "अधिक जाणा"; diff --git a/firefox-ios/Shared/ms.lproj/Localizable.strings b/firefox-ios/Shared/ms.lproj/Localizable.strings index a29ceb6d33176..75fbf4142eaf2 100644 --- a/firefox-ios/Shared/ms.lproj/Localizable.strings +++ b/firefox-ios/Shared/ms.lproj/Localizable.strings @@ -179,7 +179,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Mungkin salah konfigurasi atau diganggu oleh penyerang. Teruskan jika anda terima potensi risikonya."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Pemilik %@ mengkonfigurasi laman web mereka secara tidak wajar. Untuk melindungi maklumat anda daripada dicuri, Firefox tidak menyambungkan ke laman web ini."; +"ErrorPages.CertWarning.Description" = "Pemilik %@ mengkonfigurasi laman web mereka secara tidak wajar. Untuk melindungi maklumat anda daripada dicuri, Ecosia tidak menyambungkan ke laman web ini."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Sambungan Ini Tidak dipercayai"; @@ -197,13 +197,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Halaman Utama Firefox"; +"Firefox.HomePage.Title" = "Halaman Utama Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Seterusnya"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Akaun Firefox"; +"FxA.FirefoxAccount" = "Akaun Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Urus Akaun & Peranti"; @@ -215,7 +215,7 @@ "FxA.SyncNow" = "Sync Sekarang"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync telah bersambung %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync telah bersambung %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync Bersambung"; @@ -224,7 +224,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ telah berjaya memutuskan sambungan."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Peranti ini telah berjaya memutuskan sambungan dengan Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Peranti ini telah berjaya memutuskan sambungan dengan Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync Terputus"; @@ -233,7 +233,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync Terputus"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Peranti telah berjaya memutuskan hubungan dengan Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Peranti telah berjaya memutuskan hubungan dengan Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Bantuan"; @@ -251,7 +251,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Daftar masuk untuk melihat senarai tab dari peranti anda yang lain."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Sejarah"; @@ -362,7 +362,7 @@ "LoginsHelper.Update.Button" = "Kemaskini"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox telah ranap sebelum ini. Adakah anda mahu memulihkan tab anda?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia telah ranap sebelum ini. Adakah anda mahu memulihkan tab anda?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Tandakan sebagai Sudah Dibaca"; @@ -425,7 +425,7 @@ "Okay" = "Okay"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox ranap"; +"Oops! Firefox crashed" = "Oops! Ecosia ranap"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Buka artikel dalam Reader View dengan menekan ikon buku apabila muncul dalam tajuk bar."; @@ -437,7 +437,7 @@ "Open Tabs" = "Buka Tab"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox tidak dapat membuka halaman ini kerana alamat tidak sah."; +"OpenURL.Error.Message" = "Ecosia tidak dapat membuka halaman ini kerana alamat tidak sah."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Gagal Membuka Halaman"; @@ -542,7 +542,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Hantar laporan ranap supaya Mozilla boleh menyelesaikan masalah ini?"; +"Send a crash report so Mozilla can fix the problem?" = "Hantar laporan ranap supaya Ecosia boleh menyelesaikan masalah ini?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Hantar Maklum balas"; @@ -578,7 +578,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tap untuk memulakan"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Papar"; @@ -632,7 +632,7 @@ "Settings.DataManagement.Title" = "Pengurusan Data"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox akan menghentikan sync akaun anda, tetapi tidak akan menghapuskan data pelayaran anda dalam peranti ini."; +"Settings.Disconnect.Body" = "Ecosia akan menghentikan sync akaun anda, tetapi tidak akan menghapuskan data pelayaran anda dalam peranti ini."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Putuskan Sync"; @@ -668,7 +668,7 @@ "Settings.FxA.Sync.SectionName" = "Tetapan Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Akaun Firefox"; +"Settings.FxA.Title" = "Akaun Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Umum"; @@ -701,10 +701,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Pilih paparan apabila anda buka tab baru"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Apabila Membuka Firefox"; +"Settings.OfferClipboardBar.Status" = "Apabila Membuka Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Apabila Membuka Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Apabila Membuka Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Menawarkan untuk Membuka Pautan yang Disalin"; @@ -725,7 +725,7 @@ "Settings.SendUsage.Link" = "Ketahui Selanjutnya."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla mengumpulkan hanya yang diperlukan untuk menyedia dan meningkatkan Firefox bagi manfaat semua pengguna."; +"Settings.SendUsage.Message" = "Ecosia mengumpulkan hanya yang diperlukan untuk menyedia dan meningkatkan Ecosia bagi manfaat semua pengguna."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Hantar Data Penggunaan"; @@ -734,7 +734,7 @@ "Settings.Siri.OpenTabShortcut" = "Buka Tab Baru"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Guna pintasan Siri supaya pantas membuka Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Guna pintasan Siri supaya pantas membuka Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Pintasan Siri"; @@ -773,13 +773,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Memuatkan dalam Latar belakang"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Memuatkan dalam Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Memuatkan dalam Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Buka dalam Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Buka dalam Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Cari dalam Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Cari dalam Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Papar Cadangan Carian"; @@ -869,7 +869,7 @@ "UIMenuItem.PasteGo" = "Tampal & Lancarkan"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Cari dengan Firefox"; +"UIMenuItem.SearchWithFirefox" = "Cari dengan Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "belum baca"; @@ -896,7 +896,7 @@ "Yesterday" = "Semalam"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Anda tidak mempunyai apa-apa tab yang dibuka pada Firefox diperanti anda yang lain."; +"You don’t have any tabs open in Firefox on your other devices." = "Anda tidak mempunyai apa-apa tab yang dibuka pada Ecosia diperanti anda yang lain."; /* Your Rights settings section title */ "Your Rights" = "Hak Anda"; diff --git a/firefox-ios/Shared/ms.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ms.lproj/PrivateBrowsing.strings index 79dcd3933e4fb..e100b21230c67 100644 --- a/firefox-ios/Shared/ms.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ms.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Tutup Tab Peribadi"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox tidak akan mengingati sejarah atau kuki anda, tetapi tandabuku baru akan disimpan."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia tidak akan mengingati sejarah atau kuki anda, tetapi tandabuku baru akan disimpan."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Ketahui Selanjutnya"; diff --git a/firefox-ios/Shared/my.lproj/Default Browser.strings b/firefox-ios/Shared/my.lproj/Default Browser.strings index 1ec1ebe557cd0..cddd87bb1afd1 100644 --- a/firefox-ios/Shared/my.lproj/Default Browser.strings +++ b/firefox-ios/Shared/my.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Firefox တွင်ဝက်ဘ်ဆိုက်များ၊ အီးမေးလ်များနှင့်မက်ဆေ့ခ်ျများမှလင့်ခ်များကိုသတ်မှတ်ပါ။"; +"DefaultBrowserCard.Description" = "Ecosia တွင်ဝက်ဘ်ဆိုက်များ၊ အီးမေးလ်များနှင့်မက်ဆေ့ခ်ျများမှလင့်ခ်များကိုသတ်မှတ်ပါ။"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "သင်၏ပုံမှန် Browser ကိုပြောင်းပါ"; diff --git a/firefox-ios/Shared/my.lproj/Intro.strings b/firefox-ios/Shared/my.lproj/Intro.strings index 86e84a9747791..ca5ffd57a6b5b 100644 --- a/firefox-ios/Shared/my.lproj/Intro.strings +++ b/firefox-ios/Shared/my.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "၀င်ပါ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox က ကြိုဆိုပါသည်"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia က ကြိုဆိုပါသည်"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "စတင် သုံးစွဲမည်"; diff --git a/firefox-ios/Shared/my.lproj/Localizable.strings b/firefox-ios/Shared/my.lproj/Localizable.strings index d005860e830a1..937510c01364f 100644 --- a/firefox-ios/Shared/my.lproj/Localizable.strings +++ b/firefox-ios/Shared/my.lproj/Localizable.strings @@ -278,13 +278,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = " Firefox ပင်မစာမျက်နှာ"; +"Firefox.HomePage.Title" = " Ecosia ပင်မစာမျက်နှာ"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "လွှဲပြောင်းပို့ရန်"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox အကောင့်"; +"FxA.FirefoxAccount" = "Ecosia အကောင့်"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "အကောင့်နှင့် ကိရိယာများကို စီမံရန်"; @@ -296,7 +296,7 @@ "fxa.signin.camera-signin" = "သင်၏ကင်မရာဖြင့်ဝင်ပါ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "https://firefox.com/pair ကိုသွားပြီး သင့်ကွန်ပျူတာတွင် Firefox ဖွင့်ပါ။"; +"fxa.signin.qr-link-instruction" = "https://ecosia.org/pair ကိုသွားပြီး သင့်ကွန်ပျူတာတွင် Ecosia ဖွင့်ပါ။"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "စကင်ဖတ်ရန်အဆင်သင့်"; @@ -308,7 +308,7 @@ "FxA.SyncNow" = "ယခုပင် တစ်ပြေးညီ ပြုလုပ်ပါ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync သည် %@ ကို ချိတ်ဆက်ထားပြီးပြီ"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync သည် %@ ကို ချိတ်ဆက်ထားပြီးပြီ"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync ကို ချိတ်ဆက်ထားသည်"; @@ -317,7 +317,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ သည် ချိတ်ဆက်မှုကို ဖြုတ်ပြီးပြီ။"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ယခုကိရိယာသည် Firefox Sync ထံသို့ ချိတ်ဆက်မှုကို အောင်မြင်စွာ ဖြုတ်ပြီးပြီ။"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ယခုကိရိယာသည် Ecosia Sync ထံသို့ ချိတ်ဆက်မှုကို အောင်မြင်စွာ ဖြုတ်ပြီးပြီ။"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync ကို ချိတ်ဆက်မထားပါ"; @@ -326,7 +326,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync ကို ချိတ်ဆက်မထားပါ"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ကိရိယာသည် Firefox Sync သို့ ချိတ်ဆက်မထားပါ"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ကိရိယာသည် Ecosia Sync သို့ ချိတ်ဆက်မထားပါ"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "အကူအညီ"; @@ -359,7 +359,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "အခြား ဖွင့်ထားသောကိရိယာများမှ တပ်ဗ်များကိုကြည့်"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "မှတ်တမ်း"; @@ -596,7 +596,7 @@ "Open Tabs" = "တပ်ဗ်များကို ဖွင့်"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "လိပ်စာ မှားယွင်းနေသောကြောင့် Firefox သည် စာမျက်နှာကို မဖွင့်နိုင်ပါ။"; +"OpenURL.Error.Message" = "လိပ်စာ မှားယွင်းနေသောကြောင့် Ecosia သည် စာမျက်နှာကို မဖွင့်နိုင်ပါ။"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "စာမျက်နှာကို မဖွင့်နိုင်ပါ"; @@ -608,7 +608,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "ရုပ်ပုံများကို သင့် Camera Roll တွင် သိမ်းနိုင်စေမည်။"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "သင့် Photos များကို Firefox က ရယူအသုံးပြုခွင့် ရှိချင်သည်"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "သင့် Photos များကို Ecosia က ရယူအသုံးပြုခွင့် ရှိချင်သည်"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "ပိတ်"; @@ -746,13 +746,13 @@ "SendTo.NavBar.Title" = "တပ်ဗ်ကို ပို့ရန်"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Firefox Account ကို Sync ရန် သင့်တွင် ချိတ်ဆက်ထားသော အခြား ကိရိယာများ မရှိပါ။"; +"SendTo.NoDevicesFound.Message" = "Ecosia Account ကို Sync ရန် သင့်တွင် ချိတ်ဆက်ထားသော အခြား ကိရိယာများ မရှိပါ။"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Firefox ကို ကျေးဇူးပြု၍ ဖွင့်ပါ၊ အပြင်အဆင်များသို့ သွားပါ၊ ပြီးနောက် ဆက်လက်ဆောင်ရွက်ရန် ဝင်ရောက်ပါ။"; +"SendTo.NotSignedIn.Message" = "Ecosia ကို ကျေးဇူးပြု၍ ဖွင့်ပါ၊ အပြင်အဆင်များသို့ သွားပါ၊ ပြီးနောက် ဆက်လက်ဆောင်ရွက်ရန် ဝင်ရောက်ပါ။"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "သင့် Firefox Account သို့ ဝင်ရောက်ထားခြင်း မရှိပါ။"; +"SendTo.NotSignedIn.Title" = "သင့် Ecosia Account သို့ ဝင်ရောက်ထားခြင်း မရှိပါ။"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ပို့ရန်"; @@ -773,7 +773,7 @@ "SentTab.NoTabArrivingNotification.body" = "စတင်ရန် တို့ထိပါ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ကြည့်ရန်"; @@ -830,7 +830,7 @@ "Settings.DataManagement.Title" = "အချက်အလက် စီမံခန့်ခွဲမှု"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox သည် အကောင့်နှင့် အချက်အလက်များ ထပ်တူပြုခြင်းကို ရပ်တန့်ပါမည်။ သို့သော် ယခုကိရိယာရှိ သင်၏ မည်သည့်အချက်အလက်ကိုမျှ ဖျက်ပစ်မည် မဟုတ်ပါ။"; +"Settings.Disconnect.Body" = "Ecosia သည် အကောင့်နှင့် အချက်အလက်များ ထပ်တူပြုခြင်းကို ရပ်တန့်ပါမည်။ သို့သော် ယခုကိရိယာရှိ သင်၏ မည်သည့်အချက်အလက်ကိုမျှ ဖျက်ပစ်မည် မဟုတ်ပါ။"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync နှင့် ချိတ်ဆက်ဖြုတ်ရန်"; @@ -893,7 +893,7 @@ "Settings.FxA.Sync.SectionName" = "Sync အပြင်အဆင်များ"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox အကောင့်"; +"Settings.FxA.Title" = "Ecosia အကောင့်"; /* General settings section title */ "Settings.General.SectionName" = "အထွေထွေ"; @@ -917,7 +917,7 @@ "Settings.NewTab.Option.BlankPage" = "စာမျက်နှာအလွတ်"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox အဖွင့်စာမျက်နှာ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia အဖွင့်စာမျက်နှာ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "အဖွင့်စာမျက်နှာ"; @@ -935,10 +935,10 @@ "Settings.NewTab.TopSectionNameFooter" = "တက်ပ်အသစ်ဖွင့်သည့်အခါတက်လာမည့်အရာကိုရွေးချယ်ပါ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox ကို ဖွင့်သည့်အခါ"; +"Settings.OfferClipboardBar.Status" = "Ecosia ကို ဖွင့်သည့်အခါ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox ကို ဖွင့်သည့်အခါ"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia ကို ဖွင့်သည့်အခါ"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "ကူးယူထားသော လင့်ခ်များကို ဖွင့်ရန် မေးမြန်းပါ"; @@ -962,7 +962,7 @@ "Settings.SendUsage.Link" = "ပိုမိုလေ့လာရန်။"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla သည် လူတိုင်းအတွက် Firefox ကို ပေးစွမ်းရန်နှင့် တိုးတက်ကောင်းမွန်စေရန်အတွက်သာ ကောက်ခံမှုပြုခြင်း ဖြစ်ပါသည်။"; +"Settings.SendUsage.Message" = "Ecosia သည် လူတိုင်းအတွက် Ecosia ကို ပေးစွမ်းရန်နှင့် တိုးတက်ကောင်းမွန်စေရန်အတွက်သာ ကောက်ခံမှုပြုခြင်း ဖြစ်ပါသည်။"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "အသုံးပြုမှုဆိုင်ရာ အချက်အလက်များကို ပေးပို့ရန်"; @@ -983,7 +983,7 @@ "Settings.Siri.OpenTabShortcut" = "တပ်ဗ်အသစ် ဖွင့်ပါ"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri မှတဆင့် Firefox ကို မြန်မြန်ဆန်ဆန် ဖွင့်ရန် Siri နည်းလမ်းတိုများကို အသုံးပြုပါ"; +"Settings.Siri.SectionDescription" = "Siri မှတဆင့် Ecosia ကို မြန်မြန်ဆန်ဆန် ဖွင့်ရန် Siri နည်းလမ်းတိုများကို အသုံးပြုပါ"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri နည်းလမ်းတိုများ"; @@ -1052,13 +1052,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "နောက်ကွယ်တွင် ဆောင်ရွက်ပါ"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox တွင် ဆောင်ရွက်နေသည်"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia တွင် ဆောင်ရွက်နေသည်"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox နှင့် ဖွင့်ပါ"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia နှင့် ဖွင့်ပါ"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox တွင် ရှာပါ"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia တွင် ရှာပါ"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "ရှာဖွေရေး အကြံပြုချက်များကို ပြပါ"; @@ -1166,7 +1166,7 @@ "UIMenuItem.PasteGo" = "ကူးယူပြီးနောက်သွားမည်"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox ဖြင့် ရှာပါ"; +"UIMenuItem.SearchWithFirefox" = "Ecosia ဖြင့် ရှာပါ"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "မဖတ်"; diff --git a/firefox-ios/Shared/my.lproj/Today.strings b/firefox-ios/Shared/my.lproj/Today.strings index 9d963a81710a1..0ad7229a1ae1e 100644 --- a/firefox-ios/Shared/my.lproj/Today.strings +++ b/firefox-ios/Shared/my.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "ကိုယ်ပိုင်သုံးတဗ်များကိုပိတ်ပါ။"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "သင်၏မူလမြင်ကွင်းသို့ Firefox ဖြတ်လမ်းများကိုထည့်ပါ။"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "သင်၏မူလမြင်ကွင်းသို့ Ecosia ဖြတ်လမ်းများကိုထည့်ပါ။"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "ကူးယူထားသည့်လင့်သို့ သွားပါ။"; @@ -23,13 +23,13 @@ "TodayWidget.NoOpenTabsLabel" = "ဖွင့်ထားသည့်တပ်ဗ်များ မရှိပါ"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox ကို ဖွင့်ပါ"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia ကို ဖွင့်ပါ"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "ကိုယ်ပိုင်ရှာဖွေမှု"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "သင်၏မူလမြင်ကွင်းသို့ Firefox ဖြတ်လမ်းများကိုထည့်ပါ။ ဤအရာကိုထည့်ပြီးလျှင်အခြားဖြတ်လမ်းများကိုရွေးချယ်၍ထားရှိပါ။"; +"TodayWidget.QuickActionGalleryDescription" = "သင်၏မူလမြင်ကွင်းသို့ Ecosia ဖြတ်လမ်းများကိုထည့်ပါ။ ဤအရာကိုထည့်ပြီးလျှင်အခြားဖြတ်လမ်းများကိုရွေးချယ်၍ထားရှိပါ။"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "လျင်မြန်စွာလုပ်ဆောင်ချက်များ"; @@ -41,7 +41,7 @@ "TodayWidget.QuickViewGalleryTitle" = "အမြန်ကြည့်မြင်ကွင်း"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox တွင် ရှာပါ"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia တွင် ရှာပါ"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "သီးသန့်တဗ်တွင် ရှာဖွေပါ"; diff --git a/firefox-ios/Shared/nb.lproj/Default Browser.strings b/firefox-ios/Shared/nb.lproj/Default Browser.strings index a78ac938cc413..a7cd2894d3d84 100644 --- a/firefox-ios/Shared/nb.lproj/Default Browser.strings +++ b/firefox-ios/Shared/nb.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Å gjøre Firefox til din standardnettleser er en stemme for et åpent, tilgjengelig internett."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Å gjøre Ecosia til din standardnettleser er en stemme for et åpent, tilgjengelig internett."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard for et bedre internett"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Finn ut hvordan"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Angi at lenker fra nettsteder, e-postmeldinger og meldinger skal åpnes automatisk i Firefox."; +"DefaultBrowserCard.Description" = "Angi at lenker fra nettsteder, e-postmeldinger og meldinger skal åpnes automatisk i Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Velg Firefox som standard nettleser for å automatisk få fårt, sikkerhet og personvern."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Velg Ecosia som standard nettleser for å automatisk få fårt, sikkerhet og personvern."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Forbedre din daglige surfing"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokkerer i gjennomsnitt over 3000 sporere per bruker hver måned. Gjør oss til din standardnettleser for å ta vare på personvernet."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokkerer i gjennomsnitt over 3000 sporere per bruker hver måned. Gjør oss til din standardnettleser for å ta vare på personvernet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox tar vare på personvernet ditt"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia tar vare på personvernet ditt"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Endre standard nettleser"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Trykk på Standard nettleserapp"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Velg Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Velg Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standard nettleser-app"; diff --git a/firefox-ios/Shared/nb.lproj/Intro.strings b/firefox-ios/Shared/nb.lproj/Intro.strings index 3689a5d1b7ff2..22104f3c21234 100644 --- a/firefox-ios/Shared/nb.lproj/Intro.strings +++ b/firefox-ios/Shared/nb.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Logg inn"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Velkommen til Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Velkommen til Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Begynn å surfe"; diff --git a/firefox-ios/Shared/nb.lproj/Localizable.strings b/firefox-ios/Shared/nb.lproj/Localizable.strings index 728aae9c85362..20a784f679b7b 100644 --- a/firefox-ios/Shared/nb.lproj/Localizable.strings +++ b/firefox-ios/Shared/nb.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Det kan være en feilkonfigurasjon eller manipulering av en angriper. Fortsett hvis du godtar den potensielle risikoen."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Eieren av %@ har konfigurert nettstedet sitt feil. For å beskytte informasjonen din fra å bli stjålet, har Firefox ikke koblet til denne nettsiden."; +"ErrorPages.CertWarning.Description" = "Eieren av %@ har konfigurert nettstedet sitt feil. For å beskytte informasjonen din fra å bli stjålet, har Ecosia ikke koblet til denne nettsiden."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Denne tilkoblingen er usikker"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox startside"; +"Firefox.HomePage.Title" = "Ecosia startside"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Tilpass startsiden"; @@ -377,7 +377,7 @@ "Forward" = "Frem"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-konto"; +"FxA.FirefoxAccount" = "Ecosia-konto"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Behandle konto og enheter"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Logg inn med kameraet ditt"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Åpne Firefox på datamaskinen din og gå til firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Åpne Ecosia på datamaskinen din og gå til ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Klar til å skanne"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synkroniser nå"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync har koblet til %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync har koblet til %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync er tilkoblet"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ har blitt koblet bort."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Denne enheten er koblet fra Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Denne enheten er koblet fra Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync er frakoblet"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync er frakoblet"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "En enhet har blitt koblet fra Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "En enhet har blitt koblet fra Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-logo, endre bakgrunn."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-logo, endre bakgrunn."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Hjelp"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Logg inn for å se en liste med faner fra de andre enhetene dine."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historikk"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Les mer"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "For å bruke autofyll-funksjonen for Firefox, må du ha en enhetssikkerhetskode aktivert."; +"Logins.PasscodeRequirement.Warning" = "For å bruke autofyll-funksjonen for Ecosia, må du ha en enhetssikkerhetskode aktivert."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ta med deg passordene dine overalt"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autofyll Firefox-passord"; +"Logins.WelcomeView.Title2" = "Autofyll Ecosia-passord"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Slå på autofyll"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "LAGREDE INNLOGGINGER"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Det ser ut til at Firefox krasjet tidligere. Vil du gjenopprette fanene dine?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Det ser ut til at Ecosia krasjet tidligere. Vil du gjenopprette fanene dine?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marker som lest"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Begynn å surfe"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ops! Firefox krasjet"; +"Oops! Firefox crashed" = "Ops! Ecosia krasjet"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Åpne artikler i lesevisning ved å trykke på bokikonet når det vises på adresselinjen."; @@ -905,7 +905,7 @@ "Open Tabs" = "Åpne faner"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kan ikke åpne siden fordi den har en ugyldig adresse."; +"OpenURL.Error.Message" = "Ecosia kan ikke åpne siden fordi den har en ugyldig adresse."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Klarte ikke å åpne siden"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Dette lar deg lagre bildet til kamerarullen."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ønsker tilgang til dine bilder"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ønsker tilgang til dine bilder"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Lukk"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Ugyldige data"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Gå til enhetens «Innstillinger» > «Firefox». Gi Firefox tilgang til kameraet."; +"ScanQRCode.PermissionError.Message.v100" = "Gå til enhetens «Innstillinger» > «Ecosia». Gi Ecosia tilgang til kameraet."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skann QR-kode"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Bytt til fane"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-forslag"; +"Search.SuggestSectionTitle.v102" = "Ecosia-forslag"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Den nye søkemotoren vil vises i hurtigsøkelinjen."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Send en krasjrapport slik at Mozilla kan ordna problemet?"; +"Send a crash report so Mozilla can fix the problem?" = "Send en krasjrapport slik at Ecosia kan ordna problemet?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Send tilbakemelding"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Send fane"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Du har ingen andre enheter koblet til denne Firefox-kontoen tilgjengelig for synkronisering."; +"SendTo.NoDevicesFound.Message" = "Du har ingen andre enheter koblet til denne Ecosia-kontoen tilgjengelig for synkronisering."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Åpne Firefox, gå til Innstillinger og logg inn for å fortsette."; +"SendTo.NotSignedIn.Message" = "Åpne Ecosia, gå til Innstillinger og logg inn for å fortsette."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Du er ikke logget inn på Firefox-kontoen din."; +"SendTo.NotSignedIn.Title" = "Du er ikke logget inn på Ecosia-kontoen din."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Trykk for å starte"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vis"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Databehandling"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox vil slutte å synkronisere med kontoen din, men vil ikke slette noen av nettleserdataene dine på denne enheten."; +"Settings.Disconnect.Body" = "Ecosia vil slutte å synkronisere med kontoen din, men vil ikke slette noen av nettleserdataene dine på denne enheten."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Koble fra Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync-innstillinger"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-konto"; +"Settings.FxA.Title" = "Ecosia-konto"; /* General settings section title */ "Settings.General.SectionName" = "Generelt"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Velg hva som skal vises som startside."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Velg hva som er inkludert på Firefox-startsiden."; +"Settings.Home.Option.Description.v101" = "Velg hva som er inkludert på Ecosia-startsiden."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Gå tilbake til"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startside"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Velg hva som vises når du går tilbake til Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Velg hva som vises når du går tilbake til Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Siste fane"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Bakgrunnsbilde"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-bakgrunnsbilde, ametystmønster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-bakgrunnsbilde, ametystmønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-bakgrunnsbilde, strandås-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-bakgrunnsbilde, strandås-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-bakgrunnsbilde, cerulean-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-bakgrunnsbilde, cerulean-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standard bakgrunnsbilde."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox bakgrunnsbilde, soloppgangsmønster."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia bakgrunnsbilde, soloppgangsmønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Knapp for å skifte bakgrunn på startsiden"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-bakgrunnsbilde, skumringsås-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-bakgrunnsbilde, skumringsås-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassisk %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Begrenset utgave"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Endre bakgrunnsbilde ved å trykke på Firefox-logoen på startsiden"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Endre bakgrunnsbilde ved å trykke på Ecosia-logoen på startsiden"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Bakgrunnsbilde"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Tilpasset"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox startside"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia startside"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startside"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokker bilder"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Når du åpner Firefox"; +"Settings.OfferClipboardBar.Status" = "Når du åpner Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Når du åpner %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Når du åpner Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Når du åpner Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Tilby å åpne kopierte lenker"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Les mer."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strever etter å bare samle inn det vi trenger for å tilby og forbedre Firefox for alle."; +"Settings.SendUsage.Message" = "Ecosia strever etter å bare samle inn det vi trenger for å tilby og forbedre Ecosia for alle."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send bruksdata"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Åpne ny fane"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Bruk Siri-snarveier for å raskt åpne Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Bruk Siri-snarveier for å raskt åpne Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-snarveier"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Les mer."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox kan installere og kjøre studier fra tid til annen."; +"Settings.Studies.Toggle.Message" = "Ecosia kan installere og kjøre studier fra tid til annen."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Undersøkelser"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Last inn i bakgrunnen"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Laster i Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Laster i Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Åpne i Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Åpne i Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Søk i Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Søk i Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopier"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Lim inn og åpne"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Søk med Firefox"; +"UIMenuItem.SearchWithFirefox" = "Søk med Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ulest"; @@ -1793,7 +1793,7 @@ "Yesterday" = "I går"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Du har ingen faner åpne i Firefox på andre enheter."; +"You don’t have any tabs open in Firefox on your other devices." = "Du har ingen faner åpne i Ecosia på andre enheter."; /* Your Rights settings section title */ "Your Rights" = "Dine rettigheter"; diff --git a/firefox-ios/Shared/nb.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/nb.lproj/PrivateBrowsing.strings index 8cf294e0e087a..26cf5f731b890 100644 --- a/firefox-ios/Shared/nb.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/nb.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Åpne i ny privat fane"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox vil ikke huske noe av din historikk eller infokapsler, men nye bokmerker blir lagret."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia vil ikke huske noe av din historikk eller infokapsler, men nye bokmerker blir lagret."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Lær mer"; @@ -22,6 +22,9 @@ /* Accessibility label for toggling on/off private mode */ "Private Mode" = "Privatmodus"; +/* Accessibility label for toggling on/off private mode */ +"PrivateBrowsing.Toggle.A11y.Label.v132" = "Privat nettlesing"; + /* Accessiblity hint for toggling on/off private mode */ "Turns private mode on or off" = "Slår privatmodus på eller av"; diff --git a/firefox-ios/Shared/nb.lproj/Today.strings b/firefox-ios/Shared/nb.lproj/Today.strings index 3486fe588b069..71a03bc6f8a2f 100644 --- a/firefox-ios/Shared/nb.lproj/Today.strings +++ b/firefox-ios/Shared/nb.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Lukk private faner"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Legg til Firefox-snarveier på hjem-skjermen."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Legg til Ecosia-snarveier på hjem-skjermen."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Gå til kopiert lenke"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ingen åpne faner."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Åpne Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Åpne Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privat søk"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Legg til en Firefox-snarvei til hjem-skjermen. Når du har lagt til widgeten, trykker du på og holder nede for å redigere den og velger en annen snarvei."; +"TodayWidget.QuickActionGalleryDescription" = "Legg til en Ecosia-snarvei til hjem-skjermen. Når du har lagt til widgeten, trykker du på og holder nede for å redigere den og velger en annen snarvei."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Hurtige handlinger"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-snarveier"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-snarveier"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Legg til snarveier til dine åpne faner."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Hurtigvisning"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Søk i Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Søk i Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Søk i privat fane"; diff --git a/firefox-ios/Shared/ne-NP.lproj/Default Browser.strings b/firefox-ios/Shared/ne-NP.lproj/Default Browser.strings index 3fccecbf3ad3b..b08f85086a306 100644 --- a/firefox-ios/Shared/ne-NP.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ne-NP.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "कसरी हो सिक्नुहोस्"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "स्वचालित रूपमा Firefoxबाट खोल्नको लागि वेबसाइटहरू,ईमेलहरू र सन्देशहरूबाट आउने लिङ्कहरू सेट गर्नुहोस् ।"; +"DefaultBrowserCard.Description" = "स्वचालित रूपमा Ecosiaबाट खोल्नको लागि वेबसाइटहरू,ईमेलहरू र सन्देशहरूबाट आउने लिङ्कहरू सेट गर्नुहोस् ।"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "तपाईंको पूर्वनिर्धारित ब्राउजर परिबर्तन गर्नुहोस्"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2। पूर्वनिर्धारित ब्राउजर एपमा ट्याप गर्नुहोस्"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3। Firefox लाई चयन गर्नुहोस्"; +"DefaultBrowserOnboarding.Description3" = "3। Ecosia लाई चयन गर्नुहोस्"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "पूर्वनिर्धारित ब्राउजर एप"; diff --git a/firefox-ios/Shared/ne-NP.lproj/Intro.strings b/firefox-ios/Shared/ne-NP.lproj/Intro.strings index 4c5132b005871..b960cbd83d135 100644 --- a/firefox-ios/Shared/ne-NP.lproj/Intro.strings +++ b/firefox-ios/Shared/ne-NP.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "साइन इन"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefoxमा हार्दिक स्वागत छ"; +"Intro.Slides.Welcome.Title.v2" = "Ecosiaमा हार्दिक स्वागत छ"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ब्राउजिङ्ग सुरु गर्नुहोस्"; diff --git a/firefox-ios/Shared/ne-NP.lproj/Localizable.strings b/firefox-ios/Shared/ne-NP.lproj/Localizable.strings index 2b88b3fff5258..ed76901a0d893 100644 --- a/firefox-ios/Shared/ne-NP.lproj/Localizable.strings +++ b/firefox-ios/Shared/ne-NP.lproj/Localizable.strings @@ -296,7 +296,7 @@ "ErrorPages.AdvancedWarning2.Text" = "यो एक आक्रमणकारीको छेडछाड वा एक गलत कन्फिगरेसन हुन सक्छ। तपाईँ सम्भावित जोखिम स्वीकार गर्नुहुन्छ भने मात्र अघि बढ्नुहोस्।"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ को मालिकले अनुचित ढङ्गले आफ्नो वेबसाइट कन्फिगर गरेको छ। तपाईँको जानकारी चोरी हुनबाट रक्षा गर्न, Firefox ले यो वेबसाइटमा जडान गरेको छैन।"; +"ErrorPages.CertWarning.Description" = "%@ को मालिकले अनुचित ढङ्गले आफ्नो वेबसाइट कन्फिगर गरेको छ। तपाईँको जानकारी चोरी हुनबाट रक्षा गर्न, Ecosia ले यो वेबसाइटमा जडान गरेको छैन।"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "यो जडान अविश्वासिलो छ"; @@ -317,7 +317,7 @@ "Facebook" = "फेसबुक"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox गृह पृष्ठ"; +"Firefox.HomePage.Title" = "Ecosia गृह पृष्ठ"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "गृहपृष्ठलाई अनुकूलित गर्नुहोस्"; @@ -338,7 +338,7 @@ "Forward" = "अगाडि"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox खाता"; +"FxA.FirefoxAccount" = "Ecosia खाता"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "खाता र उपकरणहरु व्यवस्थापन गर्नुहोस्"; @@ -350,7 +350,7 @@ "fxa.signin.camera-signin" = "आफ्नो क्यामराको साथमा साइन इन गर्नुहोस्"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "आफ्नो कम्प्युटरमा Firefox खोल्नुहोस् र firefox.com/pair मा जानुहोस्"; +"fxa.signin.qr-link-instruction" = "आफ्नो कम्प्युटरमा Ecosia खोल्नुहोस् र ecosia.org/pair मा जानुहोस्"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "स्क्यान गर्न तयार छ"; @@ -362,7 +362,7 @@ "FxA.SyncNow" = "अहिले सिङ्क गर्नुहोस्"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox सिङ्क %@ मा जोडिएको छ"; +"FxAPush_DeviceConnected_body" = "Ecosia सिङ्क %@ मा जोडिएको छ"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "सिङ्क जडान भयो"; @@ -371,7 +371,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ सफलतापूर्वक विच्छेद भएको छ ।"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "यो यन्त्र सफलतापूर्वक Firefox सिङ्क बाट विच्छेद भयो ।"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "यो यन्त्र सफलतापूर्वक Ecosia सिङ्क बाट विच्छेद भयो ।"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "सिङ्क विच्छेद भयो"; @@ -380,10 +380,10 @@ "FxAPush_DeviceDisconnected_title" = "सिङ्क विच्छेद भयो"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "एक उपकरण Firefox सिङ्कबाट बिच्छेद भएको छ"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "एक उपकरण Ecosia सिङ्कबाट बिच्छेद भएको छ"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox लोगो, वालपेपर परिवर्तन गर्नुहोस्।"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia लोगो, वालपेपर परिवर्तन गर्नुहोस्।"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "सहयोग"; @@ -416,7 +416,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "तपाईँको अन्य यन्त्रहरूबाट ट्याबहरूको सूची हेर्न साइन-इन गर्नुहोस्।"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox सिङ्क"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia सिङ्क"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "इतिहास"; @@ -584,13 +584,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "थप जान्नुहोस्"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox को लागि स्वत: भरण सुविधा प्रयोग गर्न, तपाइँसँग एक उपकरण पासकोड सक्षम हुनुपर्छ।"; +"Logins.PasscodeRequirement.Warning" = "Ecosia को लागि स्वत: भरण सुविधा प्रयोग गर्न, तपाइँसँग एक उपकरण पासकोड सक्षम हुनुपर्छ।"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "तपाईंको पासवर्डलाई तपाईंसङ्गै लैजानुहोस्"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox पासवर्डहरू स्वतः भर्नुहोस्"; +"Logins.WelcomeView.Title2" = "Ecosia पासवर्डहरू स्वतः भर्नुहोस्"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "स्वत: भरण सक्षम गर्नुहोस्"; @@ -635,7 +635,7 @@ "LoginsList.Title" = "सुरक्षित गरिएका लगइनहरू"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox पहिले क्र्यास भएको जस्तो देखिन्छ। तपाईँ आफ्नो ट्याबहरू पुनःस्थापना गर्न चाहनुहुन्छ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia पहिले क्र्यास भएको जस्तो देखिन्छ। तपाईँ आफ्नो ट्याबहरू पुनःस्थापना गर्न चाहनुहुन्छ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "पढेको रूपमा चिन्ह लगाउनुहोस्"; @@ -809,7 +809,7 @@ "Onboarding.WhatsNew.Button.Title" = "ब्राउजिङ्ग सुरु गर्नुहोस्"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ओहो! Firefox क्रयास भयो"; +"Oops! Firefox crashed" = "ओहो! Ecosia क्रयास भयो"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Reader View मा लेखहरू हेर्न शीर्षक पट्टीमा रहेको पुस्तक चिनोमा थिच्नुहोस्।"; @@ -821,7 +821,7 @@ "Open Tabs" = "ट्याबहरू खोल्नुहोस्"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "ठेगाना नमिलेको कारण Firefox ले यो पृष्ठ खोल्न सकेन।"; +"OpenURL.Error.Message" = "ठेगाना नमिलेको कारण Ecosia ले यो पृष्ठ खोल्न सकेन।"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "पृष्ठ खोल्न सकिएन"; @@ -833,7 +833,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "यसले तपाईंको तस्वीरलाई क्यामरा रोलमा सङ्ग्रह गर्न अनुमति दिन्छ।"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ले तपाईँका तस्बिरहरूमा पहुँच बनाउन चाहन्छ"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ले तपाईँका तस्बिरहरूमा पहुँच बनाउन चाहन्छ"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "बन्द गर्नुहोस्"; @@ -959,7 +959,7 @@ "Search.ThirdPartyEngines.OK" = "ठिक छ"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozilla ले समस्या समाधान गरोस् भनेर क्रयास् रिपोट पठाउने?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosia ले समस्या समाधान गरोस् भनेर क्रयास् रिपोट पठाउने?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "प्रतिक्रिया पठाउनुहोस्"; @@ -986,13 +986,13 @@ "SendTo.NavBar.Title" = "ट्याब पठाउनुहोस्"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "तपाईँको यो Firefox खातामा कुनै पनि अरू यन्त्रहरू जडान भएको छैन।"; +"SendTo.NoDevicesFound.Message" = "तपाईँको यो Ecosia खातामा कुनै पनि अरू यन्त्रहरू जडान भएको छैन।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "कृपया Firefox खोल्नुहोस्, अगाडि जानका लागि सेटिङ्गहरुमा गएर साइन इन गर्नुहोस्।"; +"SendTo.NotSignedIn.Message" = "कृपया Ecosia खोल्नुहोस्, अगाडि जानका लागि सेटिङ्गहरुमा गएर साइन इन गर्नुहोस्।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "तपाईँ आफ्नो Firefox खातामा साइन इन हुनुहुन्न।"; +"SendTo.NotSignedIn.Title" = "तपाईँ आफ्नो Ecosia खातामा साइन इन हुनुहुन्न।"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "पठाउनुहोस्"; @@ -1013,7 +1013,7 @@ "SentTab.NoTabArrivingNotification.body" = "सुरु गर्न छुनुहोस्"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "हेर्नुहोस्"; @@ -1073,7 +1073,7 @@ "Settings.DataManagement.Title" = "डाटा व्यवस्थापन"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ले तपाईँको खातासँग समक्रमण गर्न रोक्नेछ, तर यो कम्प्युटरमा तपाईँको ब्राउजिङ्ग डाटा मेटिने छैन।"; +"Settings.Disconnect.Body" = "Ecosia ले तपाईँको खातासँग समक्रमण गर्न रोक्नेछ, तर यो कम्प्युटरमा तपाईँको ब्राउजिङ्ग डाटा मेटिने छैन।"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync विच्छेद गर्नुहोस्"; @@ -1136,7 +1136,7 @@ "Settings.FxA.Sync.SectionName" = "Sync सेटिङ्गहरू"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox खाता"; +"Settings.FxA.Title" = "Ecosia खाता"; /* General settings section title */ "Settings.General.SectionName" = "सामान्य"; @@ -1163,7 +1163,7 @@ "Settings.Home.Option.StartAtHome.Always" = "गृहपृष्ठ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "तपाईंले Firefox मा फर्किँदा के देख्नुहुन्छ छनौट गर्नुहोस्।"; +"Settings.Home.Option.StartAtHome.Description" = "तपाईंले Ecosia मा फर्किँदा के देख्नुहुन्छ छनौट गर्नुहोस्।"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "अन्तिम ट्याब"; @@ -1193,7 +1193,7 @@ "Settings.Home.Option.Wallpaper.CollectionTitle" = "खोल्ने स्क्रिन"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox होमपेज लोगो ट्याप गरेर वालपेपर परिवर्तन गर्नुहोस्"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia होमपेज लोगो ट्याप गरेर वालपेपर परिवर्तन गर्नुहोस्"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "वालपेपर"; @@ -1223,7 +1223,7 @@ "Settings.NewTab.Option.BlankPage" = "खाली पृष्ठ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox गृह"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia गृह"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "गृहपृष्ठ"; @@ -1244,10 +1244,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "तस्वीरहरु अवरुद्ध गर्नुहोस्"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox खोल्ने समयमा"; +"Settings.OfferClipboardBar.Status" = "Ecosia खोल्ने समयमा"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox खोल्ने समयमा"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia खोल्ने समयमा"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "प्रतिलिपि गरिएका लिङ्कहरू खोल्न प्रस्ताव गर्नुहोस्"; @@ -1271,7 +1271,7 @@ "Settings.SendUsage.Link" = "अझ जान्नुहोस् |"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla सबैको लागि Firefox प्रदान र सुधार गर्न आवश्यक कुरा मात्र संकलन गर्ने प्रयास गर्दछ ।"; +"Settings.SendUsage.Message" = "Ecosia सबैको लागि Ecosia प्रदान र सुधार गर्न आवश्यक कुरा मात्र संकलन गर्ने प्रयास गर्दछ ।"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "उपयोग डाटा पठाउनुहोस्"; @@ -1292,7 +1292,7 @@ "Settings.Siri.OpenTabShortcut" = "नयाँ ट्याब खोल्नुहोस्"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "सिरी मार्फत Firefox छिटो खोल्नका लागि सिरी सर्टकटहरू प्रयोग गर्नुहोस्"; +"Settings.Siri.SectionDescription" = "सिरी मार्फत Ecosia छिटो खोल्नका लागि सिरी सर्टकटहरू प्रयोग गर्नुहोस्"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "सिरी सर्टकटहरु"; @@ -1301,7 +1301,7 @@ "Settings.Studies.Toggle.Link" = "थप जान्नुहोस्।"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox ले समय-समयमा अध्ययनहरू स्थापना गर्न र चलाउन सक्छ।"; +"Settings.Studies.Toggle.Message" = "Ecosia ले समय-समयमा अध्ययनहरू स्थापना गर्न र चलाउन सक्छ।"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "अध्ययनहरु"; @@ -1394,13 +1394,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "पृष्ठभूमिमा लोड गर्नुहोस्"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox मा लोड हुँदै"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia मा लोड हुँदै"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox मा खोल्नुहोस्"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia मा खोल्नुहोस्"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox मा खोज्नुहोस्"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia मा खोज्नुहोस्"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "खोजी सुझावहरू देखाउनुहोस्"; @@ -1526,7 +1526,7 @@ "UIMenuItem.PasteGo" = "पेष्ट गरेर अगाडी बढ्नुहोस्"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox सङ्ग खोज्नुहोस्"; +"UIMenuItem.SearchWithFirefox" = "Ecosia सङ्ग खोज्नुहोस्"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "नपढिएका"; @@ -1556,7 +1556,7 @@ "Yesterday" = "हिजो"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "तपाईँको अन्य यन्त्रहरूको Firefox मा कुनै पनि ट्याबहरू खुला छैन।"; +"You don’t have any tabs open in Firefox on your other devices." = "तपाईँको अन्य यन्त्रहरूको Ecosia मा कुनै पनि ट्याबहरू खुला छैन।"; /* Your Rights settings section title */ "Your Rights" = "तपाईँका अधिकारहरू"; diff --git a/firefox-ios/Shared/ne-NP.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ne-NP.lproj/PrivateBrowsing.strings index becae39e89059..8d6ae5a418acf 100644 --- a/firefox-ios/Shared/ne-NP.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ne-NP.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "नयाँ निजी ट्याबमा खोल्नुहोस्"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ले तपाईँको कुनैपनि ईतिहास र कुकिज सन्चीत गर्ने छैन, तर नयाँ पुस्तकचिनो भने सन्चीत हुनेछ।"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ले तपाईँको कुनैपनि ईतिहास र कुकिज सन्चीत गर्ने छैन, तर नयाँ पुस्तकचिनो भने सन्चीत हुनेछ।"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "अझ जान्नुहोस्"; diff --git a/firefox-ios/Shared/ne-NP.lproj/Today.strings b/firefox-ios/Shared/ne-NP.lproj/Today.strings index ed8de6c7f31dc..a73be166f2cbe 100644 --- a/firefox-ios/Shared/ne-NP.lproj/Today.strings +++ b/firefox-ios/Shared/ne-NP.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "निजी ट्याबहरू\nबन्द गर्नुहोस्"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefox लाई तपाईंको गृह स्क्रिनमा सर्टकटको रुपमा थप्नुहोस्।"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosia लाई तपाईंको गृह स्क्रिनमा सर्टकटको रुपमा थप्नुहोस्।"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "कपि गरिएको लिङ्कमा जानुहोस्"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "कुनैपनि खोलिएका ट्याबहरु छैनन्।"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox खोल्नुहोस्"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia खोल्नुहोस्"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "निजी खोजी"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "तपाईंको गृह स्क्रिनमा Firefox सर्टकट थप्नुहोस्। विजेट थपेपछि, यसलाई सम्पादन गर्न छुनुहोस् र होल्ड गर्नुहोस् र फरक सर्टकट चयन गर्नुहोस्।"; +"TodayWidget.QuickActionGalleryDescription" = "तपाईंको गृह स्क्रिनमा Ecosia सर्टकट थप्नुहोस्। विजेट थपेपछि, यसलाई सम्पादन गर्न छुनुहोस् र होल्ड गर्नुहोस् र फरक सर्टकट चयन गर्नुहोस्।"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "द्रुत कार्यहरु"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox सर्टकटहरु"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia सर्टकटहरु"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "तपाईंको खुला ट्याबहरूमा सर्टकटहरू थप्नुहोस्।"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "द्रुत दृश्य"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox मा\nखोज्नुहोस्"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia मा\nखोज्नुहोस्"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "निजी ट्याबमा\nखोज्नुहोस्"; diff --git a/firefox-ios/Shared/nl.lproj/Default Browser.strings b/firefox-ios/Shared/nl.lproj/Default Browser.strings index 16b98ffa977ef..ef4b47391a090 100644 --- a/firefox-ios/Shared/nl.lproj/Default Browser.strings +++ b/firefox-ios/Shared/nl.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Van Firefox uw standaardbrowser maken is een stem voor een open, toegankelijk internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Van Ecosia uw standaardbrowser maken is een stem voor een open, toegankelijk internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Maak een beter internet uw standaard"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Meer info"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Koppelingen van websites, e-mail en berichten automatisch in Firefox openen."; +"DefaultBrowserCard.Description" = "Koppelingen van websites, e-mail en berichten automatisch in Ecosia openen."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Kies Firefox als uw standaardbrowser om snelheid, veiligheid en privacy automatisch te maken."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Kies Ecosia als uw standaardbrowser om snelheid, veiligheid en privacy automatisch te maken."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Breng uw dagelijkse surfen naar een hoger niveau"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokkeert gemiddeld meer dan 3.000 trackers per gebruiker per maand. Maak van ons uw standaardbrowser voor gemoedsrust op privacygebied."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokkeert gemiddeld meer dan 3.000 trackers per gebruiker per maand. Maak van ons uw standaardbrowser voor gemoedsrust op privacygebied."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox denkt aan uw privacy"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia denkt aan uw privacy"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Uw standaardbrowser wisselen"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tik op Standaard browser-app"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Selecteer Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Selecteer Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standaard browser-app"; diff --git a/firefox-ios/Shared/nl.lproj/Intro.strings b/firefox-ios/Shared/nl.lproj/Intro.strings index 21f85f96c03f7..1b26546744abf 100644 --- a/firefox-ios/Shared/nl.lproj/Intro.strings +++ b/firefox-ios/Shared/nl.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Aanmelden"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Welkom bij Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Welkom bij Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Beginnen met browsen"; diff --git a/firefox-ios/Shared/nl.lproj/Localizable.strings b/firefox-ios/Shared/nl.lproj/Localizable.strings index f815e2ea2cf67..e5ad126d756d3 100644 --- a/firefox-ios/Shared/nl.lproj/Localizable.strings +++ b/firefox-ios/Shared/nl.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Het kan een misconfiguratie zijn, of geknoei door een aanvaller. Ga door als u het mogelijke risico accepteert."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "De eigenaar van %@ heeft zijn of haar website niet juist geconfigureerd. Om uw gegevens tegen diefstal te beschermen, heeft Firefox geen verbinding met deze website gemaakt."; +"ErrorPages.CertWarning.Description" = "De eigenaar van %@ heeft zijn of haar website niet juist geconfigureerd. Om uw gegevens tegen diefstal te beschermen, heeft Ecosia geen verbinding met deze website gemaakt."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Deze verbinding is niet beveiligd"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox-startpagina"; +"Firefox.HomePage.Title" = "Ecosia-startpagina"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Startpagina aanpassen"; @@ -377,7 +377,7 @@ "Forward" = "Vooruit"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-account"; +"FxA.FirefoxAccount" = "Ecosia-account"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Account & apparaten beheren"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Meld u aan met uw camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Open Firefox op uw computer en ga naar firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Open Ecosia op uw computer en ga naar ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Klaar om te scannen"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Nu synchroniseren"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox-synchronisatie is verbonden met %@"; +"FxAPush_DeviceConnected_body" = "Ecosia-synchronisatie is verbonden met %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Synchronisatie verbonden"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "De verbinding van %@ is met succes verbroken."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "De verbinding van dit apparaat met Firefox-synchronisatie is met succes verbroken."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "De verbinding van dit apparaat met Ecosia-synchronisatie is met succes verbroken."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Synchronisatie verbroken"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Synchronisatie verbroken"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Een apparaat heeft de verbinding met Firefox-synchronisatie verbroken"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Een apparaat heeft de verbinding met Ecosia-synchronisatie verbroken"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-logo, de achtergrond wijzigen."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-logo, de achtergrond wijzigen."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Help"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Meld u aan om een lijst van tabbladen van uw andere apparaten weer te geven."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox-synchronisatie"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia-synchronisatie"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Geschiedenis"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Meer info"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Om de functie Automatisch invullen voor Firefox te gebruiken, moet u een apparaattoegangscode hebben ingeschakeld."; +"Logins.PasscodeRequirement.Warning" = "Om de functie Automatisch invullen voor Ecosia te gebruiken, moet u een apparaattoegangscode hebben ingeschakeld."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Neem uw wachtwoorden overal mee naartoe"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox-wachtwoorden automatisch invullen"; +"Logins.WelcomeView.Title2" = "Ecosia-wachtwoorden automatisch invullen"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Automatisch invullen inschakelen"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "OPGESLAGEN AANMELDINGEN"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Het lijkt erop dat Firefox eerder is gecrasht. Wilt u uw tabbladen herstellen?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Het lijkt erop dat Ecosia eerder is gecrasht. Wilt u uw tabbladen herstellen?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Markeren als gelezen"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Beginnen met surfen"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oeps! Firefox is gecrasht"; +"Oops! Firefox crashed" = "Oeps! Ecosia is gecrasht"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Open artikelen in de lezerweergave door op het boekpictogram te tikken zodra dit in de titelbalk verschijnt."; @@ -905,7 +905,7 @@ "Open Tabs" = "Open tabbladen"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kan de pagina niet openen, omdat deze een ongeldig adres heeft."; +"OpenURL.Error.Message" = "Ecosia kan de pagina niet openen, omdat deze een ongeldig adres heeft."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Kan pagina niet openen"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Hierdoor kunt u de afbeelding opslaan naar uw Filmrol."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vraagt om toegang tot uw foto’s"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vraagt om toegang tot uw foto’s"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Sluiten"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "De gegevens zijn ongeldig"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ga op uw apparaat naar ‘Instellingen’ > ‘Firefox’. Geef Firefox toegang tot uw camera."; +"ScanQRCode.PermissionError.Message.v100" = "Ga op uw apparaat naar ‘Instellingen’ > ‘Ecosia’. Geef Ecosia toegang tot uw camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR-code scannen"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Wisselen naar tabblad"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggesties"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggesties"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "De nieuwe zoekmachine verschijnt in de snelzoekbalk."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Een crashrapport verzenden, zodat Mozilla het probleem kan verhelpen?"; +"Send a crash report so Mozilla can fix the problem?" = "Een crashrapport verzenden, zodat Ecosia het probleem kan verhelpen?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Feedback verzenden"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Tabblad verzenden"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "U hebt geen andere aan deze Firefox-account gekoppelde apparaten beschikbaar voor synchronisatie."; +"SendTo.NoDevicesFound.Message" = "U hebt geen andere aan deze Ecosia-account gekoppelde apparaten beschikbaar voor synchronisatie."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Open Firefox, ga naar Instellingen en meld u aan om door te gaan."; +"SendTo.NotSignedIn.Message" = "Open Ecosia, ga naar Instellingen en meld u aan om door te gaan."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "U bent niet aangemeld bij uw Firefox-account."; +"SendTo.NotSignedIn.Title" = "U bent niet aangemeld bij uw Ecosia-account."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Verzenden"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tik om te beginnen"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox-synchronisatie"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia-synchronisatie"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Weergeven"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gegevensbeheer"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox stopt de synchronisatie met uw account, maar zal geen surfgegevens op dit apparaat verwijderen."; +"Settings.Disconnect.Body" = "Ecosia stopt de synchronisatie met uw account, maar zal geen surfgegevens op dit apparaat verwijderen."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Verbinding met synchronisatie verbreken"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Synchronisatie-instellingen"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-account"; +"Settings.FxA.Title" = "Ecosia-account"; /* General settings section title */ "Settings.General.SectionName" = "Algemeen"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Kies wat wordt getoond als de startpagina."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Kies wat er op de Firefox-startpagina staat."; +"Settings.Home.Option.Description.v101" = "Kies wat er op de Ecosia-startpagina staat."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Terugspringen"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startpagina"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Kies wat u ziet als u Firefox opnieuw opent."; +"Settings.Home.Option.StartAtHome.Description" = "Kies wat u ziet als u Ecosia opnieuw opent."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Laatste tabblad"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Achtergrond"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox-achtergrond, amethistpatroon."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia-achtergrond, amethistpatroon."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-achtergrond, duinpatroon."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-achtergrond, duinpatroon."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-achtergrond, felblauw patroon."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-achtergrond, felblauw patroon."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standaard heldere achtergrond."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox-achtergrond, zonsopgangpatroon."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia-achtergrond, zonsopgangpatroon."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Schakelaar startpagina-achtergrondcyclus"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-achtergrond, heuvelpatroon in schemering."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-achtergrond, heuvelpatroon in schemering."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassieke %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limited Edition"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Wijzig uw achtergrond door op het Firefox-startpaginalogo te tikken"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Wijzig uw achtergrond door op het Ecosia-startpaginalogo te tikken"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Achtergrond"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Aangepast"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox-startpagina"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia-startpagina"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startpagina"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Afbeeldingen blokkeren"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Bij het openen van Firefox"; +"Settings.OfferClipboardBar.Status" = "Bij het openen van Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Bij het openen van %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Bij het openen van Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Bij het openen van Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Aanbieden gekopieerde koppelingen te openen"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Meer info."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla streeft ernaar alleen te verzamelen wat nodig is om Firefox voor iedereen aan te bieden en te verbeteren."; +"Settings.SendUsage.Message" = "Ecosia streeft ernaar alleen te verzamelen wat nodig is om Ecosia voor iedereen aan te bieden en te verbeteren."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Gebruiksgegevens verzenden"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Nieuw tabblad openen"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri-snelkoppelingen gebruiken om Firefox snel te openen via Siri"; +"Settings.Siri.SectionDescription" = "Siri-snelkoppelingen gebruiken om Ecosia snel te openen via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-snelkoppelingen"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Meer info."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox kan af en toe onderzoeken installeren en uitvoeren."; +"Settings.Studies.Toggle.Message" = "Ecosia kan af en toe onderzoeken installeren en uitvoeren."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Onderzoeken"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Laden op de achtergrond"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Laden in Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Laden in Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Openen in Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Openen in Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Zoeken in Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Zoeken in Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiëren"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Plakken & Gaan"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Zoeken met Firefox"; +"UIMenuItem.SearchWithFirefox" = "Zoeken met Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ongelezen"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Gisteren"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "U hebt geen tabbladen geopend in Firefox op uw andere apparaten."; +"You don’t have any tabs open in Firefox on your other devices." = "U hebt geen tabbladen geopend in Ecosia op uw andere apparaten."; /* Your Rights settings section title */ "Your Rights" = "Uw rechten"; diff --git a/firefox-ios/Shared/nl.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/nl.lproj/PrivateBrowsing.strings index ce9632ddbf988..b365170b7f2f1 100644 --- a/firefox-ios/Shared/nl.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/nl.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Openen in nieuw privétabblad"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox zal geen geschiedenis of cookies onthouden, maar nieuwe bladwijzers zullen worden opgeslagen."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia zal geen geschiedenis of cookies onthouden, maar nieuwe bladwijzers zullen worden opgeslagen."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Meer info"; diff --git a/firefox-ios/Shared/nl.lproj/Today.strings b/firefox-ios/Shared/nl.lproj/Today.strings index 14ccb2fc4f53a..9dbfb2c884d42 100644 --- a/firefox-ios/Shared/nl.lproj/Today.strings +++ b/firefox-ios/Shared/nl.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Privétabbladen sluiten"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefox-snelkoppelingen toevoegen aan uw startscherm."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosia-snelkoppelingen toevoegen aan uw startscherm."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Naar gekopieerde koppeling"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Geen open tabbladen."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox openen"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia openen"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privé zoeken"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Voeg een Firefox-snelkoppeling toe aan uw startscherm. Houd, nadat u deze hebt toegevoegd, de widget ingedrukt om deze te bewerken en een andere snelkoppeling te selecteren."; +"TodayWidget.QuickActionGalleryDescription" = "Voeg een Ecosia-snelkoppeling toe aan uw startscherm. Houd, nadat u deze hebt toegevoegd, de widget ingedrukt om deze te bewerken en een andere snelkoppeling te selecteren."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Snelle acties"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-snelkoppelingen"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-snelkoppelingen"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Snelkoppelingen naar uw open tabbladen toevoegen."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Snel bekijken"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Zoeken in Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Zoeken in Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Zoeken in privétabblad"; diff --git a/firefox-ios/Shared/nn.lproj/Default Browser.strings b/firefox-ios/Shared/nn.lproj/Default Browser.strings index 4eb597785a7c7..b1a4c5492c229 100644 --- a/firefox-ios/Shared/nn.lproj/Default Browser.strings +++ b/firefox-ios/Shared/nn.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Å gjere Firefox til din standardnettlesar er ei stemme for eit ope, tilgjengeleg internett."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Å gjere Ecosia til din standardnettlesar er ei stemme for eit ope, tilgjengeleg internett."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard for eit betre internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Finn ut korleis"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Vel at lenker frå nettstadar, e-postmeldingar og meldingar skal opnast automatisk i Firefox."; +"DefaultBrowserCard.Description" = "Vel at lenker frå nettstadar, e-postmeldingar og meldingar skal opnast automatisk i Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Vel Firefox som din standardnettlesar for å automatisk få fart, sikkerheit og personvern."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Vel Ecosia som din standardnettlesar for å automatisk få fart, sikkerheit og personvern."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Forbetre den daglege surfinga di"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokkerer i gjenomsnitt over 3000 sporarar per brukar kvar månad. Gjer oss til din standardnettlesar for å ta vare på personvernet."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokkerer i gjenomsnitt over 3000 sporarar per brukar kvar månad. Gjer oss til din standardnettlesar for å ta vare på personvernet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox tek vare på personvernet ditt"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia tek vare på personvernet ditt"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Endre standard nettlesar"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Trykk på «Standard nettlesarapp»"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Vel Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Vel Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standard nettlesar-app"; diff --git a/firefox-ios/Shared/nn.lproj/Intro.strings b/firefox-ios/Shared/nn.lproj/Intro.strings index e1dd205abb13e..ff4bfe6959d8a 100644 --- a/firefox-ios/Shared/nn.lproj/Intro.strings +++ b/firefox-ios/Shared/nn.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Logg inn"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Velkomen til Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Velkomen til Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Begynn å surfe"; diff --git a/firefox-ios/Shared/nn.lproj/Localizable.strings b/firefox-ios/Shared/nn.lproj/Localizable.strings index 77653548b1683..7b1ac2870d49c 100644 --- a/firefox-ios/Shared/nn.lproj/Localizable.strings +++ b/firefox-ios/Shared/nn.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Det kan vere ein feilkonfigurasjon eller ei manipulering av ein angripar. Fortset om du godtek risikoen."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Eigaren av %@ har konfigurerat nettstaden sin feil. For å verne informasjonen din frå å verte stolen, har Firefox ikkje kopla seg til denne nettstaden."; +"ErrorPages.CertWarning.Description" = "Eigaren av %@ har konfigurerat nettstaden sin feil. For å verne informasjonen din frå å verte stolen, har Ecosia ikkje kopla seg til denne nettstaden."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Denne tilkoplinga er usikker"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox startside"; +"Firefox.HomePage.Title" = "Ecosia startside"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Tilpass startsida"; @@ -377,7 +377,7 @@ "Forward" = "Fram"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-konto"; +"FxA.FirefoxAccount" = "Ecosia-konto"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Handsam kontoen og einingar"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Logg inn med kameraet ditt"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Opne Firefox på datamaskina di og gå til firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Opne Ecosia på datamaskina di og gå til ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Klar til å skanne"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synkroniser no"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync har kopla til %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync har kopla til %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync er tilkopla"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ har blitt kopla frå."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Denne eininga er kopla frå Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Denne eininga er kopla frå Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync er fråkopla"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync er fråkopla"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ei eining har blitt kopla frå Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ei eining har blitt kopla frå Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox-logo, endre bakgrunn."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia-logo, endre bakgrunn."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Hjelp"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Logg inn for å sjå ei liste med faner frå dei andre einingane dine."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historikk"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Les meir"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "For å bruke autofyll-funksjonen for Firefox, må du ha ein tryggingskode for eininga påslått."; +"Logins.PasscodeRequirement.Warning" = "For å bruke autofyll-funksjonen for Ecosia, må du ha ein tryggingskode for eininga påslått."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ta med deg passorda dine overalt"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autofyll Firefox-passord"; +"Logins.WelcomeView.Title2" = "Autofyll Ecosia-passord"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Slå på autofyll"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "LAGRA INNLOGGINGAR"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Det ser ut til at Firefox krasja tidlegare. Vil du rette oppatt fanene dine?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Det ser ut til at Ecosia krasja tidlegare. Vil du rette oppatt fanene dine?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marker som lesen"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Byrj surfinga"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ops! Firefox krasja"; +"Oops! Firefox crashed" = "Ops! Ecosia krasja"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Opne artiklar i lesevising ved å trykkje på bok-ikonet når du ser det i adresselinja."; @@ -905,7 +905,7 @@ "Open Tabs" = "Opne faner"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kan ikkje opne sida fordi ho har ei ugyldig adresse."; +"OpenURL.Error.Message" = "Ecosia kan ikkje opne sida fordi ho har ei ugyldig adresse."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Klarte ikkje å opne sida"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Dette vil tillate lagring av bildet til kamerarullen din."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ønskjer tilgang til bilda dine"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ønskjer tilgang til bilda dine"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Lat att"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Ugyldige data"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Gå til einings-«Innstillingar» > «Firefox». Gi Firefox tilgang til kameraet."; +"ScanQRCode.PermissionError.Message.v100" = "Gå til einings-«Innstillingar» > «Ecosia». Gi Ecosia tilgang til kameraet."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skann QR-kode"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Byt til fane"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-forslag"; +"Search.SuggestSectionTitle.v102" = "Ecosia-forslag"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Den nye søkjemotoren vil visast i hurtigsøkjelinja."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Sende inn ein krasjrapport slik at Mozilla kan ordne problemet?"; +"Send a crash report so Mozilla can fix the problem?" = "Sende inn ein krasjrapport slik at Ecosia kan ordne problemet?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Send tilbakemelding"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Send fane"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Du har ingen andre einingar tilkopla til denne Firefox-kontoen som er tilgjengelege for synkronisering."; +"SendTo.NoDevicesFound.Message" = "Du har ingen andre einingar tilkopla til denne Ecosia-kontoen som er tilgjengelege for synkronisering."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Opne Firefox, gå till Instillingar og logg inn for å halde fram."; +"SendTo.NotSignedIn.Message" = "Opne Ecosia, gå till Instillingar og logg inn for å halde fram."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Du er ikkje innlogga med Firefox-kontoen din."; +"SendTo.NotSignedIn.Title" = "Du er ikkje innlogga med Ecosia-kontoen din."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Send"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Trykk for å starte"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vis"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Datahandsaming"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox vil slutta å synkronisera med kontoen din, men vil ikkje sletta nokon av nettlesardataa dine på denne eininga."; +"Settings.Disconnect.Body" = "Ecosia vil slutta å synkronisera med kontoen din, men vil ikkje sletta nokon av nettlesardataa dine på denne eininga."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Kople frå Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync-innstillingar"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-konto"; +"Settings.FxA.Title" = "Ecosia-konto"; /* General settings section title */ "Settings.General.SectionName" = "Generelt"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Vel kva som skal visast som startside."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Vel kva som skal inkluderast på Firefox-startsida."; +"Settings.Home.Option.Description.v101" = "Vel kva som skal inkluderast på Ecosia-startsida."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Gå tilbake til"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startside"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Vel kva som skal visast når du går tilbake til Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Vel kva som skal visast når du går tilbake til Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Siste fane"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Bakgrunnsbilde"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox bakgrunnsbilde, ametystmønster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia bakgrunnsbilde, ametystmønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox-bakgrunnsbilde, strandås-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia-bakgrunnsbilde, strandås-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox-bakgrunnsbilde, cerulean-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia-bakgrunnsbilde, cerulean-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standard bakgrunnsbilde."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox bakgrunnsbilde, soloppgangsmønster."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia bakgrunnsbilde, soloppgangsmønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Knapp for å skifte bakgrunn på startsida"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox-bakgrunnsbilde, skumringsås-mønster."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia-bakgrunnsbilde, skumringsås-mønster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassisk %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Avgrensa utgåve"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Endre bakgrunnsbilde ved å trykkje på Firefox-logoen på startsida"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Endre bakgrunnsbilde ved å trykkje på Ecosia-logoen på startsida"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Bakgrunnsbilde"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Tilpassa"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox startside"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia startside"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startsida"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokker bilde"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Når du opnar Firefox"; +"Settings.OfferClipboardBar.Status" = "Når du opnar Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Når du opnar %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Når du opnar Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Når du opnar Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Tilby å opne kopierte lenker"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Les meir."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strevar etter å berre samle inn det vi treng for å tilby og forbetre Firefox for alle."; +"Settings.SendUsage.Message" = "Ecosia strevar etter å berre samle inn det vi treng for å tilby og forbetre Ecosia for alle."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Send bruksdata"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Opne ny fane"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Bruk Siri-snarvegar for å raskt opne Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Bruk Siri-snarvegar for å raskt opne Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-snarvegar"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Les meir."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox kan installere og køyre undersøkingar av og til."; +"Settings.Studies.Toggle.Message" = "Ecosia kan installere og køyre undersøkingar av og til."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Undersøkingar"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Last inn i bakgrunnen"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Lastar i Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Lastar i Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Opne i Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Opne i Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Søk i Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Søk i Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopier"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Lim inn og gå til sida"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Søk med Firefox"; +"UIMenuItem.SearchWithFirefox" = "Søk med Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ulesen"; @@ -1793,7 +1793,7 @@ "Yesterday" = "I går"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Du har ingen faner opne i Firefox på andre einingar."; +"You don’t have any tabs open in Firefox on your other devices." = "Du har ingen faner opne i Ecosia på andre einingar."; /* Your Rights settings section title */ "Your Rights" = "Dine rettar"; diff --git a/firefox-ios/Shared/nn.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/nn.lproj/PrivateBrowsing.strings index 4048ddb971f91..46c0ac0bb8a19 100644 --- a/firefox-ios/Shared/nn.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/nn.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Opne i ny privat fane"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox kjem ikkje til å hugse historikk eller infokapslar, men nye bokmerke vert lagra."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia kjem ikkje til å hugse historikk eller infokapslar, men nye bokmerke vert lagra."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Les meir"; diff --git a/firefox-ios/Shared/nn.lproj/Today.strings b/firefox-ios/Shared/nn.lproj/Today.strings index aaed48034306e..d5d5cc0e640a2 100644 --- a/firefox-ios/Shared/nn.lproj/Today.strings +++ b/firefox-ios/Shared/nn.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Lat att private faner"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Legg til Firefox-snarvegar på heim-skjermen."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Legg til Ecosia-snarvegar på heim-skjermen."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Gå til kopiert lenke"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ingen opne faner."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Opne Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Opne Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privat søk"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Legg til ein Firefox-snarveg til heim-skjermen. Når du har lagt til widgeten, trykkjer du på og held nede for å redigere han og vel ein annan snarveg."; +"TodayWidget.QuickActionGalleryDescription" = "Legg til ein Ecosia-snarveg til heim-skjermen. Når du har lagt til widgeten, trykkjer du på og held nede for å redigere han og vel ein annan snarveg."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Hurtighandlingar"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-snarvegar"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-snarvegar"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Legg til snarvegar til dei opne fanene dine."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Snøggtitt"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Søk i Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Søk i Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Søk i privat fane"; diff --git a/firefox-ios/Shared/oc.lproj/Intro.strings b/firefox-ios/Shared/oc.lproj/Intro.strings index d288a1fcb33d9..dbdd7dcc599cd 100644 --- a/firefox-ios/Shared/oc.lproj/Intro.strings +++ b/firefox-ios/Shared/oc.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Se connectar"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Benvenguda dins Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Benvenguda dins Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Començar de navegar"; diff --git a/firefox-ios/Shared/oc.lproj/Localizable.strings b/firefox-ios/Shared/oc.lproj/Localizable.strings index b96c6afce7124..3643d8157b7e3 100644 --- a/firefox-ios/Shared/oc.lproj/Localizable.strings +++ b/firefox-ios/Shared/oc.lproj/Localizable.strings @@ -260,7 +260,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pagina d'acuèlh de Firefox"; +"Firefox.HomePage.Title" = "Pagina d'acuèlh de Ecosia"; /* On each Pocket Stories on the Firefox Homepage, this label appears and indicates the number of minutes to read an article. Minutes should be abbreviated due to space constraints. %d represents the number of minutes */ "FirefoxHome.Pocket.Minutes.v99" = "%S min"; @@ -278,7 +278,7 @@ "Forward" = "Pagina seguenta"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Compte Firefox"; +"FxA.FirefoxAccount" = "Compte Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gestion del compte"; @@ -290,7 +290,7 @@ "FxA.SyncNow" = "Sincronizar ara"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync s’es connectat a %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync s’es connectat a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync connectat"; @@ -299,7 +299,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ es estat corrèctament desconnectat."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Aqueste periferic es estat corrèctament desconnectat de Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Aqueste periferic es estat corrèctament desconnectat de Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sincronizacion desconnectada"; @@ -308,7 +308,7 @@ "FxAPush_DeviceDisconnected_title" = "Sincronizacion desconnectada"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un periferic es estat desconnectat de Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un periferic es estat desconnectat de Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ajuda"; @@ -338,7 +338,7 @@ "HistoryPanel.EmptySyncedTabsNullState.Description" = "Los onglets de vòstres autres periferics s’afichan aquí."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Istoric"; @@ -506,7 +506,7 @@ "LoginsList.Search.Cancel" = "Anullar"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sembla que Firefox a fracassat. Volètz restablir los onglets ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Sembla que Ecosia a fracassat. Volètz restablir los onglets ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar coma legit"; @@ -638,7 +638,7 @@ "Okay" = "OK"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Firefox a plantat"; +"Oops! Firefox crashed" = "Ecosia a plantat"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Obrir d'articles en mòde lectura en tocant l’icòna del libre quand apareis dins la barra de títol."; @@ -650,7 +650,7 @@ "Open Tabs" = "Dobrir los onglets"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox pòt pas dobrir la pagina perque l’adreça es invalida."; +"OpenURL.Error.Message" = "Ecosia pòt pas dobrir la pagina perque l’adreça es invalida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Dobertura impossibla de la pagina"; @@ -662,7 +662,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Aquò vos permet d’enregistrar l’imatge dins Pellicula."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vòl accedir a vòstras fòtos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vòl accedir a vòstras fòtos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Tampar"; @@ -764,7 +764,7 @@ "Search.ThirdPartyEngines.OK" = "D'acòrd"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Enviar un rapòrt de plantatge que Mozilla pòsca corregir lo problèma ?"; +"Send a crash report so Mozilla can fix the problem?" = "Enviar un rapòrt de plantatge que Ecosia pòsca corregir lo problèma ?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar un comentari"; @@ -794,13 +794,13 @@ "SendTo.NavBar.Title" = "Enviar l’onglet"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Avètz pas cap d'autre aparelh prèst a èsser sincronizat e connectat a aqueste compte Firefox."; +"SendTo.NoDevicesFound.Message" = "Avètz pas cap d'autre aparelh prèst a èsser sincronizat e connectat a aqueste compte Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Obrissètz Firefox, anatz als paramètres e connectatz per aviar."; +"SendTo.NotSignedIn.Message" = "Obrissètz Ecosia, anatz als paramètres e connectatz per aviar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Sètz pas connectat a vòstre compte Firefox."; +"SendTo.NotSignedIn.Title" = "Sètz pas connectat a vòstre compte Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -818,7 +818,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tocatz per començar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Veire"; @@ -920,7 +920,7 @@ "Settings.FxA.Sync.SectionName" = "Paramètres de sincronizacion"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Compte Firefox"; +"Settings.FxA.Title" = "Compte Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -947,7 +947,7 @@ "Settings.NewTab.Option.BlankPage" = "Pagina voida"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Acuèlh de Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Acuèlh de Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pagina d’acuèlh"; @@ -965,10 +965,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Causissètz çò que cal mostrar en dobrir un onglet novèl"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "En dobrir Firefox"; +"Settings.OfferClipboardBar.Status" = "En dobrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "En dobrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "En dobrir Ecosia"; /* Label used as an item in Settings. When touched it will open a dialog to configure the open with (mail links) behavior. */ "Settings.OpenWith.SectionName" = "Aplicacion de corrièr electronic"; @@ -986,7 +986,7 @@ "Settings.SendUsage.Link" = "Ne saber mai."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla s’esfòrça de reculhir pas que las donadas necessàrias per vos ofrir e melhorar Firefox."; +"Settings.SendUsage.Message" = "Ecosia s’esfòrça de reculhir pas que las donadas necessàrias per vos ofrir e melhorar Ecosia."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Mandar de donadas d’utilizacion"; @@ -998,7 +998,7 @@ "Settings.Siri.OpenTabShortcut" = "Dobrir un onglet novèl"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilizar las acorcha Siri per obrir rapidament Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Utilizar las acorcha Siri per obrir rapidament Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Acorcha Siri"; @@ -1046,13 +1046,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Cargar en rèire plan"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargament dins Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Cargament dins Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Dobrir dins Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Dobrir dins Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Cercar dins Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Cercar dins Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Afichar las suggestions de recèrca"; @@ -1151,7 +1151,7 @@ "UIMenuItem.PasteGo" = "Pegar e i anar"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Cercar amb Firefox"; +"UIMenuItem.SearchWithFirefox" = "Cercar amb Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "pas legit"; @@ -1178,7 +1178,7 @@ "Yesterday" = "Ièr"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Avètz pas cap d'onglet obèrt dins Firefox sus vòstres autres aparelhs."; +"You don’t have any tabs open in Firefox on your other devices." = "Avètz pas cap d'onglet obèrt dins Ecosia sus vòstres autres aparelhs."; /* Your Rights settings section title */ "Your Rights" = "Vòstres dreits"; diff --git a/firefox-ios/Shared/oc.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/oc.lproj/PrivateBrowsing.strings index e09ff42421eff..994335a6dc81a 100644 --- a/firefox-ios/Shared/oc.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/oc.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Dobrir un onglet privat novèl"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox gardarà pas d'istoric nimai de cookies, pasmens los marcapaginas novèls seràn servats."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia gardarà pas d'istoric nimai de cookies, pasmens los marcapaginas novèls seràn servats."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Ne saber mai"; diff --git a/firefox-ios/Shared/or.lproj/Default Browser.strings b/firefox-ios/Shared/or.lproj/Default Browser.strings index cb5dc8c881748..5cbe713540abb 100644 --- a/firefox-ios/Shared/or.lproj/Default Browser.strings +++ b/firefox-ios/Shared/or.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefoxକୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର କରିବା ଏକ ଖୋଲା, ଉପଲବ୍ଧ ଇଣ୍ଟରନେଟ୍ ପାଇଁ ଏକ ଭୋଟ୍ ଅଟେ।"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosiaକୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର କରିବା ଏକ ଖୋଲା, ଉପଲବ୍ଧ ଇଣ୍ଟରନେଟ୍ ପାଇଁ ଏକ ଭୋଟ୍ ଅଟେ।"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "ଏକ ଉତ୍ତମ ଇଣ୍ଟରନେଟ୍ କୁ ଡିଫଲ୍ଟ କରନ୍ତୁ"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "ଶିଖନ୍ତୁ କିପରି"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Firefoxରେ ସ୍ୱତଃସ୍ପୃତ ଭାବେ ଖୋଲିବାକୁ ୱେବସାଇଟ୍, ଇମେଲ୍ ଏବଂ ମେସେଜଗୁଡିକରୁ ଲିଙ୍କ୍ ସେଟ୍ କରନ୍ତୁ।"; +"DefaultBrowserCard.Description" = "Ecosiaରେ ସ୍ୱତଃସ୍ପୃତ ଭାବେ ଖୋଲିବାକୁ ୱେବସାଇଟ୍, ଇମେଲ୍ ଏବଂ ମେସେଜଗୁଡିକରୁ ଲିଙ୍କ୍ ସେଟ୍ କରନ୍ତୁ।"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "ବେଗ, ନିରାପତ୍ତା ଏବଂ ଗୋପନୀୟତାକୁ ସ୍ୱୟଂଚାଳିତ କରିବାକୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର୍ ଭାବରେ Firefox ଚୟନ କରନ୍ତୁ।"; +"DefaultBrowserCard.NextLevel.Description.v108" = "ବେଗ, ନିରାପତ୍ତା ଏବଂ ଗୋପନୀୟତାକୁ ସ୍ୱୟଂଚାଳିତ କରିବାକୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର୍ ଭାବରେ Ecosia ଚୟନ କରନ୍ତୁ।"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "ଦୈନନ୍ଦିନ ବ୍ରାଉଜିଂକୁ ଉନ୍ନତ କରନ୍ତୁ"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ପ୍ରତି ମାସରେ ହାରାହାରି ୩,୦୦୦ରୁ ଅଧିକ ଟ୍ରାକରକୁ ବ୍ଲକ କରିଥାଏ । ମନର ଗୋପନୀୟତା ଶାନ୍ତି ପାଇଁ ଆମକୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର ତିଆରି କରନ୍ତୁ।"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ପ୍ରତି ମାସରେ ହାରାହାରି ୩,୦୦୦ରୁ ଅଧିକ ଟ୍ରାକରକୁ ବ୍ଲକ କରିଥାଏ । ମନର ଗୋପନୀୟତା ଶାନ୍ତି ପାଇଁ ଆମକୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର ତିଆରି କରନ୍ତୁ।"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefoxରେ ଗୋପନୀୟତା ଆଚ୍ଛାଦିତ ଅଛି"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosiaରେ ଗୋପନୀୟତା ଆଚ୍ଛାଦିତ ଅଛି"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "ଆପଣଙ୍କ ଡିଫଲ୍ଟ ବ୍ରାଉଜର୍ ବଦଳାନ୍ତୁ"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. ଡିଫଲ୍ଟ ବ୍ରାଉଜର ଆପ୍ ଟ୍ୟାପ୍ କରନ୍ତୁ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox ଚୟନ କରନ୍ତୁ"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia ଚୟନ କରନ୍ତୁ"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "ପୂର୍ବନିର୍ଦ୍ଧାରିତ ବ୍ରାଉଜର ଆପ"; diff --git a/firefox-ios/Shared/or.lproj/Intro.strings b/firefox-ios/Shared/or.lproj/Intro.strings index b92d99cc5df46..ece5f95bd63f2 100644 --- a/firefox-ios/Shared/or.lproj/Intro.strings +++ b/firefox-ios/Shared/or.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ସାଇନ ଇନ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox ରେ ଆପଣଙ୍କୁ ସ୍ୱାଗତ କରୁଅଛୁ"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia ରେ ଆପଣଙ୍କୁ ସ୍ୱାଗତ କରୁଅଛୁ"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ବ୍ରାଉଜିଂ ଆରମ୍ଭ କରନ୍ତୁ"; diff --git a/firefox-ios/Shared/or.lproj/Localizable.strings b/firefox-ios/Shared/or.lproj/Localizable.strings index 9bc458f6df3b3..1b8b9ce01909c 100644 --- a/firefox-ios/Shared/or.lproj/Localizable.strings +++ b/firefox-ios/Shared/or.lproj/Localizable.strings @@ -392,7 +392,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ସଫଳତାର ସହିତ ବିଛିନ୍ନ ହୋଇଛି"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ଏହି ଡିଭାଇସ୍ Firefox ସିଙ୍କ୍ ରୁ ସଫଳତାର ସହ ବିଚ୍ଛିନ୍ନ ହୋଇଛି।"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ଏହି ଡିଭାଇସ୍ Ecosia ସିଙ୍କ୍ ରୁ ସଫଳତାର ସହ ବିଚ୍ଛିନ୍ନ ହୋଇଛି।"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ସମୟାନୁସାମୟିକ ସଂଯୋଗ ବିଚ୍ୟୁତି ହୋଇଗଲା"; @@ -401,10 +401,10 @@ "FxAPush_DeviceDisconnected_title" = "ସମୟାନୁସାମୟିକ ସଂଯୋଗ ବିଚ୍ୟୁତି ହୋଇଗଲା"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ଏକ ଡିଭାଇସ୍ Firefox ସିଙ୍କ୍ ରୁ ସଫଳତାର ସହ ବିଚ୍ଛିନ୍ନ ହୋଇଛି।"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ଏକ ଡିଭାଇସ୍ Ecosia ସିଙ୍କ୍ ରୁ ସଫଳତାର ସହ ବିଚ୍ଛିନ୍ନ ହୋଇଛି।"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox ଲୋଗୋ, ୱାଲ୍ପେପର ପରିବର୍ତ୍ତନ କରନ୍ତୁ।"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia ଲୋଗୋ, ୱାଲ୍ପେପର ପରିବର୍ତ୍ତନ କରନ୍ତୁ।"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ସହଯୋଗ"; @@ -647,7 +647,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ଲାଗୁଛି ଗତଥର Firefox ବନ୍ଦ ହୋଇଗଲା । ପୁରୁଣା ଟ୍ୟାବ ଫେରାଇବାକୁ ଚାହାନ୍ତି କି?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ଲାଗୁଛି ଗତଥର Ecosia ବନ୍ଦ ହୋଇଗଲା । ପୁରୁଣା ଟ୍ୟାବ ଫେରାଇବାକୁ ଚାହାନ୍ତି କି?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "ପଠିତ ଭାବେ ଚିହ୍ନଟ କରନ୍ତୁ"; @@ -914,7 +914,7 @@ "Settings.DataManagement.SearchLabel" = "ସ୍କ୍ରିପ୍ଟଗୁଡ଼ିକୁ ଛାଣନ୍ତୁ"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ସହ ସିଙ୍କ କରିବା ବନ୍ଦ କରିଦେଲେ ମଧ୍ୟ ଆପଣଙ୍କ ଏହି ଡିଭାଇସରେ ବ୍ରାଉଜିଂ ଡାଟା ଲିଭାଇବ ନାହିଁ ।"; +"Settings.Disconnect.Body" = "Ecosia ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ସହ ସିଙ୍କ କରିବା ବନ୍ଦ କରିଦେଲେ ମଧ୍ୟ ଆପଣଙ୍କ ଏହି ଡିଭାଇସରେ ବ୍ରାଉଜିଂ ଡାଟା ଲିଭାଇବ ନାହିଁ ।"; /* Cancel action button in alert when user is prompted for disconnect */ "Settings.Disconnect.CancelButton" = "ବାତିଲ କରନ୍ତୁ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/ClearHistoryConfirm.strings b/firefox-ios/Shared/pa-IN.lproj/ClearHistoryConfirm.strings index 87d9f14f1c59d..26c1456ebb8f4 100644 --- a/firefox-ios/Shared/pa-IN.lproj/ClearHistoryConfirm.strings +++ b/firefox-ios/Shared/pa-IN.lproj/ClearHistoryConfirm.strings @@ -5,5 +5,5 @@ "OK" = "ਠੀਕ ਹੈ"; /* Description of the confirmation dialog shown when a user tries to clear history that's synced to another device. */ -"This action will clear all of your private data, including history from your synced devices." = "ਇਹ ਕਾਰਵਾਈ ਤੁਹਾਡੀਆਂ ਸਿੰਕ ਕੀਤੀਆਂ ਡਿਵਾਈਸਾਂ ਤੋਂ ਅਤੀਤ ਸਮੇਤ, ਤੁਹਾਡੇ ਨਿੱਜੀ ਡੇਟਾ ਨੂੰ ਸਾਫ਼ ਕਰ ਦੇਵੇਗੀ।"; +"This action will clear all of your private data, including history from your synced devices." = "ਇਹ ਕਾਰਵਾਈ ਤੁਹਾਡੀਆਂ ਸਿੰਕ ਕੀਤੀਆਂ ਡਿਵਾਈਸਾਂ ਤੋਂ ਅਤੀਤ ਸਮੇਤ, ਤੁਹਾਡੇ ਪ੍ਰਾਈਵੇਟ ਡਾਟੇ ਨੂੰ ਸਾਫ਼ ਕਰ ਦੇਵੇਗੀ।"; diff --git a/firefox-ios/Shared/pa-IN.lproj/ClearPrivateData.strings b/firefox-ios/Shared/pa-IN.lproj/ClearPrivateData.strings index 2c4b8425d8155..8430b1bb20518 100644 --- a/firefox-ios/Shared/pa-IN.lproj/ClearPrivateData.strings +++ b/firefox-ios/Shared/pa-IN.lproj/ClearPrivateData.strings @@ -8,7 +8,7 @@ "Cookies" = "ਕੂਕੀਜ਼"; /* Settings item for deleting downloaded files */ -"Downloaded Files" = "ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਫਾਇਲਾਂ"; +"Downloaded Files" = "ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਫਾਈਲਾਂ"; /* Settings item for clearing website data */ "Offline Website Data" = "ਆਫ਼ਲਾਈਨ ਵੈੱਬਸਾਈਟ ਡਾਟਾ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/ClearPrivateDataConfirm.strings b/firefox-ios/Shared/pa-IN.lproj/ClearPrivateDataConfirm.strings index 7a0ce743ab781..558ac928382e7 100644 --- a/firefox-ios/Shared/pa-IN.lproj/ClearPrivateDataConfirm.strings +++ b/firefox-ios/Shared/pa-IN.lproj/ClearPrivateDataConfirm.strings @@ -5,5 +5,5 @@ "OK" = "ਠੀਕ ਹੈ"; /* Description of the confirmation dialog shown when a user tries to clear their private data. */ -"This action will clear all of your private data. It cannot be undone." = "ਇਹ ਕਾਰਵਾਈ ਤੁਹਾਡੇ ਸਾਰੇ ਨਿੱਜੀ ਡਾਟੇ ਨੂੰ ਸਾਫ਼ ਕਰ ਦੇਵੇਗੀ। ਇਸ ਨੂੰ ਵਾਪਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"; +"This action will clear all of your private data. It cannot be undone." = "ਇਹ ਕਾਰਵਾਈ ਤੁਹਾਡੇ ਸਾਰੇ ਪ੍ਰਾਈਵੇਟ ਡਾਟੇ ਨੂੰ ਸਾਫ਼ ਕਰ ਦੇਵੇਗੀ। ਇਸ ਨੂੰ ਵਾਪਸ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"; diff --git a/firefox-ios/Shared/pa-IN.lproj/Default Browser.strings b/firefox-ios/Shared/pa-IN.lproj/Default Browser.strings index faf79ef228e83..cc1208d518b48 100644 --- a/firefox-ios/Shared/pa-IN.lproj/Default Browser.strings +++ b/firefox-ios/Shared/pa-IN.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox ਨੂੰ ਆਪਣਾ ਮੂਲ ਬਰਾਊਜ਼ਰ ਬਨਾਉਣਾ ਆਜ਼ਾਦ, ਪਹੁੰਚ ਵਿੱਚ ਇੰਟਰਨੈੱਟ ਲਈ ਵੋਟ ਪਾਉਣਾ ਹੈ।"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia ਨੂੰ ਆਪਣਾ ਮੂਲ ਬਰਾਊਜ਼ਰ ਬਨਾਉਣਾ ਆਜ਼ਾਦ, ਪਹੁੰਚ ਵਿੱਚ ਇੰਟਰਨੈੱਟ ਲਈ ਵੋਟ ਪਾਉਣਾ ਹੈ।"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "ਬੇਹਤਰ ਇੰਟਰਨੈੱਟ ਲਈ ਮੂਲ"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "ਹੋਰ ਸਿੱਖੋ"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "ਵੈੱਬਸਾਈਟਾਂ, ਈਮੇਲਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਵਿੱਚੋਂ ਲਿੰਕਾਂ ਨੂੰ Firefox ਵਿੱਚ ਸਿੱਧਾ ਖੋਲ੍ਹਣ ਲਈ ਸੈੱਟ ਕਰੋ।"; +"DefaultBrowserCard.Description" = "ਵੈੱਬਸਾਈਟਾਂ, ਈਮੇਲਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਵਿੱਚੋਂ ਲਿੰਕਾਂ ਨੂੰ Ecosia ਵਿੱਚ ਸਿੱਧਾ ਖੋਲ੍ਹਣ ਲਈ ਸੈੱਟ ਕਰੋ।"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "ਸਪੀਡ, ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ ਨੂੰ ਆਪਣੇ-ਆਪ ਵਰਤਣ ਲਈ Firefox ਨੂੰ ਆਪਣਾ ਮੂਲ ਬਰਾਊਜ਼ਰ ਬਣਾਓ।"; +"DefaultBrowserCard.NextLevel.Description.v108" = "ਸਪੀਡ, ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ ਨੂੰ ਆਪਣੇ-ਆਪ ਵਰਤਣ ਲਈ Ecosia ਨੂੰ ਆਪਣਾ ਮੂਲ ਬਰਾਊਜ਼ਰ ਬਣਾਓ।"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "ਹਰ ਰੋਜ਼ ਦੇ ਬਰਾਊਜ਼ ਕਰਨ ਦਾ ਪੱਧਰ ਉੱਚਾ ਕਰੋ"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ਹਰ ਮਹੀਨੇ ਔਸਤ 3,000+ ਟਰੈਕਰਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਂਦਾ ਹੈ। ਪਰਦੇਦਾਰੀ ਨੂੰ ਧਿਆਨ ਵਿੱਚ ਰੱਖਣ ਲਈ ਸਾਨੂੰ ਆਪਣਾ ਮੂਲ ਬਰਾਊਜ਼ਰ ਬਣਾਓ।"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ਹਰ ਮਹੀਨੇ ਔਸਤ 3,000+ ਟਰੈਕਰਾਂ ਉੱਤੇ ਪਾਬੰਦੀ ਲਾਉਂਦਾ ਹੈ। ਪਰਦੇਦਾਰੀ ਨੂੰ ਧਿਆਨ ਵਿੱਚ ਰੱਖਣ ਲਈ ਸਾਨੂੰ ਆਪਣਾ ਮੂਲ ਬਰਾਊਜ਼ਰ ਬਣਾਓ।"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ਵਿੱਚ ਪਰਦੇਦਾਰੀ ਹੈ"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ਵਿੱਚ ਪਰਦੇਦਾਰੀ ਹੈ"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "ਆਪਣੇ ਮੂਲ ਬਰਾਊਜ਼ਰ ਨੂੰ ਬਦਲੋ"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Default Browser App ਨੂੰ ਛੂਹੋ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox ਚੁਣੋ"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia ਚੁਣੋ"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "ਮੂਲ ਬਰਾਊਜ਼ਰ ਐਪ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/Intro.strings b/firefox-ios/Shared/pa-IN.lproj/Intro.strings index 1ab588d639722..fb032251ea102 100644 --- a/firefox-ios/Shared/pa-IN.lproj/Intro.strings +++ b/firefox-ios/Shared/pa-IN.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ਸਾਈਨ ਇਨ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox ਵਲੋਂ ਜੀ ਆਇਆਂ ਨੂੰ"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia ਵਲੋਂ ਜੀ ਆਇਆਂ ਨੂੰ"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ਬਰਾਊਜ਼ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰੋ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/Localizable.strings b/firefox-ios/Shared/pa-IN.lproj/Localizable.strings index fb3a7a7a88c75..1d081c75584e8 100644 --- a/firefox-ios/Shared/pa-IN.lproj/Localizable.strings +++ b/firefox-ios/Shared/pa-IN.lproj/Localizable.strings @@ -98,10 +98,10 @@ "Bookmarks.Edit.Button" = "ਸੋਧੋ"; /* The button to edit a bookmark */ -"Bookmarks.EditBookmark.Label" = "ਬੁੱਕਮਾਰਕ ਸੋਧੋ"; +"Bookmarks.EditBookmark.Label" = "ਬੁੱਕਮਾਰਕ ਨੂੰ ਸੋਧੋ"; /* The button to edit a folder */ -"Bookmarks.EditFolder.Label" = "ਫੋਲਡਰ ਸੋਧੋ"; +"Bookmarks.EditFolder.Label" = "ਫੋਲਡਰ ਨੂੰ ਸੋਧੋ"; /* The label to show the location of the folder where the bookmark is located */ "Bookmarks.Folder.Label" = "ਫ਼ੋਲਡਰ"; @@ -158,46 +158,46 @@ "Close" = "ਬੰਦ ਕਰੋ"; /* Accessibility label (used by assistive technology) notifying the user that the tab is being closed. */ -"Closing tab" = "ਟੈਬ ਬੰਦ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"; +"Closing tab" = "ਟੈਬ ਨੂੰ ਬੰਦ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"; /* Context menu item for bookmarking a link URL */ -"ContextMenu.BookmarkLinkButtonTitle" = "ਲਿੰਕ ਬੁੱਕਮਾਰਕ ਕਰੋ"; +"ContextMenu.BookmarkLinkButtonTitle" = "ਲਿੰਕ ਨੂੰ ਬੁੱਕਮਾਰਕ ਕਰੋ"; /* The label text in the Button Toast for switching to a fresh New Private Tab. */ -"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText" = "ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ ਖੋਲ੍ਹੀ ਗਈ"; +"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText" = "ਨਵੀਂ ਪ੍ਰਾਈਵੈਟ ਟੈਬ ਖੋਲ੍ਹੀ"; /* The label text in the Button Toast for switching to a fresh New Private Tab. */ -"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText.v113" = "ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ ਖੋਲ੍ਹੀ ਗਈ"; +"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText.v113" = "ਨਵੀਂ ਪ੍ਰਾਈਵੈਟ ਟੈਬ ਖੋਲ੍ਹੀ"; /* The button text in the Button Toast for switching to a fresh New Tab. */ "ContextMenu.ButtonToast.NewTabOpened.ButtonText" = "ਜਾਓ"; /* The label text in the Button Toast for switching to a fresh New Tab. */ -"ContextMenu.ButtonToast.NewTabOpened.LabelText" = "ਨਵੀਂ ਟੈਬ ਖੋਲ੍ਹੀ ਗਈ"; +"ContextMenu.ButtonToast.NewTabOpened.LabelText" = "ਨਵੀਂ ਖੋਲ੍ਹੀ ਗਈ ਟੈਬ"; /* The label text in the Button Toast for switching to a fresh New Tab. */ -"ContextMenu.ButtonToast.NewTabOpened.LabelText.v114" = "ਨਵੀਂ ਟੈਬ ਖੋਲ੍ਹੀ ਗਈ"; +"ContextMenu.ButtonToast.NewTabOpened.LabelText.v114" = "ਨਵੀਂ ਖੋਲ੍ਹੀ ਗਈ ਟੈਬ"; /* Context menu item for copying an image to the clipboard */ -"ContextMenu.CopyImageButtonTitle" = "ਚਿੱਤਰ ਕਾਪੀ ਕਰੋ"; +"ContextMenu.CopyImageButtonTitle" = "ਚਿੱਤਰ ਨੂੰ ਕਾਪੀ ਕਰੋ"; /* Context menu item for copying an image URL to the clipboard */ -"ContextMenu.CopyImageLinkButtonTitle" = "ਚਿੱਤਰ ਲਿੰਕ ਕਾਪੀ ਕਰੋ"; +"ContextMenu.CopyImageLinkButtonTitle" = "ਚਿੱਤਰ ਦੇ ਲਿੰਕ ਨੂੰ ਕਾਪੀ ਕਰੋ"; /* Context menu item for copying a link URL to the clipboard */ -"ContextMenu.CopyLinkButtonTitle" = "ਲਿੰਕ ਕਾਪੀ ਕਰੋ"; +"ContextMenu.CopyLinkButtonTitle" = "ਲਿੰਕ ਨੂੰ ਕਾਪੀ ਕਰੋ"; /* Context menu item for downloading a link URL */ -"ContextMenu.DownloadLinkButtonTitle" = "ਲਿੰਕ ਡਾਊਨਲੋਡ ਕਰੋ"; +"ContextMenu.DownloadLinkButtonTitle" = "ਲਿੰਕ ਨੂੰ ਡਾਊਨਲੋਡ ਕਰੋ"; /* Context menu item for opening a link in a new tab */ -"ContextMenu.OpenInNewTabButtonTitle" = "ਨਵੇਂ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ"; +"ContextMenu.OpenInNewTabButtonTitle" = "ਨਵੀਂ ਟੈਬ 'ਚ ਖੋਲ੍ਹੋ"; /* Context menu item for saving an image */ -"ContextMenu.SaveImageButtonTitle" = "ਚਿੱਤਰ ਸੰਭਾਲੋ"; +"ContextMenu.SaveImageButtonTitle" = "ਚਿੱਤਰ ਨੂੰ ਸੰਭਾਲੋ"; /* Context menu item for sharing a link URL */ -"ContextMenu.ShareLinkButtonTitle" = "ਲਿੰਕ ਸਾਂਝਾ ਕਰੋ"; +"ContextMenu.ShareLinkButtonTitle" = "ਲਿੰਕ ਨੂੰ ਸਾਂਝਾ ਕਰੋ"; /* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing search bar placement. It indicates a user can navigate to the settings page that allows them to customize the placement of the search bar. */ "ContextualHints.SearchBarPlacement.CallToAction" = "ਟੂਲਬਾਰ ਸੈਟਿੰਗਾਂ"; @@ -269,10 +269,10 @@ "Downloads.CancelDialog.Resume" = "ਮੁੜ ਜਾਰੀ ਰੱਖੋ"; /* Alert dialog title when the user taps the cancel download icon. */ -"Downloads.CancelDialog.Title" = "ਡਾਊਨਲੋਡ ਰੱਦ ਕਰੋ"; +"Downloads.CancelDialog.Title" = "ਡਾਊਨਲੋਡ ਨੂੰ ਰੱਦ ਕਰੋ"; /* The label text in the Download Cancelled toast for showing confirmation that the download was cancelled. */ -"Downloads.Toast.Cancelled.LabelText" = "ਡਾਊਨਲੋਡ ਰੱਦ ਕਰੋ"; +"Downloads.Toast.Cancelled.LabelText" = "ਡਾਊਨਲੋਡ ਨੂੰ ਰੱਦ ਕੀਤਾ ਗਿਆ"; /* The label text in the Download Failed toast for showing confirmation that the download has failed. */ "Downloads.Toast.Failed.LabelText" = "ਡਾਊਨਲੋਡ ਅਸਫ਼ਲ ਹੈ"; @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "ਇਹ ਗਲਤ ਸੰਰਚਨਾ ਹੋ ਸਕਦੀ ਹੈ ਜਾਂ ਹਮਲਾਵਰ ਵਲੋਂ ਨੁਕਸਾਨਿਆ ਗਿਆ। ਤਾਂ ਹੀ ਜਾਰੀ ਰੱਖੋ ਜੇ ਤੁਸੀਂ ਸੰਭਾਵਿਤ ਖ਼ਤਰੇ ਨੂੰ ਮੰਨਦੇ ਹੋ।"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ ਦੇ ਮਾਲਕ ਨੇ ਆਪਣੀ ਵੈੱਬਸਾਈਟ ਨੂੰ ਗਲਤ ਢੰਗ ਨਾਲ ਸੰਰਚਿਤ ਕੀਤਾ ਹੈ। ਤੁਹਾਡੀ ਜਾਣਕਾਰੀ ਨੂੰ ਚੋਰੀ ਹੋਣ ਤੋਂ ਬਚਾਉਣ ਲਈ Firefox ਇਸ ਵੈੱਬਸਾਈਟ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਹੈ।"; +"ErrorPages.CertWarning.Description" = "%@ ਦੇ ਮਾਲਕ ਨੇ ਆਪਣੀ ਵੈੱਬਸਾਈਟ ਨੂੰ ਗਲਤ ਢੰਗ ਨਾਲ ਸੰਰਚਿਤ ਕੀਤਾ ਹੈ। ਤੁਹਾਡੀ ਜਾਣਕਾਰੀ ਨੂੰ ਚੋਰੀ ਹੋਣ ਤੋਂ ਬਚਾਉਣ ਲਈ Ecosia ਇਸ ਵੈੱਬਸਾਈਟ ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਹੈ।"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "ਇਹ ਸੰਪਰਕ ਗ਼ੈਰ-ਭਰੋਸੇਯੋਗ ਹੈ"; @@ -326,13 +326,13 @@ "ExternalLink.AppStore.ConfirmationTitle" = "ਐਪ ਸਟੋਰ ਐਪ 'ਚ ਇਹ ਲਿੰਕ ਖੋਲ੍ਹਣਾ ਹੈ?"; /* Question shown to user when tapping an SMS or MailTo link that opens the external app for those. */ -"ExternalLink.AppStore.GenericConfirmationTitle" = "ਇਹ ਲਿੰਕ ਬਾਹਰੀ ਐਪ ਵਿੱਚ ਖੋਲ੍ਹਣਾ ਹੈ?"; +"ExternalLink.AppStore.GenericConfirmationTitle" = "ਇਸ ਲਿੰਕ ਨੂੰ ਬਾਹਰੀ ਐਪ ਨਾਲ ਖੋਲ੍ਹਣਾ ਹੈ?"; /* Tile title for Facebook */ "Facebook" = "ਫੇਸਬੁੱਕ"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox ਮੁੱਖ ਸਫ਼ਾ"; +"Firefox.HomePage.Title" = "Ecosia ਮੁੱਖ ਸਫ਼ਾ"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "ਮੁੱਖ-ਸਫ਼ੇ ਨੂੰ ਕਸਟਮਾਈਜ਼ ਕਰੋ"; @@ -377,10 +377,10 @@ "Forward" = "ਅੱਗੇ"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox ਖਾਤਾ"; +"FxA.FirefoxAccount" = "Ecosia ਖਾਤਾ"; /* Button label to go to Firefox Account settings */ -"FxA.ManageAccount" = "ਖਾਤੇ ਤੇ ਡਿਵਾਈਸਾਂ ਦਾ ਇੰਤਜ਼ਾਮ"; +"FxA.ManageAccount" = "ਖਾਤੇ ਅਤੇ ਡਿਵਾਈਸਾਂ ਦਾ ਇੰਤਜ਼ਾਮ"; /* Label when no internet is present */ "FxA.NoInternetConnection" = "ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਨਹੀਂ ਹੈ"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "ਆਪਣੇ ਕੈਮਰੇ ਨਾਲ ਸਾਈਨ ਇਨ ਕਰੋ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "ਆਪਣੇ ਕੰਪਿਊਟਰ ਉੱਤੇ Firefox ਖੋਲ੍ਹੋ ਅਤੇ firefox.com/pair ਉੱਤੇ ਜਾਓ"; +"fxa.signin.qr-link-instruction" = "ਆਪਣੇ ਕੰਪਿਊਟਰ ਉੱਤੇ Ecosia ਖੋਲ੍ਹੋ ਅਤੇ ecosia.org/pair ਉੱਤੇ ਜਾਓ"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "ਸਕੈਨ ਕਰਨ ਲਈ ਤਿਆਰ"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "ਹੁਣੇ ਸਿੰਕ ਕਰੋ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox ਸਿੰਕ %@ ਨਾਲ ਕਨੈਕਟ ਹੋ ਚੁੱਕਾ ਹੈ"; +"FxAPush_DeviceConnected_body" = "Ecosia ਸਿੰਕ %@ ਨਾਲ ਕਨੈਕਟ ਹੋ ਚੁੱਕਾ ਹੈ"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "ਸਿੰਕ ਕਨੈਕਟ ਹੈ"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ਕਾਮਯਾਬੀ ਨਾਲ ਡਿਸ-ਕਨੈਕਟ ਹੋ ਚੁੱਕਾ ਹੈ।"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ਇਹ ਡਿਵਾਈਸ ਕਾਮਯਾਬੀ ਨਾਲ Firefox ਸਿੰਕ ਤੋਂ ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਜਾ ਚੁੱਕਾ ਹੈ।"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ਇਹ ਡਿਵਾਈਸ ਕਾਮਯਾਬੀ ਨਾਲ Ecosia ਸਿੰਕ ਤੋਂ ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਜਾ ਚੁੱਕਾ ਹੈ।"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ਸਿੰਕ ਡਿਸ-ਕਨੈਕਟ ਹੈ"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "ਸਿੰਕ ਡਿਸ-ਕਨੈਕਟ ਹੈ"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Firefox ਸਿੰਕ ਤੋਂ ਡਿਵਾਈਸ ਡਿਸ-ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ecosia ਸਿੰਕ ਤੋਂ ਡਿਵਾਈਸ ਡਿਸ-ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox ਲੋਗੋ, ਵਾਲਪੇਪਰ ਬਦਲੋ"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia ਲੋਗੋ, ਵਾਲਪੇਪਰ ਬਦਲੋ"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ਮਦਦ"; @@ -431,7 +431,7 @@ "History" = "ਅਤੀਤ"; /* Title for button in the history panel to clear recent history */ -"HistoryPanel.ClearHistoryButtonTitle" = "…ਸੱਜਰਾ ਅਤੀਤ ਮਿਟਾਓ"; +"HistoryPanel.ClearHistoryButtonTitle" = "…ਸੱਜਰੇ ਅਤੀਤ ਨੂੰ ਮਿਟਾਓ"; /* Option title to clear all browsing history. */ "HistoryPanel.ClearHistoryMenuOptionEverything" = "ਹਰ ਚੀਜ਼"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "ਆਪਣੇ ਹੋਰ ਡਿਵਾਈਸਾਂ ਤੋਂ ਟੈਬਾਂ ਦੀ ਸੂਚੀ ਖੋਲ੍ਹਣ ਲਈ ਸਾਈਨ ਇਨ ਕਰੋ।"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox ਸਿੰਕ"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia ਸਿੰਕ"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ਅਤੀਤ"; @@ -473,7 +473,7 @@ "HomePanel.ContextMenu.DeleteFromHistory" = "ਅਤੀਤ 'ਚੋਂ ਹਟਾਓ"; /* The title for the Open in New Private Tab context menu action for sites in Home Panels */ -"HomePanel.ContextMenu.OpenInNewPrivateTab.v101" = "ਨਿੱਜੀ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ"; +"HomePanel.ContextMenu.OpenInNewPrivateTab.v101" = "ਪ੍ਰਾਈਵੇਟ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ"; /* The title for the Open in New Tab context menu action for sites in Home Panels */ "HomePanel.ContextMenu.OpenInNewTab" = "ਨਵੀਂ ਟੈਬ 'ਚ ਖੋਲ੍ਹੋ"; @@ -491,7 +491,7 @@ "Hotkeys.Back.DiscoveryTitle" = "ਪਿੱਛੇ"; /* A label indicating the keyboard shortcut of closing the current tab a user is in. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Hotkeys.CloseTab.DiscoveryTitle" = "ਟੈਬ ਬੰਦ ਕਰੋ"; +"Hotkeys.CloseTab.DiscoveryTitle" = "ਟੈਬ ਨੂੰ ਬੰਦ ਕਰੋ"; /* A label indicating the keyboard shortcut of finding text a user desires within a page. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Hotkeys.Find.DiscoveryTitle" = "ਲੱਭੋ"; @@ -500,7 +500,7 @@ "Hotkeys.Forward.DiscoveryTitle" = "ਅੱਗੇ"; /* A label indicating the keyboard shortcut of creating a new private tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Hotkeys.NewPrivateTab.DiscoveryTitle" = "ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ"; +"Hotkeys.NewPrivateTab.DiscoveryTitle" = "ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਟੈਬ"; /* A label indicating the keyboard shortcut of creating a new tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Hotkeys.NewTab.DiscoveryTitle" = "ਨਵੀਂ ਟੈਬ"; @@ -509,7 +509,7 @@ "Hotkeys.NormalMode.DiscoveryTitle" = "ਆਮ ਬਰਾਊਜ਼ ਕਰਨ ਢੰਗ"; /* A label indicating the keyboard shortcut of switching from Normal Browsing mode to Private Browsing Mode. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Hotkeys.PrivateMode.DiscoveryTitle" = "ਨਿੱਜੀ ਬਰਾਊਜ਼ਿੰਗ ਢੰਗ"; +"Hotkeys.PrivateMode.DiscoveryTitle" = "ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ਿੰਗ ਢੰਗ"; /* A label indicating the keyboard shortcut of reloading the current page. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Hotkeys.Reload.DiscoveryTitle" = "ਸਫ਼ੇ ਨੂੰ ਮੁੜ ਲੋਡ ਕਰੋ"; @@ -521,7 +521,7 @@ "Hotkeys.ShowNextTab.DiscoveryTitle" = "ਅਗਲੀ ਟੈਬ ਨੂੰ ਵੇਖੋ"; /* A label indicating the keyboard shortcut of switching to a tab immediately preceding to the currently selected tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Hotkeys.ShowPreviousTab.DiscoveryTitle" = "ਪਿਛਲਾ ਟੈਬ ਨੂੰ ਵੇਖੋ"; +"Hotkeys.ShowPreviousTab.DiscoveryTitle" = "ਪਿਛਲੀ ਟੈਬ ਨੂੰ ਵੇਖੋ"; /* In the Tabs Tray, in the Inactive Tabs section, this is the button the user must tap in order to close all inactive tabs. */ "InactiveTabs.TabTray.CloseButtonTitle" = "ਸਾਰੀਆਂ ਨਾ-ਸਰਗਰਮ ਟੈਬਾਂ ਬੰਦ ਕਰੋ"; @@ -536,7 +536,7 @@ "Keyboard.Shortcuts.ActualSize" = "ਅਸਲ ਆਕਾਰ"; /* A label indicating the keyboard shortcut of adding the currently viewing web page as a bookmark. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Keyboard.Shortcuts.AddBookmark" = "ਬੁੱਕਮਾਰਕ ਜੋੜੋ"; +"Keyboard.Shortcuts.AddBookmark" = "ਬੁੱਕਮਾਰਕ ਨੂੰ ਜੋੜੋ"; /* A label indicating the keyboard shortcut of clearing recent history. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Keyboard.Shortcuts.ClearRecentHistory" = "ਸੱਜਰੇ ਅਤੀਤ ਨੂੰ ਸਾਫ਼ ਕਰੋ"; @@ -563,19 +563,19 @@ "Keyboard.Shortcuts.Settings" = "ਸੈਟਿੰਗਾਂ"; /* A label indicating the keyboard shortcut of showing all bookmarks. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Keyboard.Shortcuts.ShowBookmarks" = "ਬੁੱਕਮਾਰਕ ਵੇਖਾਓ"; +"Keyboard.Shortcuts.ShowBookmarks" = "ਬੁੱਕਮਾਰਕਾਂ ਨੂੰ ਵੇਖਾਓ"; /* A label indcating the keyboard shortcut of showing all downloads. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Keyboard.Shortcuts.ShowDownloads" = "ਡਾਊਨਲੋਡ ਵੇਖਾਓ"; +"Keyboard.Shortcuts.ShowDownloads" = "ਡਾਊਨਲੋਡ ਨੂੰ ਵੇਖਾਓ"; /* A label indicating the keyboard shortcut to switch from the current tab to the first tab. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Keyboard.Shortcuts.ShowFirstTab" = "ਪਹਿਲੀ ਟੈਬ ਨੂੰ ਵੇਖੋ"; +"Keyboard.Shortcuts.ShowFirstTab" = "ਪਹਿਲੀ ਟੈਬ ਨੂੰ ਵੇਖਾਓ"; /* A label indicating the keyboard shortcut of showing all history. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Keyboard.Shortcuts.ShowHistory" = "ਅਤੀਤ ਵੇਖੋ"; +"Keyboard.Shortcuts.ShowHistory" = "ਅਤੀਤ ਨੂੰ ਵੇਖਾਓ"; /* A label indicating the keyboard shortcut switch from your current tab to the last tab. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Keyboard.Shortcuts.ShowLastTab" = "ਪਿਛਲੀ ਟੈਬ ਵੇਖੋ"; +"Keyboard.Shortcuts.ShowLastTab" = "ਪਿਛਲੀ ਟੈਬ ਵੇਖਾਓ"; /* A label indicating the keyboard shortcut of enlarging the view of the current web page. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Keyboard.Shortcuts.ZoomIn" = "ਜ਼ੂਮ ਇਨ"; @@ -614,7 +614,7 @@ "LibraryPanel.History.NoHistoryFound.v99" = "ਕੋਈ ਅਤੀਤ ਨਹੀਂ ਲੱਭਿਆ"; /* In the history panel, this is the title on the button that navigates the user to a screen showing their recently closed tabs. */ -"LibraryPanel.History.RecentlyClosedTabs.v99" = "ਹਾਲੀਆ ਬੰਦ ਕੀਤੀ ਟੈਬ ਨਹੀਂ ਹੈ"; +"LibraryPanel.History.RecentlyClosedTabs.v99" = "ਸੱਜਰੀਆਂ ਬੰਦ ਕੀਤੀਆਂ ਟੈਬਾਂ"; /* In the history panel, users will be able to search terms in their browsing history. This placeholder text inside the search component will indicate that a user can search through their browsing history. */ "LibraryPanel.History.SearchHistoryPlaceholder.v99" = "ਖੋਜ ਸ਼ਬਦ ਦਿਓ"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "ਹੋਰ ਜਾਣੋ"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox ਲਈ ਆਪੇ-ਭਰਨ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਨੂੰ ਡਿਵਾਈਸ ਪਾਸਕੋਡ ਸਮਰੱਥ ਕਰਨਾ ਪਵੇਗਾ।"; +"Logins.PasscodeRequirement.Warning" = "Ecosia ਲਈ ਆਪੇ-ਭਰਨ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਨੂੰ ਡਿਵਾਈਸ ਪਾਸਕੋਡ ਸਮਰੱਥ ਕਰਨਾ ਪਵੇਗਾ।"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "ਆਪਣੇ ਪਾਸਵਰਡ ਹਰ ਥਾਂ ਆਪਣੇ ਨਾਲ ਰੱਖੋ"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox ਪਾਸਵਰਡ ਆਪੇ-ਭਰੋ"; +"Logins.WelcomeView.Title2" = "Ecosia ਪਾਸਵਰਡ ਆਪੇ-ਭਰੋ"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "ਆਪੇ-ਭਰੋ ਨੂੰ ਚਾਲੂ ਕਰੋ"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ਸੰਭਾਲੇ ਗਏ ਲਾਗਇਨ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ਜਾਪਦਾ ਹੈ ਕਿ ਪਿਛਲੀ ਵਾਰ Firefox ਕਰੈਸ਼ ਹੋ ਗਿਆ ਸੀ। ਕੀ ਤੁਸੀਂ ਆਪਣੀਆਂ ਟੈਬਾਂ ਨੂੰ ਮੁੜ-ਸਟੋਰ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ਜਾਪਦਾ ਹੈ ਕਿ ਪਿਛਲੀ ਵਾਰ Ecosia ਕਰੈਸ਼ ਹੋ ਗਿਆ ਸੀ। ਕੀ ਤੁਸੀਂ ਆਪਣੀਆਂ ਟੈਬਾਂ ਨੂੰ ਮੁੜ-ਸਟੋਰ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "ਪੜ੍ਹੇ ਵਜੋਂ ਨਿਸ਼ਾਨੀ ਲਾਓ"; @@ -713,7 +713,7 @@ "Mark as Unread" = "ਨਾ-ਪੜ੍ਹੇ ਵਜੋਂ ਨਿਸ਼ਾਨੀ ਲਾਓ"; /* Toast displayed to the user after a bookmark has been added. */ -"Menu.AddBookmark.Confirm" = "ਬੁੱਕਮਾਰਕ ਜੋੜਿਆ"; +"Menu.AddBookmark.Confirm" = "ਬੁੱਕਮਾਰਕ ਨੂੰ ਜੋੜਿਆ ਗਿਆ"; /* Label for the add bookmark button in the menu. Pressing this button bookmarks the current page. Please keep the text as short as possible for this label. */ "Menu.AddBookmark.Label.v99" = "ਜੋੜੋ"; @@ -734,7 +734,7 @@ "Menu.Bookmarks.Label" = "ਬੁੱਕਮਾਰਕ"; /* The title for the button that lets you copy the url from the location bar. */ -"Menu.Copy.Title" = "ਸਿਰਨਾਵਾਂ ਕਾਪੀ ਕਰੋ"; +"Menu.Copy.Title" = "ਸਿਰਨਾਵੇਂ ਨੂੰ ਕਾਪੀ ਕਰੋ"; /* Toast displayed to user after copy url pressed. */ "Menu.CopyURL.Confirm" = "URL ਕਲਿੱਪਬੋਰਡ 'ਚ ਕਾਪੀ ਕੀਤਾ"; @@ -788,7 +788,7 @@ "Menu.ReloadWithTrackingProtection.Title" = "ਟਰੈਕਿੰਗ ਸੁਰੱਖਿਆ ਨਾਲ ਮੁੜ-ਲੋਡ ਕਰੋ"; /* Toast displayed to the user after a bookmark has been removed. */ -"Menu.RemoveBookmark.Confirm" = "ਬੁੱਕਮਾਰਕ ਹਟਾਇਆ"; +"Menu.RemoveBookmark.Confirm" = "ਬੁੱਕਮਾਰਕ ਨੂੰ ਹਟਾਇਆ"; /* Label for the remove bookmark button in the menu. Pressing this button remove the current page from the bookmarks. Please keep the text as short as possible for this label. */ "Menu.RemoveBookmark.Label.v99" = "ਹਟਾਓ"; @@ -809,7 +809,7 @@ "Menu.Share.v99" = "ਸਾਂਝਾ ਕਰੋ"; /* Label for the Firefox Sync button in the menu page. Pressing this button open the sign in to Firefox page service to sync and save data. */ -"Menu.SyncAndSaveData.v103" = "ਡਾਟਾ ਸਿੰਕ ਕਰੋ ਤੇ ਸੰਭਾਲੋ"; +"Menu.SyncAndSaveData.v103" = "ਡਾਟਾ ਸਿੰਕ ਕਰੋ ਅਤੇ ਸੰਭਾਲੋ"; /* Toast displayed to the user after a tab has been sent successfully. */ "Menu.TabSent.Confirm" = "ਟੈਬ ਭੇਜੋ"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "ਬਰਾਊਜ਼ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰੋ"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ਓਹ ਹੋ! Firefox ਕਰੈਸ਼ ਹੋ ਗਿਆ"; +"Oops! Firefox crashed" = "ਓਹ ਹੋ! Ecosia ਕਰੈਸ਼ ਹੋ ਗਿਆ"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "ਲੇਖਾਂ ਨੂੰ ਪੜ੍ਹਨ ਝਲਕ ਵਿੱਚ ਕਿਤਾਬ ਦੇ ਆਈਕਾਨ ਉੱਤੇ ਛੂਹ ਕੇ ਖੋਲ੍ਹੋ, ਜਦੋਂ ਕਿ ਉਹ ਟਾਈਟਲ ਪੱਟੀ ਵਿੱਚ ਦਿਖਾਈ ਦਿੰਦੇ ਹਨ।"; @@ -905,7 +905,7 @@ "Open Tabs" = "ਟੈਬਾਂ ਖੋਲ੍ਹੋ"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ਗਲਤ ਸਿਰਨਾਵਾਂ ਹੋਣ ਕਰਕੇ ਸਫ਼ੇ ਨੂੰ ਖੋਲ੍ਹ ਨਹੀਂ ਸਕਦਾ ਹੈ।"; +"OpenURL.Error.Message" = "Ecosia ਗਲਤ ਸਿਰਨਾਵਾਂ ਹੋਣ ਕਰਕੇ ਸਫ਼ੇ ਨੂੰ ਖੋਲ੍ਹ ਨਹੀਂ ਸਕਦਾ ਹੈ।"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "ਸਫ਼ੇ ਨੂੰ ਖੋਲ੍ਹਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "ਇਹ ਤੁਹਾਨੂੰ ਚਿੱਤਰਾਂ ਨੂੰ ਤੁਹਾਡੇ ਕੈਮਰਾ ਰੋਲ ਵਿੱਚ ਸੰਭਾਲਣ ਲਈ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ।"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ਨੂੰ ਆਪਣੀਆਂ ਫੋਟੋਆਂ (Photos) ਲਈ ਪਹੁੰਚ ਚਾਹੁੰਦਾ ਹੈ"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ਨੂੰ ਆਪਣੀਆਂ ਫੋਟੋਆਂ (Photos) ਲਈ ਪਹੁੰਚ ਚਾਹੁੰਦਾ ਹੈ"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "ਬੰਦ ਕਰੋ"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "ਡਾਟਾ ਅਢੁੱਕਵਾਂ ਹੈ"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "ਡਿਵਾਈਸ ‘Settings’ > ‘Firefox’ ਉੱਤੇ ਜਾਓ। Firefox ਨੂੰ ਕੈਮਰਾ ਵਰਤਣ ਦੀ ਇਜ਼ਾਜ਼ਤ ਦਿਓ।"; +"ScanQRCode.PermissionError.Message.v100" = "ਡਿਵਾਈਸ ‘Settings’ > ‘Ecosia’ ਉੱਤੇ ਜਾਓ। Ecosia ਨੂੰ ਕੈਮਰਾ ਵਰਤਣ ਦੀ ਇਜ਼ਾਜ਼ਤ ਦਿਓ।"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR ਕੋਡ ਨੂੰ ਸਕੈਨ ਕਰੋ"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "ਟੈਬ ਉੱਤੇ ਜਾਓ"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox ਸੁਝਾਅ"; +"Search.SuggestSectionTitle.v102" = "Ecosia ਸੁਝਾਅ"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "ਨਵਾਂ ਖੋਜ ਇੰਜਣ ਤੁਰੰਤ ਖੋਜ ਪੱਟੀ ਵਿੱਚ ਦਿਖਾਈ ਦੇਵੇਗਾ।"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "ਟੈਬ ਭੇਜੋ"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "ਤੁਹਾਡੇ ਕੋਲ ਸਿੰਕ ਕਰਨ ਲਈ ਇਸ Firefox ਖਾਤੇ ਨਾਲ ਹੋਰ ਕੋਈ ਡਿਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਹੈ।"; +"SendTo.NoDevicesFound.Message" = "ਤੁਹਾਡੇ ਕੋਲ ਸਿੰਕ ਕਰਨ ਲਈ ਇਸ Ecosia ਖਾਤੇ ਨਾਲ ਹੋਰ ਕੋਈ ਡਿਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਹੈ।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "ਜਾਰੀ ਰੱਖਣ ਲਈ Firefox ਖੋਲ੍ਹੋ, ਸੈਟਿੰਗਾਂ 'ਤੇ ਜਾਓ ਅਤੇ ਸਾਈਨ ਇਨ ਕਰੋ।"; +"SendTo.NotSignedIn.Message" = "ਜਾਰੀ ਰੱਖਣ ਲਈ Ecosia ਖੋਲ੍ਹੋ, ਸੈਟਿੰਗਾਂ 'ਤੇ ਜਾਓ ਅਤੇ ਸਾਈਨ ਇਨ ਕਰੋ।"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "ਤੁਸੀਂ ਆਪਣੇ Firefox ਖਾਤੇ ਵਿੱਚ ਸਾਈਨ ਇਨ ਨਹੀਂ ਕੀਤਾ ਹੈ।"; +"SendTo.NotSignedIn.Title" = "ਤੁਸੀਂ ਆਪਣੇ Ecosia ਖਾਤੇ ਵਿੱਚ ਸਾਈਨ ਇਨ ਨਹੀਂ ਕੀਤਾ ਹੈ।"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ਭੇਜੋ"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox ਸਿੰਕ"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia ਸਿੰਕ"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ਵੇਖੋ"; @@ -1154,10 +1154,10 @@ "Settings.ClearAllWebsiteData.Clear.Button" = "ਸਾਰੇ ਵੈੱਬਸਾਈਟ ਡਾਟੇ ਨੂੰ ਸਾਫ਼ ਕਰੋ"; /* Button in settings that clears private data for the selected items. */ -"Settings.ClearPrivateData.Clear.Button" = "ਨਿੱਜੀ ਡਾਟੇ ਨੂੰ ਮਿਟਾਓ"; +"Settings.ClearPrivateData.Clear.Button" = "ਪ੍ਰਾਈਵੇਟ ਡਾਟੇ ਨੂੰ ਮਿਟਾਓ"; /* Label used as an item in Settings. When touched it will open a dialog prompting the user to make sure they want to clear all of their private data. */ -"Settings.ClearPrivateData.SectionName" = "ਨਿੱਜੀ ਡਾਟੇ ਨੂੰ ਮਿਟਾਓ"; +"Settings.ClearPrivateData.SectionName" = "ਪ੍ਰਾਈਵੇਟ ਡਾਟੇ ਨੂੰ ਮਿਟਾਓ"; /* Button in Data Management that clears private data for the selected items. Parameter is the number of items to be cleared */ "Settings.ClearSelectedWebsiteData.ClearSelected.Button" = "ਚੀਜ਼ਾਂ ਮਿਟਾਓ: %1$@"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "ਡਾਟਾ ਬੰਦੋਬਸਤ"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ਤੁੁਹਾਡੇ ਖਾਤੇ ਨੂੰ ਸਿੰਕ ਕਰਨ ਤੋਂ ਰੋਕੇਗਾ, ਪਰ ਇਸ ਡਿਵਾਈਸ ਲਈ ਤੁਹਾਡੇ ਬਰਾਊਜ਼ ਕਰਨ ਵਾਲੇ ਕਿਸੇ ਵੀ ਡਾਟੇ ਨੂੰ ਹਟਾਏਗਾ ਨਹੀਂ।"; +"Settings.Disconnect.Body" = "Ecosia ਤੁੁਹਾਡੇ ਖਾਤੇ ਨੂੰ ਸਿੰਕ ਕਰਨ ਤੋਂ ਰੋਕੇਗਾ, ਪਰ ਇਸ ਡਿਵਾਈਸ ਲਈ ਤੁਹਾਡੇ ਬਰਾਊਜ਼ ਕਰਨ ਵਾਲੇ ਕਿਸੇ ਵੀ ਡਾਟੇ ਨੂੰ ਹਟਾਏਗਾ ਨਹੀਂ।"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ਸਿੰਕ ਡਿਸ-ਕਨੈਕਟ ਕਰੋ"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "ਸਿੰਕ ਸੈਟਿੰਗਾਂ"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox ਖਾਤਾ"; +"Settings.FxA.Title" = "Ecosia ਖਾਤਾ"; /* General settings section title */ "Settings.General.SectionName" = "ਆਮ"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "ਚੁਣੋ ਕਿ ਮੁੱਖ ਸਫ਼ੇ ਉੱਤੇ ਕੀ ਦਿਖਾਉਣਾ ਹੈ।"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox ਮੁੱਖ ਸਫ਼ੇ ਉੱਤੇ ਦਿਖਾਉਣ ਵਾਲੀ ਸਮੱਗਰੀ ਚੁਣੋ।"; +"Settings.Home.Option.Description.v101" = "Ecosia ਮੁੱਖ ਸਫ਼ੇ ਉੱਤੇ ਦਿਖਾਉਣ ਵਾਲੀ ਸਮੱਗਰੀ ਚੁਣੋ।"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "ਪਿੱਛੇ ਜਾਓ"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "ਮੁੱਖ-ਸਫ਼ਾ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "ਜਦੋਂ ਤੁਸੀਂ Firefox ਉੱਤੇ ਪਰਤੋ ਤਾਂ ਤੁਸੀਂ ਜੋ ਵੇਖਣਾ ਚਾਹੋਗੇ, ਉਹ ਚੁਣੋ।"; +"Settings.Home.Option.StartAtHome.Description" = "ਜਦੋਂ ਤੁਸੀਂ Ecosia ਉੱਤੇ ਪਰਤੋ ਤਾਂ ਤੁਸੀਂ ਜੋ ਵੇਖਣਾ ਚਾਹੋਗੇ, ਉਹ ਚੁਣੋ।"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "ਪਿਛਲੀ ਟੈਬ"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "ਵਾਲਪੇਪਰ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox ਵਾਲਪੇਪਰ, ਜਾਮਨੀ ਪੈਟਰਨ"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia ਵਾਲਪੇਪਰ, ਜਾਮਨੀ ਪੈਟਰਨ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox ਵਾਲਪੇਪਰ, ਕੰਢੇ ਉੱਤੇ ਪਹਾੜ ਵਾਲੀ ਤਰਤੀਬ।"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia ਵਾਲਪੇਪਰ, ਕੰਢੇ ਉੱਤੇ ਪਹਾੜ ਵਾਲੀ ਤਰਤੀਬ।"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox ਵਾਲਪੇਪਰ, ਅਸਮਾਨੀ ਨੀਲਾ ਪੈਟਰਨ"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia ਵਾਲਪੇਪਰ, ਅਸਮਾਨੀ ਨੀਲਾ ਪੈਟਰਨ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "ਮੂਲ ਸਾਫ਼ ਵਾਲਪੇਪਰ।"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox ਵਾਲਪੇਪਰ, ਸੂਰਜ-ਚੜ੍ਹਨ ਪੈਟਰਨ।"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia ਵਾਲਪੇਪਰ, ਸੂਰਜ-ਚੜ੍ਹਨ ਪੈਟਰਨ।"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "ਮੁੱਖ ਸਫ਼ਾ ਵਾਲਪੇਪਰ ਚੱਕਰ ਬਦਲੋ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox ਵਾਲਪੇਪਰ, ਦੁਮੇਲ ਪਹਾੜੀ ਵਾਲੀ ਤਰਤੀਬ।"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia ਵਾਲਪੇਪਰ, ਦੁਮੇਲ ਪਹਾੜੀ ਵਾਲੀ ਤਰਤੀਬ।"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "ਕਲਾਸਿਕ %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "ਲਿਮਟਡ ਐਡੀਸ਼ਨ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox ਮੁੱਖ-ਸਫ਼ੇ ਲੋਗੋ ਨੂੰ ਛੂਹ ਕੇ ਵਾਲਪੇਪਰ ਬਦਲੋ"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia ਮੁੱਖ-ਸਫ਼ੇ ਲੋਗੋ ਨੂੰ ਛੂਹ ਕੇ ਵਾਲਪੇਪਰ ਬਦਲੋ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "ਵਾਲਪੇਪਰ"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "ਕਸਟਮ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox ਮੁੱਖ-ਸਫ਼ਾ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia ਮੁੱਖ-ਸਫ਼ਾ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ਮੁੱਖ-ਸਫ਼ਾ"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "ਚਿੱਤਰਾਂ ਤੇ ਪਾਬੰਦੀ ਲਾਓ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "ਜਦੋਂ Firefox ਖੋਲ੍ਹਣਾ ਹੋਵੇ"; +"Settings.OfferClipboardBar.Status" = "ਜਦੋਂ Ecosia ਖੋਲ੍ਹਣਾ ਹੋਵੇ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "ਜਦੋਂ %@ ਨੂੰ ਖੋਲ੍ਹਣਾ ਹੋਵੇ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "ਜਦੋਂ Firefox ਖੋਲ੍ਹਣਾ ਹੋਵੇ"; +"Settings.OfferClipboardBar.StatusV2" = "ਜਦੋਂ Ecosia ਖੋਲ੍ਹਣਾ ਹੋਵੇ"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "ਕਾਪੀ ਕੀਤੇ ਲਿੰਕਾਂ ਨੂੰ ਖੋਲ੍ਹਣ ਦੀ ਪੇਸ਼ਕਸ਼ ਕਰੋ"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "ਹੋਰ ਜਾਣੋ।"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ਸਿਰਫ਼ ਉਹੀ ਇਕੱਤਰ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ, ਜੋ ਕਿ ਸਾਨੂੰ ਹਰ ਕਿਸੇ ਲਈ Firefox ਪਹੁੰਚਣ ਅਤੇ ਸੁਧਾਰਨ ਲਈ ਚਾਹੀਦਾ ਹੈ।"; +"Settings.SendUsage.Message" = "Ecosia ਸਿਰਫ਼ ਉਹੀ ਇਕੱਤਰ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ, ਜੋ ਕਿ ਸਾਨੂੰ ਹਰ ਕਿਸੇ ਲਈ Ecosia ਪਹੁੰਚਣ ਅਤੇ ਸੁਧਾਰਨ ਲਈ ਚਾਹੀਦਾ ਹੈ।"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ਵਰਤੋ ਡਾਟੇ ਨੂੰ ਭੇਜੋ"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "ਨਵੀਂ ਟੈਬ ਖੋਲ੍ਹੋ"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri ਰਾਹੀਂ ਤੁਰੰਤ Firefox ਖੋਲ੍ਹਣ ਲਈ ਸੀਰੀ ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ ਵਰਤੋਂ"; +"Settings.Siri.SectionDescription" = "Siri ਰਾਹੀਂ ਤੁਰੰਤ Ecosia ਖੋਲ੍ਹਣ ਲਈ ਸੀਰੀ ਸ਼ਾਰਟਕੱਟਾਂ ਨੂੰ ਵਰਤੋਂ"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "ਸੀਰੀ ਸ਼ਾਰਟਕੱਟ"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "ਹੋਰ ਜਾਣੋ।"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox ਸਮੇਂ ਸਮੇਂ ਉੱਤੇ ਅਧਿਐਨ ਇੰਸਟਾਲ ਅਤੇ ਚਲਾ ਸਕਦਾ ਹੈ।"; +"Settings.Studies.Toggle.Message" = "Ecosia ਸਮੇਂ ਸਮੇਂ ਉੱਤੇ ਅਧਿਐਨ ਇੰਸਟਾਲ ਅਤੇ ਚਲਾ ਸਕਦਾ ਹੈ।"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "ਅਧਿਐਨ"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ਬੈਕਗਰਾਊਂਡ 'ਚ ਲੋਡ ਕਰੋ"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox ਵਿੱਚ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia ਵਿੱਚ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox 'ਚ ਖੋਲ੍ਹੋ"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia 'ਚ ਖੋਲ੍ਹੋ"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox ਵਿੱਚ ਖੋਜੋ"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia ਵਿੱਚ ਖੋਜੋ"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "ਕਾਪੀ ਕਰੋ"; @@ -1676,7 +1676,7 @@ "TabTray.OpenNewTab.KeyCodeTitle" = "ਨਵੀਂ ਟੈਬ ਖੋਲ੍ਹੋ"; /* The title on the button to look at private tabs. */ -"TabTray.SegmentedControlTitles.PrivateTabs" = "ਨਿੱਜੀ"; +"TabTray.SegmentedControlTitles.PrivateTabs" = "ਪ੍ਰਾਈਵੇਟ"; /* The title on the button to look at synced tabs. */ "TabTray.SegmentedControlTitles.SyncedTabs" = "ਸਿੰਕ ਕੀਤੀਆਂ"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "ਚੇਪੋ ਤੇ ਜਾਓ"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox ਨਾਲ ਖੋਜੋ"; +"UIMenuItem.SearchWithFirefox" = "Ecosia ਨਾਲ ਖੋਜੋ"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ਨਾ-ਪੜ੍ਹੇ"; @@ -1793,7 +1793,7 @@ "Yesterday" = "ਕੱਲ੍ਹ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "ਤੁਹਾਡੇ ਕੋਲ ਤੁਹਾਡੇ ਹੋਰ ਡਿਵਾਈਸ ਉੱਤੇ Firefox ਵਿੱਚ ਕੋਈ ਟੈਬ ਨਹੀਂ ਖੁੱਲ੍ਹੀ ਹੈ।"; +"You don’t have any tabs open in Firefox on your other devices." = "ਤੁਹਾਡੇ ਕੋਲ ਤੁਹਾਡੇ ਹੋਰ ਡਿਵਾਈਸ ਉੱਤੇ Ecosia ਵਿੱਚ ਕੋਈ ਟੈਬ ਨਹੀਂ ਖੁੱਲ੍ਹੀ ਹੈ।"; /* Your Rights settings section title */ "Your Rights" = "ਤੁਹਾਡੇ ਅਧਿਕਾਰ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/Menu.strings b/firefox-ios/Shared/pa-IN.lproj/Menu.strings index 5713bd8062345..7d2fa7af89491 100644 --- a/firefox-ios/Shared/pa-IN.lproj/Menu.strings +++ b/firefox-ios/Shared/pa-IN.lproj/Menu.strings @@ -14,7 +14,7 @@ "Menu.FindInPageAction.Title" = "ਸਫ਼ੇ 'ਚ ਲੱਭੋ"; /* Label for the button, displayed in the menu, used to open a new private tab. */ -"Menu.NewPrivateTabAction.Title" = "ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ ਖੋਲ੍ਹੋ"; +"Menu.NewPrivateTabAction.Title" = "ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਟੈਬ ਨੂੰ ਖੋਲ੍ਹੋ"; /* Label for the button, displayed in the menu, used to open a new tab */ "Menu.NewTabAction.Title" = "ਨਵੀਂ ਟੈਬ ਖੋਲ੍ਹੋ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/pa-IN.lproj/PrivateBrowsing.strings index 9daeb989b1f0c..fbb9b66b855f8 100644 --- a/firefox-ios/Shared/pa-IN.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/pa-IN.lproj/PrivateBrowsing.strings @@ -1,11 +1,11 @@ /* Setting for closing private tabs */ -"Close Private Tabs" = "ਨਿੱਜੀ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰੋ"; +"Close Private Tabs" = "ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰੋ"; /* Context menu option for opening a link in a new private tab */ -"ContextMenu.OpenInNewPrivateTabButtonTitle" = "ਨਵੀਂ ਨਿੱਜੀ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ"; +"ContextMenu.OpenInNewPrivateTabButtonTitle" = "ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਟੈਬ ਵਿੱਚ ਖੋਲ੍ਹੋ"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ਤੁਹਾਡੇ ਅਤੀਤ ਜਾਂ ਕੂਕੀਜ਼ ਨੂੰ ਯਾਦ ਨਹੀਂ ਰੱਖੇਗਾ, ਪਰ ਤੁਹਾਡੇ ਨਵੇਂ ਬੁੱਕਮਾਰਕਾਂ ਨੂੰ ਸੰਭਾਲਿਆ ਜਾਵੇਗਾ।"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ਤੁਹਾਡੇ ਅਤੀਤ ਜਾਂ ਕੂਕੀਜ਼ ਨੂੰ ਯਾਦ ਨਹੀਂ ਰੱਖੇਗਾ, ਪਰ ਤੁਹਾਡੇ ਨਵੇਂ ਬੁੱਕਮਾਰਕਾਂ ਨੂੰ ਸੰਭਾਲਿਆ ਜਾਵੇਗਾ।"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "ਹੋਰ ਸਿੱਖੋ"; @@ -17,17 +17,17 @@ "On" = "ਚਾਲੂ"; /* Title displayed for when there are no open tabs while in private mode */ -"Private Browsing" = "ਨਿੱਜੀ ਬਰਾਊਜ਼ਿੰਗ"; +"Private Browsing" = "ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ਿੰਗ"; /* Accessibility label for toggling on/off private mode */ -"Private Mode" = "ਨਿੱਜੀ ਮੋਡ"; +"Private Mode" = "ਪ੍ਰਾਈਵੇਟ ਮੋਡ"; /* Accessibility label for toggling on/off private mode */ "PrivateBrowsing.Toggle.A11y.Label.v132" = "ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ਿੰਗ"; /* Accessiblity hint for toggling on/off private mode */ -"Turns private mode on or off" = "ਨਿੱਜੀ ਮੋਡ ਨੂੰ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕਰਦੇ ਹੋ"; +"Turns private mode on or off" = "ਪ੍ਰਾਈਵੇਟ ਮੋਡ ਨੂੰ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕਰਦੇ ਹੋ"; /* Will be displayed in Settings under 'Close Private Tabs' */ -"When Leaving Private Browsing" = "ਜਦੋਂ ਤਸੀਂ ਨਿੱਜੀ ਬਰਾਊਜ਼ਿੰਗ ਨੂੰ ਛੱਡਦੇ ਹੋ"; +"When Leaving Private Browsing" = "ਜਦੋਂ ਤੁਸੀਂ ਪ੍ਰਾਈਵੇਟ ਬਰਾਊਜ਼ਿੰਗ ਨੂੰ ਛੱਡਦੇ ਹੋ"; diff --git a/firefox-ios/Shared/pa-IN.lproj/Today.strings b/firefox-ios/Shared/pa-IN.lproj/Today.strings index 8f8bcaa2e95e7..da867fa4ce9d4 100644 --- a/firefox-ios/Shared/pa-IN.lproj/Today.strings +++ b/firefox-ios/Shared/pa-IN.lproj/Today.strings @@ -1,11 +1,11 @@ /* Close Private Tabs button label */ -"TodayWidget.ClosePrivateTabsButton" = "ਨਿੱਜੀ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰੋ"; +"TodayWidget.ClosePrivateTabsButton" = "ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਨੂੰ ਬੰਦ ਕਰੋ"; /* Close Private Tabs */ -"TodayWidget.ClosePrivateTabsLabelV2" = "ਨਿੱਜੀ ਟੈਬਾਂ\nਨੂੰ ਬੰਦ ਕਰੋ"; +"TodayWidget.ClosePrivateTabsLabelV2" = "ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ\nਨੂੰ ਬੰਦ ਕਰੋ"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefox ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਆਪਣੀ ਮੁੱਖ ਸਕਰੀਨ ਉੱਤੇ ਜੋੜੋ"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosia ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਆਪਣੀ ਮੁੱਖ ਸਕਰੀਨ ਉੱਤੇ ਜੋੜੋ"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "ਕਾਪੀ ਕੀਤੇ ਲਿੰਕ 'ਤੇ ਜਾਓ"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "ਕੋਈ ਖੁੱਲ੍ਹੀ ਟੈਬ ਨਹੀਂ ਹੈ।"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox ਖੋਲ੍ਹੋ"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia ਖੋਲ੍ਹੋ"; /* Open New Private Tab button label */ -"TodayWidget.PrivateTabButtonLabelV1" = "ਨਿੱਜੀ ਖੋਜ"; +"TodayWidget.PrivateTabButtonLabelV1" = "ਪ੍ਰਾਈਵੇਟ ਖੋਜ"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "ਆਪਣੀ ਮੁੱਖ ਸਕਰੀਨ ਉੱਤੇ Firefox ਜੋੜੋ। ਵਿਜੈਟ ਜੋੜਨ ਦੇ ਬਾਅਦ ਇਸ ਨੂੰ ਸੋਧਣ ਲਈ ਛੂਹ ਕੇ ਰੱਖੋ ਅਤੇ ਵੱਖਰਾ ਸ਼ਾਰਟਕੱਟ ਚੁਣੋ।"; +"TodayWidget.QuickActionGalleryDescription" = "ਆਪਣੀ ਮੁੱਖ ਸਕਰੀਨ ਉੱਤੇ Ecosia ਜੋੜੋ। ਵਿਜੈਟ ਜੋੜਨ ਦੇ ਬਾਅਦ ਇਸ ਨੂੰ ਸੋਧਣ ਲਈ ਛੂਹ ਕੇ ਰੱਖੋ ਅਤੇ ਵੱਖਰਾ ਸ਼ਾਰਟਕੱਟ ਚੁਣੋ।"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "ਫ਼ੌਰੀ ਕਾਰਵਾਈਆਂ"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox ਸ਼ਾਰਟਕੱਟ"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia ਸ਼ਾਰਟਕੱਟ"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "ਆਪਣੀਆਂ ਖੁੱਲ੍ਹੀਆਂ ਟੈਬਾਂ ਲਈ ਸ਼ਾਰਟਕਟ ਜੋੜੋ।"; @@ -44,10 +44,10 @@ "TodayWidget.QuickViewGalleryTitle" = "ਫ਼ੌਰੀ ਝਲਕ"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox\nਵਿੱਚ ਖੋਜੋ"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia\nਵਿੱਚ ਖੋਜੋ"; /* Search in private tab */ -"TodayWidget.SearchInPrivateTabLabelV2" = "ਨਿੱਜੀ ਟੈਬ\nਵਿੱਚ ਖੋਜੋ"; +"TodayWidget.SearchInPrivateTabLabelV2" = "ਪ੍ਰਾਈਵੇਟ ਟੈਬ\nਵਿੱਚ ਖੋਜੋ"; /* Description for top sites widget to add Firefox top sites shotcuts to home screen */ "TodayWidget.TopSitesGalleryDescription" = "ਅਕਸਰ ਅਤੇ ਤਾਜ਼ਾ ਵੇਖੀਆਂ ਵੈੱਬਸਾਈਟਾਂ ਲਈ ਸ਼ਾਰਟਕੱਟ ਜੋੜੋ।"; diff --git a/firefox-ios/Shared/pl.lproj/Default Browser.strings b/firefox-ios/Shared/pl.lproj/Default Browser.strings index 8f410ea6c3dc4..6064cf710ead7 100644 --- a/firefox-ios/Shared/pl.lproj/Default Browser.strings +++ b/firefox-ios/Shared/pl.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Ustawienie Firefoksa jako domyślną przeglądarkę to głos na otwarty, dostępny Internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ustawienie Ecosia jako domyślną przeglądarkę to głos na otwarty, dostępny Internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Domyślnie lepszy Internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Dowiedz się, jak to zrobić"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Ustaw automatyczne otwieranie odnośników z witryn, wiadomości e-mail i aplikacji Messages w Firefoksie."; +"DefaultBrowserCard.Description" = "Ustaw automatyczne otwieranie odnośników z witryn, wiadomości e-mail i aplikacji Messages w Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Wybierz Firefoksa jako domyślną przeglądarkę, by szybkość, bezpieczeństwo i prywatność były automatyczne."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Wybierz Ecosia jako domyślną przeglądarkę, by szybkość, bezpieczeństwo i prywatność były automatyczne."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Podnieś poziom codziennego przeglądania"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blokuje średnio ponad 3000 elementów śledzących na użytkownika miesięcznie. Ustaw nas jako domyślną przeglądarkę, by cieszyć się prywatnością bez obaw."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blokuje średnio ponad 3000 elementów śledzących na użytkownika miesięcznie. Ustaw nas jako domyślną przeglądarkę, by cieszyć się prywatnością bez obaw."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox chroni Twoją prywatność"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia chroni Twoją prywatność"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Przełącz domyślną przeglądarkę"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Stuknij „Domyślna przeglądarka”"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Wybierz Firefoksa"; +"DefaultBrowserOnboarding.Description3" = "3. Wybierz Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Domyślna przeglądarka"; diff --git a/firefox-ios/Shared/pl.lproj/Intro.strings b/firefox-ios/Shared/pl.lproj/Intro.strings index 700a4dfca0543..09fb6f09ca491 100644 --- a/firefox-ios/Shared/pl.lproj/Intro.strings +++ b/firefox-ios/Shared/pl.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Zaloguj się"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Witamy w Firefoksie"; +"Intro.Slides.Welcome.Title.v2" = "Witamy w Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Zacznij przeglądać Internet"; diff --git a/firefox-ios/Shared/pl.lproj/Localizable.strings b/firefox-ios/Shared/pl.lproj/Localizable.strings index 84014fa3a90e8..d29b29fbd2404 100644 --- a/firefox-ios/Shared/pl.lproj/Localizable.strings +++ b/firefox-ios/Shared/pl.lproj/Localizable.strings @@ -14,7 +14,7 @@ "Aa" = "Aa"; /* About settings section title */ -"About" = "O Firefoksie"; +"About" = "O Ecosia"; /* The title for the pinning a shortcut action */ "ActivityStream.ContextMenu.AddToShortcuts" = "Dodaj do skrótów"; @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Może to być skutkiem błędnej konfiguracji lub próby ataku. Kontynuuj tylko, jeśli akceptujesz potencjalne ryzyko."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Właściciel witryny %@ niepoprawnie ją skonfigurował. Firefox nie połączył się z nią, by chronić użytkownika przed kradzieżą informacji."; +"ErrorPages.CertWarning.Description" = "Właściciel witryny %@ niepoprawnie ją skonfigurował. Ecosia nie połączył się z nią, by chronić użytkownika przed kradzieżą informacji."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Połączenie nie jest bezpieczne"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Strona startowa Firefoksa"; +"Firefox.HomePage.Title" = "Strona startowa Ecosii"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Dostosuj stronę startową"; @@ -377,7 +377,7 @@ "Forward" = "Przejdź do następnej strony"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Konto Firefoksa"; +"FxA.FirefoxAccount" = "Konto Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Zarządzaj kontem i urządzeniami"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Zaloguj się za pomocą aparatu"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Otwórz Firefoksa na komputerze i wejdź na stronę firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Otwórz Ecosia na komputerze i wejdź na stronę ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Gotowy do skanowania"; @@ -422,7 +422,7 @@ "FxAPush_DeviceDisconnected_UnknownDevice_body" = "Urządzenie zostało odłączone od synchronizacji"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefoksa, zmień tapetę."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, zmień tapetę."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomoc"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Więcej informacji"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Aby móc korzystać z funkcji automatycznego wypełniania w Firefoksie, musi być włączony kod urządzenia."; +"Logins.PasscodeRequirement.Warning" = "Aby móc korzystać z funkcji automatycznego wypełniania w Ecosia, musi być włączony kod urządzenia."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Miej hasła zawsze przy sobie"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Automatycznie wypełniaj hasła w Firefoksie"; +"Logins.WelcomeView.Title2" = "Automatycznie wypełniaj hasła w Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Włącz automatyczne wypełnianie"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "Zachowane dane logowania"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Wygląda na to, że Firefox uległ poprzednio awarii. Czy przywrócić karty?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Wygląda na to, że Ecosia uległ poprzednio awarii. Czy przywrócić karty?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Oznacz jako przeczytaną"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Zacznij przeglądać Internet"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Firefox uległ awarii :("; +"Oops! Firefox crashed" = "Ecosia uległ awarii :("; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Otwieraj artykuły w widoku poprawionej czytelności stukając ikonę książki, kiedy pojawi się w pasku adresu."; @@ -905,7 +905,7 @@ "Open Tabs" = "Otwarte karty"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox nie może otworzyć tej strony, gdyż jej adres jest nieprawidłowy."; +"OpenURL.Error.Message" = "Ecosia nie może otworzyć tej strony, gdyż jej adres jest nieprawidłowy."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nie można otworzyć strony"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Pozwoli to zachować obraz w albumie Wszystkie zdjęcia."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox chce uzyskać dostęp do Zdjęć"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia chce uzyskać dostęp do Zdjęć"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zamknij"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Dane są nieprawidłowe"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Otwórz „Ustawienia” urządzenia → „Firefox” i zezwól Firefoksowi na dostęp do aparatu."; +"ScanQRCode.PermissionError.Message.v100" = "Otwórz „Ustawienia” urządzenia → „Ecosia” i zezwól Firefoksowi na dostęp do aparatu."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skanowanie kodu QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Przełącz na kartę"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Podpowiedzi Firefoksa"; +"Search.SuggestSectionTitle.v102" = "Podpowiedzi Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nowa wyszukiwarka pojawi się na pasku szybkiego wyszukiwania i będzie można nią zarządzać poprzez ustawienia."; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Wyślij kartę"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Brak innych urządzeń połączonych z tym kontem Firefoksa dostępnych do synchronizacji."; +"SendTo.NoDevicesFound.Message" = "Brak innych urządzeń połączonych z tym kontem Ecosia dostępnych do synchronizacji."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Otwórz Firefoksa, przejdź do ustawień i zaloguj się, by kontynuować."; +"SendTo.NotSignedIn.Message" = "Otwórz Ecosia, przejdź do ustawień i zaloguj się, by kontynuować."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nie zalogowano na koncie Firefoksa."; +"SendTo.NotSignedIn.Title" = "Nie zalogowano na koncie Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Wyślij"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Stuknij, by rozpocząć"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Wyświetl"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Zarządzanie danymi"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox zatrzyma synchronizację z tym kontem, ale nie usunie danych przeglądania na tym urządzeniu."; +"Settings.Disconnect.Body" = "Ecosia zatrzyma synchronizację z tym kontem, ale nie usunie danych przeglądania na tym urządzeniu."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Rozłącz synchronizację"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Ustawienia synchronizacji"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Konto Firefoksa"; +"Settings.FxA.Title" = "Konto Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Ogólne"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Wybierz, co będzie wyświetlane jako strona startowa."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Wybierz zawartość strony startowej Firefoksa."; +"Settings.Home.Option.Description.v101" = "Wybierz zawartość strony startowej Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Wskocz z powrotem"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Strona startowa"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Wybierz, co wyświetlać po powrocie do Firefoksa."; +"Settings.Home.Option.StartAtHome.Description" = "Wybierz, co wyświetlać po powrocie do Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Ostatnia karta"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Tapeta"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tapeta Firefoksa, ametystowy wzór."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tapeta Ecosia, ametystowy wzór."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tapeta Firefoksa, wzór wzgórz na plaży."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tapeta Ecosia, wzór wzgórz na plaży."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tapeta Firefoksa, błękitny wzór."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tapeta Ecosia, błękitny wzór."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Domyślna, przezroczysta tapeta."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tapeta Firefoksa, wzór ze wschodem słońca."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tapeta Ecosia, wzór ze wschodem słońca."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Przełącznik tapety na stronie startowej"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tapeta Firefoksa, wzór wzgórz o zmierzchu."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tapeta Ecosia, wzór wzgórz o zmierzchu."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasyczny %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edycja limitowana"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Zmień tapetę, stukając logo na stronie startowej Firefoksa"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Zmień tapetę, stukając logo na stronie startowej Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Tapeta"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Inna"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Strona startowa Firefoksa"; +"Settings.NewTab.Option.FirefoxHome" = "Strona startowa Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Strona startowa"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokowanie obrazów"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "podczas otwierania Firefoksa"; +"Settings.OfferClipboardBar.Status" = "podczas otwierania Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "podczas otwierania przeglądarki %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "podczas otwierania Firefoksa"; +"Settings.OfferClipboardBar.StatusV2" = "podczas otwierania Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Otwieranie skopiowanych odnośników"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Więcej informacji."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla dokłada wszelkich starań, by zbierać jedynie niezbędne dane w celu dostarczania i ulepszania Firefoksa."; +"Settings.SendUsage.Message" = "Ecosia dokłada wszelkich starań, by zbierać jedynie niezbędne dane w celu dostarczania i ulepszania Ecosia."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Przesyłanie danych o użyciu"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Otwórz nową kartę"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Skróty Siri umożliwiają szybkie otwieranie Firefoksa przez Siri"; +"Settings.Siri.SectionDescription" = "Skróty Siri umożliwiają szybkie otwieranie Ecosia przez Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Skróty Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Więcej informacji."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox może od czasu do czasu instalować i przeprowadzać badania."; +"Settings.Studies.Toggle.Message" = "Ecosia może od czasu do czasu instalować i przeprowadzać badania."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Badania"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Wczytaj w tle"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Wczytywanie w Firefoksie"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Wczytywanie w Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Otwórz w Firefoksie"; +"ShareExtension.OpenInFirefoxAction.Title" = "Otwórz w Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Wyszukaj w Firefoksie"; +"ShareExtension.SeachInFirefoxAction.Title" = "Wyszukaj w Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiuj"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Wklej i przejdź"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Wyszukaj przez Firefoksa"; +"UIMenuItem.SearchWithFirefox" = "Wyszukaj przez Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "nieprzeczytane"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Wczoraj"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Brak otwartych kart w Firefoksach na innych urządzeniach."; +"You don’t have any tabs open in Firefox on your other devices." = "Brak otwartych kart w Ecosiach na innych urządzeniach."; /* Your Rights settings section title */ "Your Rights" = "O prawach użytkownika"; diff --git a/firefox-ios/Shared/pl.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/pl.lproj/PrivateBrowsing.strings index ee92e3f06ccea..a6af3cf640581 100644 --- a/firefox-ios/Shared/pl.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/pl.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Otwórz w nowej karcie prywatnej"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox nie zachowa historii przeglądania ani ciasteczek (dodane zakładki zostaną zachowane)"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia nie zachowa historii przeglądania ani ciasteczek (dodane zakładki zostaną zachowane)"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Więcej informacji"; diff --git a/firefox-ios/Shared/pl.lproj/Today.strings b/firefox-ios/Shared/pl.lproj/Today.strings index c38062de44869..8e97e1bf81adf 100644 --- a/firefox-ios/Shared/pl.lproj/Today.strings +++ b/firefox-ios/Shared/pl.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zamknij prywatne karty"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodaj skróty Firefoksa do ekranu głównego."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodaj skróty Ecosia do ekranu głównego."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Otwórz skopiowany odnośnik"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Brak otwartych kart."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Otwórz Firefoksa"; +"TodayWidget.OpenFirefoxLabel" = "Otwórz Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Wyszukaj prywatnie"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Dodaj skrót Firefoksa do ekranu głównego. Po dodaniu widżetu dotknij i przytrzymaj go, by go zmienić i wybrać inny skrót."; +"TodayWidget.QuickActionGalleryDescription" = "Dodaj skrót Ecosia do ekranu głównego. Po dodaniu widżetu dotknij i przytrzymaj go, by go zmienić i wybrać inny skrót."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Szybkie działania"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Skróty Firefoksa"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Skróty Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Dodaj skróty do otwartych kart."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Szybki podgląd"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Wyszukaj w Firefoksie"; +"TodayWidget.SearchInFirefoxV2" = "Wyszukaj w Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Wyszukaj w prywatnej karcie"; diff --git a/firefox-ios/Shared/pt-BR.lproj/Default Browser.strings b/firefox-ios/Shared/pt-BR.lproj/Default Browser.strings index a5fb4f93e0768..5aa2ae5a3b385 100644 --- a/firefox-ios/Shared/pt-BR.lproj/Default Browser.strings +++ b/firefox-ios/Shared/pt-BR.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Tornar o Firefox seu navegador padrão é um voto por uma internet aberta e acessível."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Tornar o Ecosia seu navegador padrão é um voto por uma internet aberta e acessível."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Torne padrão para uma Internet melhor"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Saiba como"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Abra links, emails e mensagens automaticamente no Firefox."; +"DefaultBrowserCard.Description" = "Abra links, emails e mensagens automaticamente no Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Escolha o Firefox como seu navegador padrão para tornar automático velocidade, segurança e privacidade."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Escolha o Ecosia como seu navegador padrão para tornar automático velocidade, segurança e privacidade."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Melhore a rotina de navegação"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "O Firefox bloqueia, em média, mais de 3.000 rastreadores por usuário a cada mês. Torne seu navegador padrão para ter tranquilidade em relação à privacidade."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "O Ecosia bloqueia, em média, mais de 3.000 rastreadores por usuário a cada mês. Torne seu navegador padrão para ter tranquilidade em relação à privacidade."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "O Firefox tem proteção de privacidade"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "O Ecosia tem proteção de privacidade"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Mude o navegador padrão"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Toque em App navegador padrão"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Selecione o Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Selecione o Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "App navegador padrão"; diff --git a/firefox-ios/Shared/pt-BR.lproj/Intro.strings b/firefox-ios/Shared/pt-BR.lproj/Intro.strings index f4f7d8fa8269e..2d521e8f69202 100644 --- a/firefox-ios/Shared/pt-BR.lproj/Intro.strings +++ b/firefox-ios/Shared/pt-BR.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Entrar"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Boas-vindas ao Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Boas-vindas ao Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Iniciar navegação"; diff --git a/firefox-ios/Shared/pt-BR.lproj/Localizable.strings b/firefox-ios/Shared/pt-BR.lproj/Localizable.strings index 6144fd9e781ee..d9428d1bb3635 100644 --- a/firefox-ios/Shared/pt-BR.lproj/Localizable.strings +++ b/firefox-ios/Shared/pt-BR.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Pode ser um erro de configuração ou uma adulteração feita por um invasor. Só prossiga se aceitar o risco em potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "O proprietário de %@ configurou este site de forma inadequada. Para proteger suas informações de ser roubadas, o Firefox não se conectou a este site."; +"ErrorPages.CertWarning.Description" = "O proprietário de %@ configurou este site de forma inadequada. Para proteger suas informações de ser roubadas, o Ecosia não se conectou a este site."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Esta conexão não é confiável"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Tela inicial do Firefox"; +"Firefox.HomePage.Title" = "Tela inicial do Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar tela inicial"; @@ -377,7 +377,7 @@ "Forward" = "Avançar"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Conta Firefox"; +"FxA.FirefoxAccount" = "Conta Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gerenciar conta e dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Conecte usando sua câmera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Em seu computador, abra o Firefox e acesse firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Em seu computador, abra o Ecosia e acesse ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pronto para digitalizar"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar agora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "O Firefox Sync foi conectado a %@"; +"FxAPush_DeviceConnected_body" = "O Ecosia Sync foi conectado a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync conectado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ foi desconectado com sucesso."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo foi desconectado com sucesso do Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo foi desconectado com sucesso do Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desconectado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desconectado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Um dispositivo foi desconectado do Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Um dispositivo foi desconectado do Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo do Firefox, alterar fundo de tela."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logotipo do Ecosia, alterar fundo de tela."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ajuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Entre para ver uma lista de abas de seus outros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Histórico"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Saiba mais"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para usar o recurso de preenchimento automático do Firefox, você deve ter ativado um código de acesso do dispositivo."; +"Logins.PasscodeRequirement.Warning" = "Para usar o recurso de preenchimento automático do Ecosia, você deve ter ativado um código de acesso do dispositivo."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Tenha suas senhas em qualquer lugar"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Preencher automaticamente senhas do Firefox"; +"Logins.WelcomeView.Title2" = "Preencher automaticamente senhas do Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Ativar preenchimento automático"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "LOGINS SALVOS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que o Firefox travou anteriormente. Quer restaurar suas abas?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que o Ecosia travou anteriormente. Quer restaurar suas abas?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como lido"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Iniciar navegação"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ops! O Firefox travou"; +"Oops! Firefox crashed" = "Ops! O Ecosia travou"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abra artigos no modo de leitura tocando no ícone de livro quando ele aparecer na barra de título."; @@ -905,7 +905,7 @@ "Open Tabs" = "Abas abertas"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "O Firefox não pode abrir a página porque o endereço é inválido."; +"OpenURL.Error.Message" = "O Ecosia não pode abrir a página porque o endereço é inválido."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Não é possível abrir a página"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Permite salvar a imagem no seu Rolo de Câmera."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "O Firefox quer acessar suas Fotos"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "O Ecosia quer acessar suas Fotos"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Fechar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Os dados são inválidos"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Vá em ‘Configurações’ do dispositivo > ‘Firefox’. Dê permissão ao Firefox para acessar a câmera."; +"ScanQRCode.PermissionError.Message.v100" = "Vá em ‘Configurações’ do dispositivo > ‘Ecosia’. Dê permissão ao Ecosia para acessar a câmera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Digitalizar código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Mudar para aba"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugestões Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugestões Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "O novo mecanismo de pesquisa aparecerá na barra de pesquisa rápida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Enviar um relatório de travamento para que a Mozilla possa corrigir o problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Enviar um relatório de travamento para que a Ecosia possa corrigir o problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar opinião"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar aba"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Você não tem nenhum outro dispositivo conectado a esta conta Firefox, disponível para sincronizar."; +"SendTo.NoDevicesFound.Message" = "Você não tem nenhum outro dispositivo conectado a esta conta Ecosia, disponível para sincronizar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Abra o Firefox, vá em Configurações e identifique-se para continuar."; +"SendTo.NotSignedIn.Message" = "Abra o Ecosia, vá em Configurações e identifique-se para continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Você não entrou na sua conta Firefox."; +"SendTo.NotSignedIn.Title" = "Você não entrou na sua conta Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Toque para começar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gerenciamento de dados"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "O Firefox deixará de sincronizar com a sua conta, mas não excluirá seus dados de navegação neste dispositivo."; +"Settings.Disconnect.Body" = "O Ecosia deixará de sincronizar com a sua conta, mas não excluirá seus dados de navegação neste dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desconectar sincronização"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Configurações de sincronização"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Conta Firefox"; +"Settings.FxA.Title" = "Conta Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Geral"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Escolha o que exibir como tela inicial."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Escolha o que incluir na tela inicial do Firefox."; +"Settings.Home.Option.Description.v101" = "Escolha o que incluir na tela inicial do Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Voltar direto para"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Tela inicial"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Escolha o que ver quando voltar ao Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Escolha o que ver quando voltar ao Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Aba mais recente"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fundo de tela"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fundo de tela do Firefox, padrão de ametista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fundo de tela do Ecosia, padrão de ametista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fundo de tela do Firefox, padrão de colinas de praia."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fundo de tela do Ecosia, padrão de colinas de praia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fundo de tela do Firefox, padrão azul celeste."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fundo de tela do Ecosia, padrão azul celeste."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fundo de tela limpo padrão."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fundo de tela do Firefox, padrão nascer do sol."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fundo de tela do Ecosia, padrão nascer do sol."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Alternar fundo da tela inicial"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fundo de tela do Firefox, padrão de montes ao anoitecer."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fundo de tela do Ecosia, padrão de montes ao anoitecer."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clássico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edição limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Mudar o fundo de tela tocando no logotipo do Firefox na tela inicial"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Mudar o fundo de tela tocando no logotipo do Ecosia na tela inicial"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fundo de tela"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizado"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Página inicial do Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Página inicial do Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Tela inicial"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imagens"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Ao abrir o Firefox"; +"Settings.OfferClipboardBar.Status" = "Ao abrir o Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Ao abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Ao abrir o Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Ao abrir o Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Oferecer abrir links copiados"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Saiba mais."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "A Mozilla se empenha em coletar somente o necessário para melhorar o Firefox para todos."; +"Settings.SendUsage.Message" = "A Ecosia se empenha em coletar somente o necessário para melhorar o Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar dados de uso"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir nova aba"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Use atalhos da Siri para abrir o Firefox rapidamente"; +"Settings.Siri.SectionDescription" = "Use atalhos da Siri para abrir o Ecosia rapidamente"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atalhos da Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Saiba mais."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "O Firefox pode instalar e executar estudos de vez em quando."; +"Settings.Studies.Toggle.Message" = "O Ecosia pode instalar e executar estudos de vez em quando."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudos"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Carregar em segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Carregando no Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Carregando no Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir no Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir no Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Pesquisar no Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Pesquisar no Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Colar e abrir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Pesquisar com o Firefox"; +"UIMenuItem.SearchWithFirefox" = "Pesquisar com o Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "não lido"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ontem"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Não tem nenhuma aba aberta no Firefox em outros dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "Não tem nenhuma aba aberta no Ecosia em outros dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Seus direitos"; diff --git a/firefox-ios/Shared/pt-BR.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/pt-BR.lproj/PrivateBrowsing.strings index d93224f6c3afa..5681fcb17ef59 100644 --- a/firefox-ios/Shared/pt-BR.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/pt-BR.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir em nova aba privativa"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "O Firefox não irá salvar seu histórico nem cookies, mas novos favoritos são salvos."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "O Ecosia não irá salvar seu histórico nem cookies, mas novos favoritos são salvos."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saiba mais"; diff --git a/firefox-ios/Shared/pt-BR.lproj/Today.strings b/firefox-ios/Shared/pt-BR.lproj/Today.strings index ce9deb1fbf586..daa609c4c2aef 100644 --- a/firefox-ios/Shared/pt-BR.lproj/Today.strings +++ b/firefox-ios/Shared/pt-BR.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Fechar abas privativas"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Adiciona atalhos do Firefox à tela inicial."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Adiciona atalhos do Ecosia à tela inicial."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir para link copiado"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nenhuma aba aberta."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Pesquisa privativa"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Adiciona um atalho do Firefox à tela inicial. Após adicionar o widget, mantenha pressionado para editar e selecionar um atalho diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Adiciona um atalho do Ecosia à tela inicial. Após adicionar o widget, mantenha pressionado para editar e selecionar um atalho diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Ações rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Atalhos do Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Atalhos do Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Adicione atalhos para suas abas abertas."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Visão rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Pesquisar no Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Pesquisar no Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Pesquisar em aba privativa"; diff --git a/firefox-ios/Shared/pt-PT.lproj/Default Browser.strings b/firefox-ios/Shared/pt-PT.lproj/Default Browser.strings index 61c7414a73d82..73fdb9a24234e 100644 --- a/firefox-ios/Shared/pt-PT.lproj/Default Browser.strings +++ b/firefox-ios/Shared/pt-PT.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Tornar o Firefox no seu navegador padrão é um voto por uma Internet aberta e acessível."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Tornar o Ecosia no seu navegador padrão é um voto por uma Internet aberta e acessível."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Por padrão uma Internet Melhor"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Saber mais"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Configure as ligações de sites, e-mails e mensagens para serem abertas automaticamente no Firefox."; +"DefaultBrowserCard.Description" = "Configure as ligações de sites, e-mails e mensagens para serem abertas automaticamente no Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Escolha o Firefox como o seu navegador padrão para tornar a velocidade, a segurança e a privacidade automáticas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Escolha o Ecosia como o seu navegador padrão para tornar a velocidade, a segurança e a privacidade automáticas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Eleve a navegação diária"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "O Firefox bloqueia, em média, mais de 3000 rastreadores por utilizador por mês. Torne-nos no seu navegador padrão para estar tranquilo relativamente à privacidade."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "O Ecosia bloqueia, em média, mais de 3000 rastreadores por utilizador por mês. Torne-nos no seu navegador padrão para estar tranquilo relativamente à privacidade."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "O Firefox tem a privacidade coberta"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "O Ecosia tem a privacidade coberta"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Alterar o seu navegador predefinido"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Toque em Navegador predefinido"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Selecione Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Selecione Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Navegador predefinido"; diff --git a/firefox-ios/Shared/pt-PT.lproj/Intro.strings b/firefox-ios/Shared/pt-PT.lproj/Intro.strings index 10fc2bc9ae6a4..ab972d1a34c0c 100644 --- a/firefox-ios/Shared/pt-PT.lproj/Intro.strings +++ b/firefox-ios/Shared/pt-PT.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Iniciar sessão"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bem-vindo ao Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bem-vindo ao Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Começar a navegar"; diff --git a/firefox-ios/Shared/pt-PT.lproj/Localizable.strings b/firefox-ios/Shared/pt-PT.lproj/Localizable.strings index 8eae02ddd47f1..787138ff45053 100644 --- a/firefox-ios/Shared/pt-PT.lproj/Localizable.strings +++ b/firefox-ios/Shared/pt-PT.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Pode ser um erro de configuração ou uma manipulação por um atacante. Proceda se aceita o risco potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "O proprietário de %@ configurou o website incorretamente. Para impedir que a sua informação fosse furtada, o Firefox não se ligou a este website."; +"ErrorPages.CertWarning.Description" = "O proprietário de %@ configurou o website incorretamente. Para impedir que a sua informação fosse furtada, o Ecosia não se ligou a este website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Esta ligação não é de confiança"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Página inicial do Firefox"; +"Firefox.HomePage.Title" = "Página inicial do Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Personalizar página inicial"; @@ -377,7 +377,7 @@ "Forward" = "Avançar"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Conta Firefox"; +"FxA.FirefoxAccount" = "Conta Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gerir conta e dispositivos"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Inicie a sessão com a sua Câmara"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "No seu computador, abra o Firefox e aceda a firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "No seu computador, abra o Ecosia e aceda a ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pronto para digitalizar"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronizar agora"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "O Firefox Sync ligou-se a %@"; +"FxAPush_DeviceConnected_body" = "O Ecosia Sync ligou-se a %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync ligado"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ foi desassociado com sucesso."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo foi desassociado com sucesso do Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Este dispositivo foi desassociado com sucesso do Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync desassociado"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync desassociado"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Um dispositivo foi desassociado do Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Um dispositivo foi desassociado do Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logótipo do Firefox, alterar o fundo."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logótipo do Ecosia, alterar o fundo."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ajuda"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Inicie sessão para ver uma lista de separadores dos seus outros dispositivos."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Histórico"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Saber mais"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Para utilizar a funcionalidade de autopreenchimento para o Firefox, deve ter um código do dispositivo ativado."; +"Logins.PasscodeRequirement.Warning" = "Para utilizar a funcionalidade de autopreenchimento para o Ecosia, deve ter um código do dispositivo ativado."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Leve as suas palavras-passe para todo o lado"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autopreenchimento de palavras-passe do Firefox"; +"Logins.WelcomeView.Title2" = "Autopreenchimento de palavras-passe do Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Ativar o Preenchimento automático"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "CREDENCIAIS GUARDADAS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que o Firefox falhou anteriormente. Gostaria de restaurar os seus separadores?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Parece que o Ecosia falhou anteriormente. Gostaria de restaurar os seus separadores?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar como lida"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Começar a navegar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! O Firefox falhou"; +"Oops! Firefox crashed" = "Ups! O Ecosia falhou"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Abra artigos na Vista de leitura, tocando no ícone do livro quando este aparece na barra de título."; @@ -905,7 +905,7 @@ "Open Tabs" = "Separadores abertos"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "O Firefox não consegue abrir a página porque tem um endereço inválido."; +"OpenURL.Error.Message" = "O Ecosia não consegue abrir a página porque tem um endereço inválido."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Não foi possível abrir a página"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Isto permite-lhe guardar a imagem para o seu Rolo da câmara."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "O Firefox gostaria de aceder às suas Fotografias"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "O Ecosia gostaria de aceder às suas Fotografias"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Fechar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Os dados são inválidos"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Aceda às ‘Definições’ do dispositivo > ‘Firefox’. Permita que o Firefox aceda à câmara."; +"ScanQRCode.PermissionError.Message.v100" = "Aceda às ‘Definições’ do dispositivo > ‘Ecosia’. Permita que o Ecosia aceda à câmara."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Digitalizar código QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Mudar para o separador"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Sugestões Firefox"; +"Search.SuggestSectionTitle.v102" = "Sugestões Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "O novo motor de pesquisa aparecerá na barra de pesquisa rápida."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Enviar um relatório de falha para que a Mozilla possa corrigir o problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Enviar um relatório de falha para que a Ecosia possa corrigir o problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Enviar feedback"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Enviar separador"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Não tem quaisquer outros dispositivos ligados a esta Conta Firefox disponíveis para sincronizar."; +"SendTo.NoDevicesFound.Message" = "Não tem quaisquer outros dispositivos ligados a esta Conta Ecosia disponíveis para sincronizar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Por favor, abra o Firefox, aceda às Definições e inicie sessão para continuar."; +"SendTo.NotSignedIn.Message" = "Por favor, abra o Ecosia, aceda às Definições e inicie sessão para continuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Não iniciou a sessão na sua Conta Firefox."; +"SendTo.NotSignedIn.Title" = "Não iniciou a sessão na sua Conta Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Enviar"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Toque para começar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Ver"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Gestão de dados"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "O Firefox irá parar a sincronização com a sua conta, mas não irá apagar quaisquer dos seus dados de navegação neste dispositivo."; +"Settings.Disconnect.Body" = "O Ecosia irá parar a sincronização com a sua conta, mas não irá apagar quaisquer dos seus dados de navegação neste dispositivo."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Desassociar o Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Definições de sincronização"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Conta Firefox"; +"Settings.FxA.Title" = "Conta Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Geral"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Escolha o que é mostrado como a página inicial."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Escolha o que está incluído na página inicial do Firefox."; +"Settings.Home.Option.Description.v101" = "Escolha o que está incluído na página inicial do Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Regressar"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Página inicial"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Escolha o que pretende ver ao voltar ao Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Escolha o que pretende ver ao voltar ao Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Último separador"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Fundo"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Imagem de fundo do Firefox, padrão ametista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Imagem de fundo do Ecosia, padrão ametista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Imagem de fundo do Firefox, padrão de colinas de praia."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Imagem de fundo do Ecosia, padrão de colinas de praia."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Imagem de fundo do Firefox, padrão cerúleo."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Imagem de fundo do Ecosia, padrão cerúleo."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Imagem de fundo limpa predefinida."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Imagem de fundo do Firefox, padrão do nascer do sol."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Imagem de fundo do Ecosia, padrão do nascer do sol."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Acionador do ciclo de fundos da página inicial"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Imagem de fundo do Firefox, padrão de colinas crepusculares."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Imagem de fundo do Ecosia, padrão de colinas crepusculares."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ clássico"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edição limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Altere o fundo ao tocar no logótipo da página principal do Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Altere o fundo ao tocar no logótipo da página principal do Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Fundo"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Personalizado"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Início do Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Início do Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Página inicial"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloquear imagens"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Ao abrir o Firefox"; +"Settings.OfferClipboardBar.Status" = "Ao abrir o Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Ao abrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Ao abrir o Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Ao abrir o Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Oferecer para abrir ligações copiadas"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Saber mais."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "A Mozilla esforça-se para apenas recolher o que precisamos para melhorar o Firefox para todos."; +"Settings.SendUsage.Message" = "A Ecosia esforça-se para apenas recolher o que precisamos para melhorar o Ecosia para todos."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Enviar dados de utilização"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Abrir novo separador"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilize atalhos de Siri para abrir rapidamente o Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Utilize atalhos de Siri para abrir rapidamente o Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Atalhos de Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Saber mais."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "O Firefox pode instalar e executar estudos esporadicamente."; +"Settings.Studies.Toggle.Message" = "O Ecosia pode instalar e executar estudos esporadicamente."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Estudos"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Carregar em segundo plano"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "A carregar no Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "A carregar no Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Abrir no Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Abrir no Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Pesquisar no Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Pesquisar no Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Colar e ir"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Pesquisar com o Firefox"; +"UIMenuItem.SearchWithFirefox" = "Pesquisar com o Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "não lido"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ontem"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Não tem quaisquer separadores abertos no Firefox nos seus outros dispositivos."; +"You don’t have any tabs open in Firefox on your other devices." = "Não tem quaisquer separadores abertos no Ecosia nos seus outros dispositivos."; /* Your Rights settings section title */ "Your Rights" = "Os seus direitos"; diff --git a/firefox-ios/Shared/pt-PT.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/pt-PT.lproj/PrivateBrowsing.strings index ee480a28ee1e4..72fac34921ecb 100644 --- a/firefox-ios/Shared/pt-PT.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/pt-PT.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Abrir num novo separador privado"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "O Firefox não irá memorizar qualquer histórico ou cookies, mas os novos marcadores serão guardados."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "O Ecosia não irá memorizar qualquer histórico ou cookies, mas os novos marcadores serão guardados."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Saber mais"; diff --git a/firefox-ios/Shared/pt-PT.lproj/Today.strings b/firefox-ios/Shared/pt-PT.lproj/Today.strings index f0cf42730327a..ed50ca5eda84b 100644 --- a/firefox-ios/Shared/pt-PT.lproj/Today.strings +++ b/firefox-ios/Shared/pt-PT.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Fechar separadores privados"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Adicionar atalhos do Firefox ao seu ecrã inicial."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Adicionar atalhos do Ecosia ao seu ecrã inicial."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir para a ligação copiada"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Sem separadores abertos."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Abrir o Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Abrir o Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Pesquisa privada"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Adicione um atalho do Firefox ao seu ecrã inicial. Depois de adicionar o widget, toque e aguarde para o editar e selecione um atalho diferente."; +"TodayWidget.QuickActionGalleryDescription" = "Adicione um atalho do Ecosia ao seu ecrã inicial. Depois de adicionar o widget, toque e aguarde para o editar e selecione um atalho diferente."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Ações rápidas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Atalhos do Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Atalhos do Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Adicione atalhos para os seus separadores abertos."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista rápida"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Pesquisar no Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Pesquisar no Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Pesquisar num separador privado"; diff --git a/firefox-ios/Shared/rm.lproj/Default Browser.strings b/firefox-ios/Shared/rm.lproj/Default Browser.strings index d7f3285e9032d..fde40be710804 100644 --- a/firefox-ios/Shared/rm.lproj/Default Browser.strings +++ b/firefox-ios/Shared/rm.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Cun definir Firefox sco navigatur da standard elegias ti in internet avert ed accessibel."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Cun definir Ecosia sco navigatur da standard elegias ti in internet avert ed accessibel."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "In meglier internet sco standard"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Vegnir a savair co"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Definescha che colliaziuns da websites, e-mails e messadis vegnan avertas automaticamain en Firefox."; +"DefaultBrowserCard.Description" = "Definescha che colliaziuns da websites, e-mails e messadis vegnan avertas automaticamain en Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Tscherna Firefox sco navigatur da standard per avair automaticamain sveltezza, segirezza e protecziun da datas."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Tscherna Ecosia sco navigatur da standard per avair automaticamain sveltezza, segirezza e protecziun da datas."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Meglierescha la navigaziun quotidiana"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blochescha en media passa 3000 fastizaders per utilisader. Definescha Firefox sco navigatur da standard e surlascha la protecziun da tias datas al navigatur."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blochescha en media passa 3000 fastizaders per utilisader. Definescha Ecosia sco navigatur da standard e surlascha la protecziun da tias datas al navigatur."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ha integrà la protecziun da datas"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ha integrà la protecziun da datas"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Mida tes navigatur da standard"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Smatgar sin l'app dal navigatur da standard"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Tscherner Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Tscherner Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "App dal navigatur da standard"; diff --git a/firefox-ios/Shared/rm.lproj/Intro.strings b/firefox-ios/Shared/rm.lproj/Intro.strings index 0d18f914addab..f3caa7ca17b6e 100644 --- a/firefox-ios/Shared/rm.lproj/Intro.strings +++ b/firefox-ios/Shared/rm.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "S'annunziar"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bainvegni tar Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bainvegni tar Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Cumenzar a navigar"; diff --git a/firefox-ios/Shared/rm.lproj/Localizable.strings b/firefox-ios/Shared/rm.lproj/Localizable.strings index 25a3c94567f97..827461f2c2790 100644 --- a/firefox-ios/Shared/rm.lproj/Localizable.strings +++ b/firefox-ios/Shared/rm.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Eventualmain sa tracti d'ina configuraziun mala u da l'emprova dad in attatgader. Cuntinuescha sche ti acceptas il ristg potenzial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Il possessur da %@ ha configurà mal sia website. Per proteger tias infurmaziuns dad enguladitschs n'ha Firefox betg stabilì ina connexiun cun questa website."; +"ErrorPages.CertWarning.Description" = "Il possessur da %@ ha configurà mal sia website. Per proteger tias infurmaziuns dad enguladitschs n'ha Ecosia betg stabilì ina connexiun cun questa website."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Questa connexiun n'è betg segira"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pagina da partenza da Firefox"; +"Firefox.HomePage.Title" = "Pagina da partenza da Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Persunalisar la pagina da partenza"; @@ -377,7 +377,7 @@ "Forward" = "Renviar"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Conto da Firefox"; +"FxA.FirefoxAccount" = "Conto da Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administrar il conto ed ils apparats"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "T'annunzia cun tia camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Avra Firefox sin tes computer e visita firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Avra Ecosia sin tes computer e visita ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pront per scannar"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sincronisar ussa"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync è connectà cun %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync è connectà cun %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync è connectà"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ è vegnì deconnetà cun success."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Quest apparat è vegnì deconnectà cun success da Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Quest apparat è vegnì deconnectà cun success da Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync è deconnectà"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync è deconnectà"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "In apparat è vegnì deconnectà da Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "In apparat è vegnì deconnectà da Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo da Firefox, midar il maletg dal fund davos."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo da Ecosia, midar il maletg dal fund davos."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Agid"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "T'annunzia per vesair ina glista dals tabs da tes auters apparats."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Cronologia"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Ulteriuras infurmaziuns"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Per utilisar la funcziun d'endataziun automatica per Firefox, stos ti activar in code-clav per l'apparat."; +"Logins.PasscodeRequirement.Warning" = "Per utilisar la funcziun d'endataziun automatica per Ecosia, stos ti activar in code-clav per l'apparat."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Prenda tes pleds-clav adina cun tai"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Endataziun automatica da pleds-clav en Firefox"; +"Logins.WelcomeView.Title2" = "Endataziun automatica da pleds-clav en Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Activar l'endataziun automatica"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "INFURMAZIUNS D'ANNUNZIA MEMORISADAS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "I para che Firefox saja collabà dacurt. Vuls ti restaurar tes tabs?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "I para che Ecosia saja collabà dacurt. Vuls ti restaurar tes tabs?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marcar sco legì"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Cumenzar a navigar"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oha! Firefox è collabà"; +"Oops! Firefox crashed" = "Oha! Ecosia è collabà"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Avra artitgels en la vista da lectura cun smatgar sin l'icona da cudesch sch'ella cumpara en la trav dal titel."; @@ -905,7 +905,7 @@ "Open Tabs" = "Tabs averts"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox na po betg avrir la pagina perquai ch'ella ha ina adressa nunvalida."; +"OpenURL.Error.Message" = "Ecosia na po betg avrir la pagina perquai ch'ella ha ina adressa nunvalida."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Impussibel d'avrir la pagina"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Quai ta lubescha da memorisar il maletg en «Camera Roll» (Aufnahmen)."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox dumonda l'access a tias fotografias"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia dumonda l'access a tias fotografias"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Serrar"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Las datas èn nunvalidas"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Ir als parameters da l'apparat > «Firefox». Permetter a Firefox dad acceder a la camera."; +"ScanQRCode.PermissionError.Message.v100" = "Ir als parameters da l'apparat > «Ecosia». Permetter a Ecosia dad acceder a la camera."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Scannar in code QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Midar al tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox propona"; +"Search.SuggestSectionTitle.v102" = "Ecosia propona"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "La nova maschina da tschertgar cumpara en la trav per la tschertga svelta."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Trametter in rapport da collaps per che Mozilla possia schliar il problem?"; +"Send a crash report so Mozilla can fix the problem?" = "Trametter in rapport da collaps per che Ecosia possia schliar il problem?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Trametter in resun"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Trametter il tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Ti n'has betg connectà auters apparats cun quest conto da Firefox che pudessan vegnir sincronisads."; +"SendTo.NoDevicesFound.Message" = "Ti n'has betg connectà auters apparats cun quest conto da Ecosia che pudessan vegnir sincronisads."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Avra p.pl. Firefox, va en las preferenzas e t'annunzia per cuntinuar."; +"SendTo.NotSignedIn.Message" = "Avra p.pl. Ecosia, va en las preferenzas e t'annunzia per cuntinuar."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Ti n'es betg annunzià cun tes conto da Firefox."; +"SendTo.NotSignedIn.Title" = "Ti n'es betg annunzià cun tes conto da Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Trametter"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Smatgar per cumenzar"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Mussar"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Administraziun da datas"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox chala da sincronisar tes conto, ma las datas da navigaziun restan sin quest apparat."; +"Settings.Disconnect.Body" = "Ecosia chala da sincronisar tes conto, ma las datas da navigaziun restan sin quest apparat."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Deconnectar Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Parameters da Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Conto da Firefox"; +"Settings.FxA.Title" = "Conto da Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Tscherna tge che vegn mussà sco pagina da partenza."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Tscherna il cuntegn da la pagina da partenza da Firefox."; +"Settings.Home.Option.Description.v101" = "Tscherna il cuntegn da la pagina da partenza da Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Returnar nà qua"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Pagina da partenza"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Tscherna tge mussar cura che ti turnas tar Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Tscherna tge mussar cura che ti turnas tar Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Ultim tab"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Maletg dal fund davos"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fund davos da Firefox, motiv cun ametist."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Fund davos da Ecosia, motiv cun ametist."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fund davos da Firefox, motiv cun dunas e riva."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Fund davos da Ecosia, motiv cun dunas e riva."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fund davos da Firefox, motiv azur."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Fund davos da Ecosia, motiv azur."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Fund davos cler predefinì."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fund davos da Firefox, motiv cun aurora."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Fund davos da Ecosia, motiv cun aurora."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Buttun per activar/deactivar il ciclus dal maletg dal fund davos da la pagina da partenza"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fund davos da Firefox, motiv cun collinas tranter di e notg."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Fund davos da Ecosia, motiv cun collinas tranter di e notg."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ classic"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Ediziun limitada"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Mida il maletg dal fund davos cun tutgar il logo da Firefox sin la pagina da partenza"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Mida il maletg dal fund davos cun tutgar il logo da Ecosia sin la pagina da partenza"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Maletg dal fund davos"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Persunalisà"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Pagina da partenza da Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Pagina da partenza da Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pagina da partenza"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Bloccar las graficas"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Cun avrir Firefox"; +"Settings.OfferClipboardBar.Status" = "Cun avrir Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Cun avrir %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Cun avrir Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Cun avrir Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Proponer d'avrir las colliaziuns copiadas"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Ulteriuras infurmaziuns."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla vul mo rimnar las datas necessarias per porscher e meglierar Firefox per in e scadin."; +"Settings.SendUsage.Message" = "Ecosia vul mo rimnar las datas necessarias per porscher e meglierar Ecosia per in e scadin."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Trametter datas davart l'utilisaziun"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Avrir in nov tab"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Utilisar scursanidas da Siri per avrir pli spert Firefox"; +"Settings.Siri.SectionDescription" = "Utilisar scursanidas da Siri per avrir pli spert Ecosia"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Scursanidas da Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Ulteriuras infurmaziuns."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox po installar e lantschar studis da temp en temp."; +"Settings.Studies.Toggle.Message" = "Ecosia po installar e lantschar studis da temp en temp."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studis"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Chargiar davos las culissas"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Chargiar en Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Chargiar en Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Avrir en Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Avrir en Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Tschertgar en Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Tschertgar en Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Copiar"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Encollar & dai"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Tschertgar cun Firefox"; +"UIMenuItem.SearchWithFirefox" = "Tschertgar cun Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "nunlegì"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Ier"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Ti n'has nagins tabs da Firefox averts sin auters apparats."; +"You don’t have any tabs open in Firefox on your other devices." = "Ti n'has nagins tabs da Ecosia averts sin auters apparats."; /* Your Rights settings section title */ "Your Rights" = "Tes dretgs"; diff --git a/firefox-ios/Shared/rm.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/rm.lproj/PrivateBrowsing.strings index e9c8d8d4c0d00..2f5bbe1f74595 100644 --- a/firefox-ios/Shared/rm.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/rm.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Avrir en in nov tab privat"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox na memorisescha betg la cronologia u cookies, ma novs segnapaginas vegnan memorisads."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia na memorisescha betg la cronologia u cookies, ma novs segnapaginas vegnan memorisads."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Ulteriuras infurmaziuns"; diff --git a/firefox-ios/Shared/rm.lproj/Today.strings b/firefox-ios/Shared/rm.lproj/Today.strings index 13ed404904c4e..cd8b30f9415f7 100644 --- a/firefox-ios/Shared/rm.lproj/Today.strings +++ b/firefox-ios/Shared/rm.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Serrar ils tabs privats"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Agiuntar scursanidas a Firefox a tes visur da partenza."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Agiuntar scursanidas a Ecosia a tes visur da partenza."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Ir a la colliaziun copiada"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Nagins tabs averts."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Avrir Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Avrir Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Tschertga privata"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Agiuntescha ina scursanida a Firefox a tes visur da partenza. Suenter l'avair agiuntà, tutga e tegna il widget per l'editar e tscherna in'autra scursanida."; +"TodayWidget.QuickActionGalleryDescription" = "Agiuntescha ina scursanida a Ecosia a tes visur da partenza. Suenter l'avair agiuntà, tutga e tegna il widget per l'editar e tscherna in'autra scursanida."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Acziuns sveltas"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Scursanidas da Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Scursanidas da Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Agiuntescha scursanidas a tes tabs averts."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vista svelta"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Tschertgar en Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Tschertgar en Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Tschertgar en in tab privat"; diff --git a/firefox-ios/Shared/ro.lproj/Default Browser.strings b/firefox-ios/Shared/ro.lproj/Default Browser.strings index d5b6e2f73dfa3..6462a94554edf 100644 --- a/firefox-ios/Shared/ro.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ro.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Setează linkurile de pe site-uri web, din mesaje de pe e-mail și din Mesaje ca să se deschidă automat cu Firefox."; +"DefaultBrowserCard.Description" = "Setează linkurile de pe site-uri web, din mesaje de pe e-mail și din Mesaje ca să se deschidă automat cu Ecosia."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Schimbă browserul implicit"; diff --git a/firefox-ios/Shared/ro.lproj/Intro.strings b/firefox-ios/Shared/ro.lproj/Intro.strings index f73e0ecce861a..dac5a61469095 100644 --- a/firefox-ios/Shared/ro.lproj/Intro.strings +++ b/firefox-ios/Shared/ro.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Autentifică-te"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Bine ai venit la Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Bine ai venit la Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Începe navigarea"; diff --git a/firefox-ios/Shared/ro.lproj/Localizable.strings b/firefox-ios/Shared/ro.lproj/Localizable.strings index 1e07167cb6cee..0977d0f19733b 100644 --- a/firefox-ios/Shared/ro.lproj/Localizable.strings +++ b/firefox-ios/Shared/ro.lproj/Localizable.strings @@ -260,7 +260,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Aceasta poate fi o configurare greșită sau un fals de la un atacator. Continuă dacă accepți riscul potențial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Proprietarul %@ și-a configurat site-ul web necorespunzător. Pentru a proteja informațiile împotriva furtului, Firefox nu s-a conectat la acest site web."; +"ErrorPages.CertWarning.Description" = "Proprietarul %@ și-a configurat site-ul web necorespunzător. Pentru a proteja informațiile împotriva furtului, Ecosia nu s-a conectat la acest site web."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Această conexiune nu prezintă încredere"; @@ -281,13 +281,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Pagina de start Firefox"; +"Firefox.HomePage.Title" = "Pagina de start Ecosia"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Înainte"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Cont Firefox"; +"FxA.FirefoxAccount" = "Cont Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Gestionează contul și dispozitivele"; @@ -299,7 +299,7 @@ "fxa.signin.camera-signin" = "Autentifică-te cu camera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Deschide Firefox pe calculator și intră pe firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Deschide Ecosia pe calculator și intră pe ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Gata de scanare"; @@ -311,7 +311,7 @@ "FxA.SyncNow" = "Sincronizează acum"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync s-a conectat la %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync s-a conectat la %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sincronizare conectată"; @@ -320,7 +320,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ a fost deconectat cu succes."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Acest dispozitiv a fost deconectat cu succes de la Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Acest dispozitiv a fost deconectat cu succes de la Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sincronizare deconectată"; @@ -329,7 +329,7 @@ "FxAPush_DeviceDisconnected_title" = "Sincronizare deconectată"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispozitiv a fost deconectat de la Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Un dispozitiv a fost deconectat de la Ecosia Sync"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ajutor"; @@ -362,7 +362,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Autentifică-te pentru a vedea o listă de file de pe celelalte dispozitive."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Istoric"; @@ -491,7 +491,7 @@ "LoginsList.Title" = "DATE DE AUTENTIFICARE SALVATE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Se pare că Firefox s-a închis neașteptat anterior. Dorești să restaurezi filele?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Se pare că Ecosia s-a închis neașteptat anterior. Dorești să restaurezi filele?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Marchează ca citit"; @@ -593,7 +593,7 @@ "Okay" = "Ok"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! Firefox s-a închis neașteptat"; +"Oops! Firefox crashed" = "Ups! Ecosia s-a închis neașteptat"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Deschide articole în modul de lectură atingând pictograma carte când apare în bara de titlu."; @@ -605,7 +605,7 @@ "Open Tabs" = "File deschise"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox nu poate deschide pagina deoarece are o adresă nevalidă."; +"OpenURL.Error.Message" = "Ecosia nu poate deschide pagina deoarece are o adresă nevalidă."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Nu se poate deschide pagina"; @@ -617,7 +617,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Aceasta permite salvarea imaginii în rolul camerei."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox solicită acces la dosarul tău de Fotografii"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia solicită acces la dosarul tău de Fotografii"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Închide"; @@ -731,7 +731,7 @@ "Search.ThirdPartyEngines.OK" = "Ok"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Trimiți un raport de defecțiune astfel încât Mozilla să poată remedia problema?"; +"Send a crash report so Mozilla can fix the problem?" = "Trimiți un raport de defecțiune astfel încât Ecosia să poată remedia problema?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Trimite feedback"; @@ -758,13 +758,13 @@ "SendTo.NavBar.Title" = "Trimite fila"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Nu mai ai niciun alt dispozitiv conectat la acest cont Firefox disponibil pentru sincronizare."; +"SendTo.NoDevicesFound.Message" = "Nu mai ai niciun alt dispozitiv conectat la acest cont Ecosia disponibil pentru sincronizare."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Te rugăm să deschizi Firefox, intră în Setări și autentifică-te pentru a continua."; +"SendTo.NotSignedIn.Message" = "Te rugăm să deschizi Ecosia, intră în Setări și autentifică-te pentru a continua."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nu ești autentificat(ă) în contul tău Firefox."; +"SendTo.NotSignedIn.Title" = "Nu ești autentificat(ă) în contul tău Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Trimite"; @@ -785,7 +785,7 @@ "SentTab.NoTabArrivingNotification.body" = "Atinge pentru a începe"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Vezi"; @@ -842,7 +842,7 @@ "Settings.DataManagement.Title" = "Gestionarea datelor"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox va opri sincronizarea cu contul tău, dar nu va șterge niciuna dintre datele de navigare de pe acest dispozitiv."; +"Settings.Disconnect.Body" = "Ecosia va opri sincronizarea cu contul tău, dar nu va șterge niciuna dintre datele de navigare de pe acest dispozitiv."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Deconectează Sync"; @@ -905,7 +905,7 @@ "Settings.FxA.Sync.SectionName" = "Setări de sincronizare"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Cont Firefox"; +"Settings.FxA.Title" = "Cont Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "General"; @@ -929,7 +929,7 @@ "Settings.NewTab.Option.BlankPage" = "Pagină goală"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Pagină de start Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Pagină de start Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Pagina de start"; @@ -947,10 +947,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Alege ce se încarcă la deschiderea unei file noi"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Când se deschide Firefox"; +"Settings.OfferClipboardBar.Status" = "Când se deschide Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Când se deschide Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Când se deschide Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Oferă deschiderea linkurilor copiate"; @@ -974,7 +974,7 @@ "Settings.SendUsage.Link" = "Află mai multe."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se străduiește să colecteze doar strictul necesar pentru a furniza și a îmbunătăți Firefox pentru toți."; +"Settings.SendUsage.Message" = "Ecosia se străduiește să colecteze doar strictul necesar pentru a furniza și a îmbunătăți Ecosia pentru toți."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Trimite date privind utilizarea"; @@ -995,7 +995,7 @@ "Settings.Siri.OpenTabShortcut" = "Deschide o filă nouă"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Folosește scurtături Siri pentru a deschide Firefox rapid prin intermediul Siri"; +"Settings.Siri.SectionDescription" = "Folosește scurtături Siri pentru a deschide Ecosia rapid prin intermediul Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Scurtături Siri"; @@ -1067,13 +1067,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Încarcă în fundal"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Se încarcă în Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Se încarcă în Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Deschide în Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Deschide în Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Caută în Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Caută în Ecosia"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Afișează sugestii de căutare"; @@ -1178,7 +1178,7 @@ "UIMenuItem.PasteGo" = "Lipește și accesează"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Caută cu Firefox"; +"UIMenuItem.SearchWithFirefox" = "Caută cu Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "necitit"; @@ -1208,7 +1208,7 @@ "Yesterday" = "Ieri"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Nu ai nicio filă deschisă în Firefox pe celelalte dispozitive ale tale."; +"You don’t have any tabs open in Firefox on your other devices." = "Nu ai nicio filă deschisă în Ecosia pe celelalte dispozitive ale tale."; /* Your Rights settings section title */ "Your Rights" = "Drepturile tale"; diff --git a/firefox-ios/Shared/ro.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ro.lproj/PrivateBrowsing.strings index 9e674d335caa0..a551b4f9d0d4d 100644 --- a/firefox-ios/Shared/ro.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ro.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Deschide într-o filă privată nouă"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox nu va reține istoricul sau cookie-urile, însă noile marcaje vor fi salvate."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia nu va reține istoricul sau cookie-urile, însă noile marcaje vor fi salvate."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Află mai multe"; diff --git a/firefox-ios/Shared/ro.lproj/Today.strings b/firefox-ios/Shared/ro.lproj/Today.strings index faec0bdce0442..35dd5f0819488 100644 --- a/firefox-ios/Shared/ro.lproj/Today.strings +++ b/firefox-ios/Shared/ro.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Închide filele private"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Adaugă scurtături Firefox în ecranul de pornire."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Adaugă scurtături Ecosia în ecranul de pornire."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Deschide linkul copiat"; @@ -23,13 +23,13 @@ "TodayWidget.NoOpenTabsLabel" = "Nicio filă deschisă."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Deschide Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Deschide Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Căutare privată"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Adaugă o scurtătură Firefox în ecranul de pornire. După ce ai adăugat widgetul, ține-l atins pentru editare și selectează o scurtătură diferită."; +"TodayWidget.QuickActionGalleryDescription" = "Adaugă o scurtătură Ecosia în ecranul de pornire. După ce ai adăugat widgetul, ține-l atins pentru editare și selectează o scurtătură diferită."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Acțiuni rapide"; @@ -38,7 +38,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Vizualizare rapidă"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Caută în Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Caută în Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Caută în filă privată"; diff --git a/firefox-ios/Shared/ru.lproj/Default Browser.strings b/firefox-ios/Shared/ru.lproj/Default Browser.strings index d1e75a24ee50b..3ab3f5660617f 100644 --- a/firefox-ios/Shared/ru.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ru.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Выбор Firefox в качестве браузера по умолчанию — это голосование за открытый и доступный Интернет."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Выбор Ecosia в качестве браузера по умолчанию — это голосование за открытый и доступный Интернет."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Перейдите на лучший интернет"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Узнайте как"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Настройте автоматическое открытие ссылок с веб-сайтов, из электронных писем и «Сообщений» в Firefox."; +"DefaultBrowserCard.Description" = "Настройте автоматическое открытие ссылок с веб-сайтов, из электронных писем и «Сообщений» в Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Выберите Firefox в качестве браузера по умолчанию, чтобы получить скорость, безопасность и конфиденциальность."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Выберите Ecosia в качестве браузера по умолчанию, чтобы получить скорость, безопасность и конфиденциальность."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Улучшите ежедневный просмотр"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ежемесячно блокирует для каждого пользователя в среднем более 3000 трекеров. Сделайте нас своим браузером по умолчанию для спокойствия по поводу конфиденциальности."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ежемесячно блокирует для каждого пользователя в среднем более 3000 трекеров. Сделайте нас своим браузером по умолчанию для спокойствия по поводу конфиденциальности."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox обеспечивает конфиденциальность"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia обеспечивает конфиденциальность"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Сменить браузер по умолчанию"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Выберите «Приложение браузера по умолчанию»"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Выберите Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Выберите Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Приложение браузера по умолчанию"; diff --git a/firefox-ios/Shared/ru.lproj/Intro.strings b/firefox-ios/Shared/ru.lproj/Intro.strings index 25f3e1c725c35..00ecf06d51ee0 100644 --- a/firefox-ios/Shared/ru.lproj/Intro.strings +++ b/firefox-ios/Shared/ru.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Войти"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Добро пожаловать в Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Добро пожаловать в Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Начать веб-сёрфинг"; diff --git a/firefox-ios/Shared/ru.lproj/Localizable.strings b/firefox-ios/Shared/ru.lproj/Localizable.strings index 878b1c4567e39..4d66b218f9c95 100644 --- a/firefox-ios/Shared/ru.lproj/Localizable.strings +++ b/firefox-ios/Shared/ru.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Это может быть из-за неправильной настройки или атаки злоумышленника. Вы можете продолжить, если принимаете на себя потенциальный риск."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Владелец %@ неправильно настроил свой веб-сайт. Чтобы защитить вашу информацию от кражи, Firefox не соединился с этим веб-сайтом."; +"ErrorPages.CertWarning.Description" = "Владелец %@ неправильно настроил свой веб-сайт. Чтобы защитить вашу информацию от кражи, Ecosia не соединился с этим веб-сайтом."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ваше соединение не защищено"; @@ -332,7 +332,7 @@ "Facebook" = "Фейсбук"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Домашняя страница Firefox"; +"Firefox.HomePage.Title" = "Домашняя страница Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Настроить домашнюю страницу"; @@ -377,7 +377,7 @@ "Forward" = "Вперёд"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Аккаунт Firefox"; +"FxA.FirefoxAccount" = "Аккаунт Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Управление аккаунтом и устройствами"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Войдите с помощью вашей камеры"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Запустите Firefox на вашем компьютере и перейдите на firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Запустите Ecosia на вашем компьютере и перейдите на ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Сканировать QR-код"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Синхронизировать"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Служба Синхронизации Firefox соединилась с %@"; +"FxAPush_DeviceConnected_body" = "Служба Синхронизации Ecosia соединилась с %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Синхронизация соединилась"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ было успешно отсоединено."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Это устройство было успешно отсоединено от службы Синхронизации Firefox."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Это устройство было успешно отсоединено от службы Синхронизации Ecosia."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Синхронизация отсоединилась"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Синхронизация отсоединилась"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Устройство отсоединилось от службы Синхронизации Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Устройство отсоединилось от службы Синхронизации Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Логотип Firefox, смена обоев."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Логотип Ecosia, смена обоев."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Справка"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Войдите, чтобы увидеть список вкладок с других устройств."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Синхронизация Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Синхронизация Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "История"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Подробнее"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Чтобы использовать функцию автозаполнения для Firefox, у вас должен быть включён код-пароль устройства."; +"Logins.PasscodeRequirement.Warning" = "Чтобы использовать функцию автозаполнения для Ecosia, у вас должен быть включён код-пароль устройства."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Возьмите свои пароли с собой"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Автозаполнение паролей Firefox"; +"Logins.WelcomeView.Title2" = "Автозаполнение паролей Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Включите автозаполнение"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "СОХРАНЁННЫЕ ЛОГИНЫ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Похоже, что Firefox ранее упал. Вы хотите восстановить ваши вкладки?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Похоже, что Ecosia ранее упал. Вы хотите восстановить ваши вкладки?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Отметить как прочитанное"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Начать веб-сёрфинг"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ой! Firefox упал"; +"Oops! Firefox crashed" = "Ой! Ecosia упал"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Открывайте статьи в виде для чтения, нажимая на значок книги, когда он появляется в адресной строке."; @@ -905,7 +905,7 @@ "Open Tabs" = "Открытые вкладки"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox не может открыть страницу, так как её адрес некорректен."; +"OpenURL.Error.Message" = "Ecosia не может открыть страницу, так как её адрес некорректен."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Не удалось открыть страницу"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Это позволит сохранить изображение в вашу «Фотопленку»."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox хотел бы получить доступ к вашим Фото"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia хотел бы получить доступ к вашим Фото"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Закрыть"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Недопустимые данные"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Перейдите в «Настройки» устройства > «Firefox». Разрешите Firefox доступ к камере."; +"ScanQRCode.PermissionError.Message.v100" = "Перейдите в «Настройки» устройства > «Ecosia». Разрешите Ecosia доступ к камере."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Сканировать QR-код"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Перейти на вкладку"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Новая поисковая система будет отображаться на панели быстрого поиска."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "ОК"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Отправить сообщение о падении, чтобы Mozilla могла устранить эту проблему?"; +"Send a crash report so Mozilla can fix the problem?" = "Отправить сообщение о падении, чтобы Ecosia могла устранить эту проблему?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Отправить отзыв"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Отправить вкладку"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "У вас нет ни одного устройства, подключенного к этому Аккаунту Firefox и доступного для синхронизации."; +"SendTo.NoDevicesFound.Message" = "У вас нет ни одного устройства, подключенного к этому Аккаунту Ecosia и доступного для синхронизации."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Пожалуйста, откройте Firefox, перейдите в «Настройки» и войдите для продолжения."; +"SendTo.NotSignedIn.Message" = "Пожалуйста, откройте Ecosia, перейдите в «Настройки» и войдите для продолжения."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Вы не вошли в свой Аккаунт Firefox."; +"SendTo.NotSignedIn.Title" = "Вы не вошли в свой Аккаунт Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Отправить"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Нажмите, чтобы начать"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Синхронизация Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Синхронизация Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Просмотреть"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Управление данными"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox прекратит синхронизацию с вашим аккаунтом, но не будет удалять ничего из ваших данных веб-сёрфинга на этом устройстве."; +"Settings.Disconnect.Body" = "Ecosia прекратит синхронизацию с вашим аккаунтом, но не будет удалять ничего из ваших данных веб-сёрфинга на этом устройстве."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Отсоединить"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Настройки синхронизации"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Аккаунт Firefox"; +"Settings.FxA.Title" = "Аккаунт Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Основные"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Выберите, что отображать в качестве домашней страницы."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Выберите, что включить в домашнюю страницу Firefox."; +"Settings.Home.Option.Description.v101" = "Выберите, что включить в домашнюю страницу Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Перейти обратно в"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Домашняя страница"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Выберите, что вы увидите, когда вернетесь в Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Выберите, что вы увидите, когда вернетесь в Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Последнюю вкладку"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Обои"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Обои Firefox, аметистовый узор."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Обои Ecosia, аметистовый узор."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Обои Firefox, узор пляжных холмов."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Обои Ecosia, узор пляжных холмов."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Обои Firefox, лазурный узор."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Обои Ecosia, лазурный узор."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Простые обои по умолчанию."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Обои Firefox, узор восхода солнца."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Обои Ecosia, узор восхода солнца."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Переключение цикла обоев домашней страницы"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Обои Firefox, узор холмов в сумерках."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Обои Ecosia, узор холмов в сумерках."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Классический %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Ограниченный выпуск"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Меняйте обои, коснувшись логотипа домашней страницы Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Меняйте обои, коснувшись логотипа домашней страницы Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Обои"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Другая"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Домашняя страница Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Домашняя страница Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Домашняя страница"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Блокировать изображения"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "При открытии Firefox"; +"Settings.OfferClipboardBar.Status" = "При открытии Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "При открытии %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "При открытии Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "При открытии Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Запрос открытия скопированных ссылок"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Подробнее."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla обязуется собирать только ту информацию, которая необходима для работы Firefox и его улучшения в будущем."; +"Settings.SendUsage.Message" = "Ecosia обязуется собирать только ту информацию, которая необходима для работы Ecosia и его улучшения в будущем."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Отправлять данные о работе"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Открыть новую вкладку"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Используйте команды Siri, чтобы быстро открыть Firefox через Siri"; +"Settings.Siri.SectionDescription" = "Используйте команды Siri, чтобы быстро открыть Ecosia через Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Команды Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Узнать больше."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox может время от времени устанавливать и проводить исследования."; +"Settings.Studies.Toggle.Message" = "Ecosia может время от времени устанавливать и проводить исследования."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Исследования"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Загрузить в фоне"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Загрузка в Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Загрузка в Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Открыть в Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Открыть в Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Искать в Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Искать в Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Копировать"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Вставить и перейти"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Поиск через Firefox"; +"UIMenuItem.SearchWithFirefox" = "Поиск через Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "непрочитано"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Вчера"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "На ваших других устройствах в Firefox не открыто ни одной вкладки."; +"You don’t have any tabs open in Firefox on your other devices." = "На ваших других устройствах в Ecosia не открыто ни одной вкладки."; /* Your Rights settings section title */ "Your Rights" = "Ваши права"; diff --git a/firefox-ios/Shared/ru.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ru.lproj/PrivateBrowsing.strings index 6144ae2464862..f22032886093b 100644 --- a/firefox-ios/Shared/ru.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ru.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Открыть в новой приватной вкладке"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox не будет запоминать ничего из вашей истории или кук, но новые закладки будут сохраняться."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia не будет запоминать ничего из вашей истории или кук, но новые закладки будут сохраняться."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Подробнее"; diff --git a/firefox-ios/Shared/ru.lproj/Today.strings b/firefox-ios/Shared/ru.lproj/Today.strings index 3f9bea61d1d4b..68161054c572e 100644 --- a/firefox-ios/Shared/ru.lproj/Today.strings +++ b/firefox-ios/Shared/ru.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Закрыть приватные вкладки"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Добавьте ярлыки Firefox на Домашний экран."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Добавьте ярлыки Ecosia на Домашний экран."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Перейти по скопированной ссылке"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Нет открытых вкладок."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Открыть Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Открыть Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Приватный поиск"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Добавьте ярлык Firefox на Домашний экран. После добавления виджета совершите долгое нажатие, чтобы отредактировать его и выбрать другой ярлык."; +"TodayWidget.QuickActionGalleryDescription" = "Добавьте ярлык Ecosia на Домашний экран. После добавления виджета совершите долгое нажатие, чтобы отредактировать его и выбрать другой ярлык."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Быстрые действия"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Ярлыки Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ярлыки Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Добавьте ярлыки на открытые вкладки."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Быстрый просмотр"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Искать в Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Искать в Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Искать в приватной вкладке"; diff --git a/firefox-ios/Shared/sat-Olck.lproj/Default Browser.strings b/firefox-ios/Shared/sat-Olck.lproj/Default Browser.strings index 502a95a28cad7..16061c82c3c5b 100644 --- a/firefox-ios/Shared/sat-Olck.lproj/Default Browser.strings +++ b/firefox-ios/Shared/sat-Olck.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox ᱫᱚ ᱟᱢᱟᱜ ᱢᱩᱞ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱞᱮᱠᱷᱟ ᱛᱮ ᱫᱚᱦᱚ ᱫᱚ ᱠᱷᱩᱞᱟᱹ, ᱟᱫᱮᱨ ᱫᱟᱲᱮᱭᱟᱜ ᱞᱮᱠᱷᱟ ᱤᱱᱴᱚᱨᱱᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱢᱤᱫᱴᱟᱝ ᱣᱚᱴ ᱞᱮᱠᱷᱟ ᱠᱟᱱᱟ ᱾"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia ᱫᱚ ᱟᱢᱟᱜ ᱢᱩᱞ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱞᱮᱠᱷᱟ ᱛᱮ ᱫᱚᱦᱚ ᱫᱚ ᱠᱷᱩᱞᱟᱹ, ᱟᱫᱮᱨ ᱫᱟᱲᱮᱭᱟᱜ ᱞᱮᱠᱷᱟ ᱤᱱᱴᱚᱨᱱᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱢᱤᱫᱴᱟᱝ ᱣᱚᱴ ᱞᱮᱠᱷᱟ ᱠᱟᱱᱟ ᱾"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "ᱢᱤᱫᱴᱟᱝ ᱵᱮᱥ ᱤᱱᱴᱚᱨᱱᱮᱴ ᱞᱟᱹᱜᱤᱫ ᱢᱩᱞ ᱞᱮᱠᱷᱟ ᱫᱚᱦᱚᱭ ᱢᱮ"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "ᱪᱮᱫᱢᱮ ᱚᱠᱟᱞᱠᱟ"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "ᱞᱤᱱᱠ ᱠᱚ ᱥᱟᱡᱟᱣ ᱢᱮ ᱣᱮᱵᱥᱟᱭᱤᱴ, ᱤᱢᱮᱞ, ᱟᱨ ᱢᱮᱥᱮᱡ ᱠᱷᱚᱱ Firefox ᱨᱮ ᱟᱪᱛᱮ ᱡᱷᱤᱜ ᱞᱚᱜᱤᱛ."; +"DefaultBrowserCard.Description" = "ᱞᱤᱱᱠ ᱠᱚ ᱥᱟᱡᱟᱣ ᱢᱮ ᱣᱮᱵᱥᱟᱭᱤᱴ, ᱤᱢᱮᱞ, ᱟᱨ ᱢᱮᱥᱮᱡ ᱠᱷᱚᱱ Ecosia ᱨᱮ ᱟᱪᱛᱮ ᱡᱷᱤᱜ ᱞᱚᱜᱤᱛ."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "ᱛᱮᱡᱽ, ᱨᱩᱠᱷᱤᱭᱟᱹ, ᱟᱨ ᱱᱤᱥᱚᱱ ᱟᱡ ᱛᱮ ᱫᱚᱦᱚ ᱪᱷᱚ ᱞᱟᱹᱜᱤᱫ Firefox ᱫᱚ ᱢᱩᱞ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱞᱮᱠᱷᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ᱾"; +"DefaultBrowserCard.NextLevel.Description.v108" = "ᱛᱮᱡᱽ, ᱨᱩᱠᱷᱤᱭᱟᱹ, ᱟᱨ ᱱᱤᱥᱚᱱ ᱟᱡ ᱛᱮ ᱫᱚᱦᱚ ᱪᱷᱚ ᱞᱟᱹᱜᱤᱫ Ecosia ᱫᱚ ᱢᱩᱞ ᱵᱽᱨᱟᱣᱡᱚᱨ ᱞᱮᱠᱷᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ᱾"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "ᱫᱤᱱ ᱨᱮᱭᱟᱜ ᱵᱽᱨᱟᱣᱡᱤᱝ ᱠᱟᱹᱢᱤ ᱠᱚ ᱵᱚᱰᱷᱟᱣ ᱢᱮ"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ᱫᱚ ᱢᱩᱴ 3,000+ ᱜᱟᱱ ᱯᱟᱧᱡᱟ ᱫᱟᱱᱟᱲ ᱢᱤᱫ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱯᱤᱪᱷᱟᱹ ᱡᱷᱚᱛᱚ ᱪᱟᱱᱫᱚ ᱵᱟᱹᱰ ᱮᱫ ᱠᱚᱣᱟᱭ ᱾ ᱯᱨᱟᱭᱣᱮᱥᱤ ᱥᱟᱱᱛ ᱢᱚᱱᱮ ᱞᱟᱹᱜᱤᱫ ᱞᱮ ᱫᱚ ᱟᱢᱟᱜ ᱢᱩᱞ ᱵᱽᱨᱟᱣᱩᱡᱚᱨ ᱞᱮᱠᱷᱟ ᱦᱟᱛᱟᱣ ᱞᱮᱢ ᱾"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ᱫᱚ ᱢᱩᱴ 3,000+ ᱜᱟᱱ ᱯᱟᱧᱡᱟ ᱫᱟᱱᱟᱲ ᱢᱤᱫ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱯᱤᱪᱷᱟᱹ ᱡᱷᱚᱛᱚ ᱪᱟᱱᱫᱚ ᱵᱟᱹᱰ ᱮᱫ ᱠᱚᱣᱟᱭ ᱾ ᱯᱨᱟᱭᱣᱮᱥᱤ ᱥᱟᱱᱛ ᱢᱚᱱᱮ ᱞᱟᱹᱜᱤᱫ ᱞᱮ ᱫᱚ ᱟᱢᱟᱜ ᱢᱩᱞ ᱵᱽᱨᱟᱣᱩᱡᱚᱨ ᱞᱮᱠᱷᱟ ᱦᱟᱛᱟᱣ ᱞᱮᱢ ᱾"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox ᱫᱚ ᱯᱨᱟᱭᱣᱮᱥᱤ ᱯᱩᱨᱟᱹᱣ ᱪᱷᱚ ᱠᱮᱜᱼᱟᱭ"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia ᱫᱚ ᱯᱨᱟᱭᱣᱮᱥᱤ ᱯᱩᱨᱟᱹᱣ ᱪᱷᱚ ᱠᱮᱜᱼᱟᱭ"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "ᱟᱢᱟᱜ ᱢᱩᱞ ᱯᱟᱱᱛᱮᱭᱟᱜ ᱵᱚᱫᱚᱞ ᱢᱮ"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. ᱢᱩᱞ ᱵᱽᱨᱟᱣᱩᱡᱽᱟᱹᱨ ᱮᱯ ᱨᱮ ᱚᱛᱟᱭ ᱢᱮ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox ᱵᱟᱪᱷᱟᱣᱢᱮ"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia ᱵᱟᱪᱷᱟᱣᱢᱮ"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "ᱢᱩᱞ ᱵᱽᱨᱟᱣᱩᱡᱽᱟᱹᱨ ᱮᱯ"; diff --git a/firefox-ios/Shared/sat-Olck.lproj/Intro.strings b/firefox-ios/Shared/sat-Olck.lproj/Intro.strings index c65702cd7af3f..10871e13e12e0 100644 --- a/firefox-ios/Shared/sat-Olck.lproj/Intro.strings +++ b/firefox-ios/Shared/sat-Olck.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox ᱨᱮ ᱥᱟᱹᱜᱩᱱ ᱫᱟᱨᱟᱢ"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia ᱨᱮ ᱥᱟᱹᱜᱩᱱ ᱫᱟᱨᱟᱢ"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "ᱯᱟᱱᱛᱮᱭᱟᱠ ᱮᱦᱚᱵ ᱢᱮ"; diff --git a/firefox-ios/Shared/sat-Olck.lproj/Localizable.strings b/firefox-ios/Shared/sat-Olck.lproj/Localizable.strings index 06eaca38d3c7e..bfe560d081bd6 100644 --- a/firefox-ios/Shared/sat-Olck.lproj/Localizable.strings +++ b/firefox-ios/Shared/sat-Olck.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "ᱱᱚᱶᱟ ᱠᱟᱹᱢᱤ ᱫᱚ ᱟᱹᱲᱤᱰᱷᱟᱹᱲᱤ ᱞᱟᱹᱜᱤᱫ ᱟᱨ ᱵᱟᱝ ᱢᱩᱸᱦᱟᱹᱤᱡ ᱨᱮᱭᱟᱜ ᱠᱟᱹᱢᱤ ᱦᱩᱭ ᱠᱚᱜᱼᱟᱭ ᱾ ᱡᱚᱫᱤ ᱟᱢ ᱟᱢᱟᱜ ᱫᱟᱭᱛᱣᱚ ᱤᱫᱤ ᱥᱟᱱᱟᱢ ᱠᱷᱟᱱ ᱢᱟᱲᱟᱝ ᱥᱮᱱ ᱞᱟᱦᱟᱜ ᱢᱮ ᱾"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ ᱨᱤᱱᱤᱡ ᱢᱟᱞᱤᱠ ᱫᱚ ᱣᱤᱵᱥᱟᱭᱤᱴ ᱵᱮᱥ ᱵᱟᱭ ᱴᱷᱤᱠ ᱞᱮᱹᱫᱟᱭ ᱾ ᱟᱢᱟᱜ ᱥᱟᱠᱷᱤᱭᱟᱹᱛ ᱟᱰ ᱫᱚᱦᱚ ᱟᱮ ᱟᱞᱚ ᱠᱩᱢᱰᱩᱼᱜ ᱞᱟᱹᱜᱤᱫ, Firefox ᱚᱱᱟ ᱣᱮᱵᱥᱟᱭᱤᱴ ᱨᱮ ᱵᱟᱭ ᱡᱩᱲᱟᱣ ᱞᱮᱱᱟᱭ ᱾"; +"ErrorPages.CertWarning.Description" = "%@ ᱨᱤᱱᱤᱡ ᱢᱟᱞᱤᱠ ᱫᱚ ᱣᱤᱵᱥᱟᱭᱤᱴ ᱵᱮᱥ ᱵᱟᱭ ᱴᱷᱤᱠ ᱞᱮᱹᱫᱟᱭ ᱾ ᱟᱢᱟᱜ ᱥᱟᱠᱷᱤᱭᱟᱹᱛ ᱟᱰ ᱫᱚᱦᱚ ᱟᱮ ᱟᱞᱚ ᱠᱩᱢᱰᱩᱼᱜ ᱞᱟᱹᱜᱤᱫ, Ecosia ᱚᱱᱟ ᱣᱮᱵᱥᱟᱭᱤᱴ ᱨᱮ ᱵᱟᱭ ᱡᱩᱲᱟᱣ ᱞᱮᱱᱟᱭ ᱾"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "ᱱᱚᱶᱟ ᱡᱩᱰᱟᱹᱣ ᱫᱚ ᱵᱟᱭ ᱯᱟᱹᱛᱤᱭᱟᱹᱣᱤᱡ ᱠᱟᱱᱟᱭ"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox ᱚᱲᱟᱜ ᱥᱟᱦᱟᱴᱟ"; +"Firefox.HomePage.Title" = "Ecosia ᱚᱲᱟᱜ ᱥᱟᱦᱟᱴᱟ"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱠᱩᱥᱤᱛᱮ ᱫᱚᱦᱚᱭ ᱢᱮ"; @@ -377,7 +377,7 @@ "Forward" = "ᱢᱟᱲᱟᱝ"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox ᱠᱷᱟᱛᱟ"; +"FxA.FirefoxAccount" = "Ecosia ᱠᱷᱟᱛᱟ"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ᱠᱷᱟᱛᱟ ᱟᱨ ᱥᱟᱫᱷᱚᱱ ᱠᱚ ᱵᱮᱵᱚᱥᱛᱟᱭ ᱢᱮ"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "ᱟᱢᱟᱜ ᱠᱮᱢᱮᱨᱟ ᱛᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱢᱮ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "ᱟᱢᱟᱜ ᱠᱚᱢᱯᱤᱭᱩᱴᱚᱨ ᱨᱮ Firefox ᱡᱷᱤᱜ ᱢᱮ ᱟᱨ firefox.com/pair ᱨᱮ ᱪᱟᱞᱟ ᱢᱮ"; +"fxa.signin.qr-link-instruction" = "ᱟᱢᱟᱜ ᱠᱚᱢᱯᱤᱭᱩᱴᱚᱨ ᱨᱮ Ecosia ᱡᱷᱤᱜ ᱢᱮ ᱟᱨ ecosia.org/pair ᱨᱮ ᱪᱟᱞᱟ ᱢᱮ"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "ᱥᱠᱮᱱ ᱞᱚᱜᱤᱛ ᱛᱮ ᱛᱮᱭᱟᱨ ᱢᱮᱱᱟᱢ-ᱟ"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "ᱱᱤᱛᱚᱠ ᱛᱚᱞ ᱡᱚᱠᱷᱟᱭ ᱢᱮ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox ᱟᱛᱩᱨ ᱟᱬᱜᱚ %@ ᱥᱟᱞᱟᱜ ᱡᱩᱰᱟᱹᱣ ᱮᱱᱟᱭ"; +"FxAPush_DeviceConnected_body" = "Ecosia ᱟᱛᱩᱨ ᱟᱬᱜᱚ %@ ᱥᱟᱞᱟᱜ ᱡᱩᱰᱟᱹᱣ ᱮᱱᱟᱭ"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "ᱛᱚᱞ ᱡᱚᱠᱷᱟ ᱡᱚᱲᱟᱣᱮᱱᱟ"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ ᱨᱟᱥ ᱞᱮᱠᱟᱛᱮ ᱵᱟᱰ ᱡᱚᱲᱟᱣ ᱦᱩᱭᱮᱱᱟ᱾"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ᱱᱚᱶᱟ ᱥᱟᱫᱷᱚᱱ ᱫᱚ ᱨᱟᱥ ᱞᱮᱠᱟᱛᱮ ᱵᱟᱰ ᱡᱚᱲᱟᱣᱮᱱᱟ Firefox ᱛᱷᱟᱨ ᱠᱷᱚᱱ᱾"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ᱱᱚᱶᱟ ᱥᱟᱫᱷᱚᱱ ᱫᱚ ᱨᱟᱥ ᱞᱮᱠᱟᱛᱮ ᱵᱟᱰ ᱡᱚᱲᱟᱣᱮᱱᱟ Ecosia ᱛᱷᱟᱨ ᱠᱷᱚᱱ᱾"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ᱛᱷᱟᱨ ᱵᱟᱰ ᱡᱚᱲᱟᱣᱮᱱᱟ"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "ᱛᱷᱟᱨ ᱵᱟᱰ ᱡᱚᱲᱟᱣᱮᱱᱟ"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ᱢᱤᱫᱴᱟᱝ ᱥᱟᱫᱷᱚᱱ ᱵᱟᱰ ᱡᱚᱲᱟᱣᱮᱱᱟ Firefox ᱛᱚᱞ ᱡᱚᱠᱷᱟ ᱠᱷᱚᱱ"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ᱢᱤᱫᱴᱟᱝ ᱥᱟᱫᱷᱚᱱ ᱵᱟᱰ ᱡᱚᱲᱟᱣᱮᱱᱟ Ecosia ᱛᱚᱞ ᱡᱚᱠᱷᱟ ᱠᱷᱚᱱ"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox ᱪᱤᱱᱦᱟᱹᱯ, ᱠᱟᱸᱛᱷᱥᱟᱦᱴᱟ ᱾"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia ᱪᱤᱱᱦᱟᱹᱯ, ᱠᱟᱸᱛᱷᱥᱟᱦᱴᱟ ᱾"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ᱜᱚᱲᱚ"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "ᱟᱢᱟᱠ ᱮᱴᱟᱠ ᱥᱟᱫᱷᱚᱱ ᱠᱷᱚᱱ ᱴᱮᱵᱽ ᱞᱤᱥᱴᱤ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱢᱮ᱾"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox ᱛᱷᱟᱨ ᱦᱚᱪᱚ"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia ᱛᱷᱟᱨ ᱦᱚᱪᱚ"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ᱦᱤᱛᱟᱹᱞ"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "ᱰᱷᱮᱨ ᱥᱮᱬᱟᱭ ᱢᱮ"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox ᱨᱮᱭᱟᱜ AutoFill ᱡᱤᱱᱤᱥ ᱵᱮᱵᱷᱟᱨ ᱞᱟᱹᱜᱤᱫ, ᱟᱢ ᱫᱚ ᱥᱟᱫᱷᱚᱱ ᱫᱟᱱᱟᱝᱠᱳᱰ ᱮᱢ ᱪᱷᱚ ᱞᱟᱹᱠᱛᱤ ᱜᱮ ᱠᱟᱱᱟ ᱾"; +"Logins.PasscodeRequirement.Warning" = "Ecosia ᱨᱮᱭᱟᱜ AutoFill ᱡᱤᱱᱤᱥ ᱵᱮᱵᱷᱟᱨ ᱞᱟᱹᱜᱤᱫ, ᱟᱢ ᱫᱚ ᱥᱟᱫᱷᱚᱱ ᱫᱟᱱᱟᱝᱠᱳᱰ ᱮᱢ ᱪᱷᱚ ᱞᱟᱹᱠᱛᱤ ᱜᱮ ᱠᱟᱱᱟ ᱾"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "ᱟᱢᱟᱜ ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱡᱟᱦᱟᱸ ᱨᱮᱜᱮ ᱤᱫᱤ ᱛᱟᱢ"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ AutoFill"; +"Logins.WelcomeView.Title2" = "Ecosia ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ AutoFill"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "AutoFill ᱮᱢ ᱪᱷᱚᱭ ᱢᱮ"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ᱵᱚᱞᱚ ᱠᱚ ᱥᱟᱧᱪᱟᱣ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ᱡᱟᱦᱟ ᱵᱩᱡᱷᱟᱣᱩᱜ ᱠᱟᱱᱟ Firefox ᱢᱟᱲᱟᱝ ᱨᱟᱹᱯᱩᱫ ᱞᱮᱱᱟ ᱾ ᱟᱢ ᱴᱮᱵᱥ ᱠᱚ ᱫᱩᱦᱲᱟᱹ ᱫᱚᱦᱚ ᱥᱟᱱᱟᱢ ᱠᱟᱱᱟ ᱥᱮ?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ᱡᱟᱦᱟ ᱵᱩᱡᱷᱟᱣᱩᱜ ᱠᱟᱱᱟ Ecosia ᱢᱟᱲᱟᱝ ᱨᱟᱹᱯᱩᱫ ᱞᱮᱱᱟ ᱾ ᱟᱢ ᱴᱮᱵᱥ ᱠᱚ ᱫᱩᱦᱲᱟᱹ ᱫᱚᱦᱚ ᱥᱟᱱᱟᱢ ᱠᱟᱱᱟ ᱥᱮ?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "ᱯᱟᱲᱦᱟᱣ ᱞᱮᱠᱟ ᱪᱤᱸᱦᱟᱭ ᱢᱮ"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "ᱯᱟᱱᱛᱮᱭᱟᱜ ᱮᱦᱚᱵ ᱢᱮ"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ᱚᱦᱟᱭ! Firefox ᱨᱚᱯᱩᱫᱮᱱᱟ"; +"Oops! Firefox crashed" = "ᱚᱦᱟᱭ! Ecosia ᱨᱚᱯᱩᱫᱮᱱᱟ"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "ᱴᱚᱭᱴᱚᱞ ᱵᱟᱨ ᱨᱮᱭᱟᱜ ᱯᱚᱛᱚᱵ ᱟᱭᱠᱚᱱ ᱨᱮ ᱞᱤᱱ ᱠᱟᱛᱮ ᱯᱟᱲᱦᱟᱣ ᱧᱮᱞ ᱨᱮ ᱚᱱᱚᱞ ᱠᱷᱩᱞᱟᱹᱢᱮ ᱾"; @@ -905,7 +905,7 @@ "Open Tabs" = "ᱴᱮᱵᱽ ᱠᱚ ᱡᱷᱤᱜ ᱢᱮ"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "ᱵᱟᱝ ᱴᱷᱤᱠ ᱴᱷᱤᱠᱬᱟᱹ ᱞᱟᱹᱜᱤᱫ Firefox ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱵᱟᱝ ᱚᱰᱚᱠ ᱫᱟᱲᱮᱟᱜ ᱠᱟᱱᱟ ᱾"; +"OpenURL.Error.Message" = "ᱵᱟᱝ ᱴᱷᱤᱠ ᱴᱷᱤᱠᱬᱟᱹ ᱞᱟᱹᱜᱤᱫ Ecosia ᱱᱚᱶᱟ ᱥᱟᱦᱴᱟ ᱵᱟᱝ ᱚᱰᱚᱠ ᱫᱟᱲᱮᱟᱜ ᱠᱟᱱᱟ ᱾"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "ᱥᱟᱦᱴᱟ ᱵᱟᱞᱮ ᱠᱷᱩᱞᱟᱹᱣ ᱫᱟᱹᱲᱤ ᱭᱟᱫᱟ"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "ᱱᱚᱶᱟ ᱟᱢᱟᱜ ᱪᱤᱛᱟᱹᱨ ᱠᱚ ᱠᱮᱢᱨᱟ ᱨᱚᱞᱞ ᱨᱮ ᱥᱟᱧᱪᱟᱣ ᱞᱟᱹᱜᱤᱫ ᱵᱟᱛᱟᱣᱟ ᱾"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ᱟᱢᱟᱠ ᱪᱤᱛᱚᱨ ᱠᱚ ᱟᱹᱛᱩᱨ ᱥᱟᱱᱟᱭ ᱤ ᱠᱟᱱᱟ"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ᱟᱢᱟᱠ ᱪᱤᱛᱚᱨ ᱠᱚ ᱟᱹᱛᱩᱨ ᱥᱟᱱᱟᱭ ᱤ ᱠᱟᱱᱟ"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "ᱵᱚᱸᱫᱚᱭ"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "ᱥᱟᱠᱷᱤᱭᱟᱛ ᱫᱚ ᱵᱟᱲ ᱵᱟᱛᱟᱣ ᱢᱮᱱᱟ-ᱟ"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "ᱥᱟᱫᱷᱚᱱ ‘ᱥᱟᱡᱟᱣ ᱠᱚ’> ‘Firefox’ ᱾ Firefox ᱫᱚ ᱠᱮᱢᱨᱟ ᱟᱫᱮᱨ ᱪᱷᱚᱣᱟᱭᱮᱢ ᱾"; +"ScanQRCode.PermissionError.Message.v100" = "ᱥᱟᱫᱷᱚᱱ ‘ᱥᱟᱡᱟᱣ ᱠᱚ’> ‘Ecosia’ ᱾ Ecosia ᱫᱚ ᱠᱮᱢᱨᱟ ᱟᱫᱮᱨ ᱪᱷᱚᱣᱟᱭᱮᱢ ᱾"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR ᱠᱚᱰ ᱥᱠᱟᱱ ᱢᱮ"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "ᱴᱮᱵᱽ ᱨᱮ ᱩᱪᱟᱹᱲᱚᱜ ᱢᱮ"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox ᱵᱟᱛᱟᱣ"; +"Search.SuggestSectionTitle.v102" = "Ecosia ᱵᱟᱛᱟᱣ"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "ᱱᱟᱶᱟ ᱯᱟᱱᱛᱮ ᱮᱱᱡᱤᱱ ᱫᱚ ᱞᱚᱜᱚᱱ ᱥᱮᱸᱽᱨᱟ ᱵᱟᱨ ᱨᱮ ᱫᱮᱠᱷᱟᱣᱜᱼᱟ ᱾"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "ᱴᱮᱵᱽ ᱠᱩᱞ ᱢᱮ"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "ᱲᱟᱢ ᱴᱷᱮᱱ ᱚᱞᱜᱟ Firefox ᱠᱷᱟᱛᱟ ᱟᱹᱭᱨ ᱢᱤᱫ ᱞᱟᱹᱜᱤᱫ ᱟᱞᱜᱟ ᱥᱟᱫᱷᱚᱱ ᱡᱩᱰᱟᱹᱣ ᱵᱟᱹᱱᱩᱜᱟ ᱾"; +"SendTo.NoDevicesFound.Message" = "ᱲᱟᱢ ᱴᱷᱮᱱ ᱚᱞᱜᱟ Ecosia ᱠᱷᱟᱛᱟ ᱟᱹᱭᱨ ᱢᱤᱫ ᱞᱟᱹᱜᱤᱫ ᱟᱞᱜᱟ ᱥᱟᱫᱷᱚᱱ ᱡᱩᱰᱟᱹᱣ ᱵᱟᱹᱱᱩᱜᱟ ᱾"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "ᱫᱭᱟ ᱠᱟᱛᱮ Firefox ᱡᱷᱤᱜ ᱢᱮ, ᱥᱟᱡᱟᱣ ᱠᱚ ᱨᱮ ᱪᱟᱞᱟ ᱢᱮ ᱟᱨ ᱞᱮᱛᱟᱰ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱢᱮ᱾"; +"SendTo.NotSignedIn.Message" = "ᱫᱭᱟ ᱠᱟᱛᱮ Ecosia ᱡᱷᱤᱜ ᱢᱮ, ᱥᱟᱡᱟᱣ ᱠᱚ ᱨᱮ ᱪᱟᱞᱟ ᱢᱮ ᱟᱨ ᱞᱮᱛᱟᱰ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱢᱮ᱾"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "ᱟᱢ ᱟᱢᱟᱠ Firefox ᱠᱷᱟᱛᱟ ᱨᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱵᱚᱱᱩᱢ-ᱟ ᱾"; +"SendTo.NotSignedIn.Title" = "ᱟᱢ ᱟᱢᱟᱠ Ecosia ᱠᱷᱟᱛᱟ ᱨᱮ ᱵᱚᱞᱚᱱ ᱥᱩᱦᱤ ᱵᱚᱱᱩᱢ-ᱟ ᱾"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ᱵᱷᱮᱡᱟ"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "ᱮᱦᱚᱵ ᱞᱟᱹᱜᱤᱫ ᱛᱮ ᱚᱛᱟᱭ ᱢᱮ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox ᱛᱚᱞ ᱡᱚᱠᱷᱟ"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia ᱛᱚᱞ ᱡᱚᱠᱷᱟ"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ᱧᱮᱞ ᱢᱮ"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "ᱥᱟᱠᱷᱤᱭᱟᱛ ᱵᱮᱵᱚᱥᱛᱟ"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ᱟᱢᱟᱜ ᱠᱷᱟᱛᱟ ᱥᱟᱞᱟᱜ ᱟᱭᱩᱨ ᱢᱤᱫ ᱵᱚᱸᱫᱭ ᱟᱭ, ᱦᱮᱞᱮ ᱟᱢᱟᱜ ᱥᱟᱫᱷᱚᱱ ᱠᱷᱚᱱ ᱪᱮᱫ ᱦᱚᱸ ᱵᱟᱭ ᱢᱮᱴᱟᱣᱟᱭ ᱾"; +"Settings.Disconnect.Body" = "Ecosia ᱟᱢᱟᱜ ᱠᱷᱟᱛᱟ ᱥᱟᱞᱟᱜ ᱟᱭᱩᱨ ᱢᱤᱫ ᱵᱚᱸᱫᱭ ᱟᱭ, ᱦᱮᱞᱮ ᱟᱢᱟᱜ ᱥᱟᱫᱷᱚᱱ ᱠᱷᱚᱱ ᱪᱮᱫ ᱦᱚᱸ ᱵᱟᱭ ᱢᱮᱴᱟᱣᱟᱭ ᱾"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ᱛᱚᱞ ᱡᱚᱠᱷᱟ ᱵᱟᱝ ᱡᱚᱲᱟᱣ"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "ᱛᱚᱞ ᱡᱚᱠᱷᱟ ᱥᱟᱡᱟᱣ ᱠᱚ"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox ᱠᱷᱟᱛᱟ"; +"Settings.FxA.Title" = "Ecosia ᱠᱷᱟᱛᱟ"; /* General settings section title */ "Settings.General.SectionName" = "ᱥᱟᱫᱷᱟᱨᱚᱱ"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱞᱮᱠᱷᱟᱛᱮ ᱪᱮᱫ ᱫᱮᱠᱷᱟᱣᱜᱼᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ᱾"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱞᱮᱠᱷᱟᱛᱮ ᱪᱮᱫ ᱫᱮᱠᱷᱟᱣᱜᱼᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ᱾"; +"Settings.Home.Option.Description.v101" = "Ecosia ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱞᱮᱠᱷᱟᱛᱮ ᱪᱮᱫ ᱫᱮᱠᱷᱟᱣᱜᱼᱟ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ᱾"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "ᱦᱮᱡ ᱨᱩᱣᱟᱹᱲᱚᱜ ᱢᱮ"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "ᱚᱲᱟᱜ ᱥᱟᱦᱴᱟ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox ᱨᱮ ᱫᱩᱦᱲᱟᱹ ᱦᱟᱹᱡᱩᱜ ᱡᱷᱚᱜ ᱵᱟᱪᱷᱟᱣᱢᱮ ᱪᱮᱫ ᱧᱮᱞ ᱥᱮᱱᱟᱮᱫ ᱢᱮᱭᱟ ᱾"; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia ᱨᱮ ᱫᱩᱦᱲᱟᱹ ᱦᱟᱹᱡᱩᱜ ᱡᱷᱚᱜ ᱵᱟᱪᱷᱟᱣᱢᱮ ᱪᱮᱫ ᱧᱮᱞ ᱥᱮᱱᱟᱮᱫ ᱢᱮᱭᱟ ᱾"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "ᱢᱩᱪᱟᱹᱫ ᱴᱮᱵᱽ"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "ᱠᱟᱸᱛᱷᱪᱤᱛᱟᱹᱨ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱞᱤᱞ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱞᱤᱞ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱫᱚᱨᱭᱟ ᱫᱷᱟᱨᱮ ᱵᱩᱨᱩ ᱨᱮᱭᱟᱜ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱫᱚᱨᱭᱟ ᱫᱷᱟᱨᱮ ᱵᱩᱨᱩ ᱨᱮᱭᱟᱜ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱚᱱᱟᱡᱽ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱚᱱᱟᱡᱽ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "ᱢᱩᱞ ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ ᱯᱷᱟᱨᱪᱟᱭ ᱢᱮ ᱾"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱵᱮᱲᱟ ᱨᱟᱠᱟᱵ ᱢᱟᱲᱟᱝ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱵᱮᱲᱟ ᱨᱟᱠᱟᱵ ᱢᱟᱲᱟᱝ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "ᱚᱲᱟᱜᱥᱟᱦᱴᱟ ᱞᱟᱴᱷᱟᱪᱤᱛᱟᱹᱨ ᱪᱚᱠᱨᱚ ᱩᱪᱟᱹᱲ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱵᱮᱲᱟ ᱦᱟᱹᱥᱩᱨ ᱵᱩᱨᱩ ᱨᱮᱭᱟᱜ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ, ᱵᱮᱲᱟ ᱦᱟᱹᱥᱩᱨ ᱵᱩᱨᱩ ᱨᱮᱭᱟᱜ ᱱᱟᱹᱢᱩᱱᱟ ᱾"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "ᱠᱞᱟᱥᱤᱠ %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "ᱥᱤᱢᱤᱛ ᱮᱰᱤᱥᱚᱱ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox ᱚᱲᱟᱜ ᱥᱟᱦᱴᱟ ᱞᱚᱜᱚ ᱨᱮ ᱴᱤᱯᱟᱹᱣ ᱠᱟᱛᱮ ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ ᱵᱚᱫᱚᱞ ᱢᱮ"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia ᱚᱲᱟᱜ ᱥᱟᱦᱴᱟ ᱞᱚᱜᱚ ᱨᱮ ᱴᱤᱯᱟᱹᱣ ᱠᱟᱛᱮ ᱞᱟᱴᱷᱟ ᱪᱤᱛᱟᱹᱨ ᱵᱚᱫᱚᱞ ᱢᱮ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "ᱠᱟᱸᱛᱷᱪᱤᱛᱟᱹᱨ"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "ᱠᱩᱥᱤᱭᱟᱜ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox ᱚᱲᱟᱜ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia ᱚᱲᱟᱜ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ᱚᱲᱟᱜ ᱥᱟᱦᱴᱟ"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "ᱪᱤᱛᱟᱹᱨ ᱠᱚ ᱟᱴᱠᱟᱣ ᱢᱮ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "ᱡᱚᱠᱷᱚᱱ Firefox ᱡᱷᱤᱜ ᱟᱢ"; +"Settings.OfferClipboardBar.Status" = "ᱡᱚᱠᱷᱚᱱ Ecosia ᱡᱷᱤᱜ ᱟᱢ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@ ᱠᱷᱩᱞᱟᱹ ᱡᱷᱚᱜ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "ᱡᱚᱠᱷᱚᱱ Firefox ᱡᱷᱤᱜ ᱟᱢ"; +"Settings.OfferClipboardBar.StatusV2" = "ᱡᱚᱠᱷᱚᱱ Ecosia ᱡᱷᱤᱜ ᱟᱢ"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "ᱱᱚᱠᱚᱞ ᱟᱠᱟᱱ ᱥᱟᱭᱤᱴ ᱠᱷᱩᱞᱟᱹ ᱯᱨᱚᱥᱛᱟᱣ"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "ᱰᱷᱮᱨ ᱥᱮᱲᱟᱭ ᱢᱮ᱾"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ᱡᱟᱦᱟᱸ ᱩᱱ ᱡᱚᱠᱷᱟᱜ ᱡᱟᱣᱨᱟ ᱟᱭ ᱚᱠᱟ ᱡᱷᱚᱜ ᱫᱚᱨᱠᱟᱨᱚᱜᱼᱟ ᱚᱱᱟ ᱦᱚᱸ Firefox ᱟᱨ ᱡᱷᱚᱛᱚ ᱦᱚᱲ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱵᱮᱥ ᱫᱚᱦᱚ ᱞᱟᱹᱜᱤᱫ ᱾"; +"Settings.SendUsage.Message" = "Ecosia ᱡᱟᱦᱟᱸ ᱩᱱ ᱡᱚᱠᱷᱟᱜ ᱡᱟᱣᱨᱟ ᱟᱭ ᱚᱠᱟ ᱡᱷᱚᱜ ᱫᱚᱨᱠᱟᱨᱚᱜᱼᱟ ᱚᱱᱟ ᱦᱚᱸ Ecosia ᱟᱨ ᱡᱷᱚᱛᱚ ᱦᱚᱲ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱵᱮᱥ ᱫᱚᱦᱚ ᱞᱟᱹᱜᱤᱫ ᱾"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ᱵᱮᱵᱷᱟᱨ ᱥᱟᱠᱷᱤᱭᱟᱛ ᱠᱩᱞ ᱢᱮ"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "ᱱᱟᱶᱟ ᱴᱮᱵᱽ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "ᱥᱤᱨᱤ ᱠᱷᱟᱴᱚ ᱢᱟᱪᱷᱟ ᱵᱮᱵᱷᱟᱨ ᱢᱮ ᱞᱚᱜᱚᱱ ᱛᱮ Firefox ᱠᱷᱩᱞᱟᱹᱭ ᱞᱚᱜᱤᱛ ᱥᱤᱨᱤ ᱥᱟᱣ ᱛᱮ"; +"Settings.Siri.SectionDescription" = "ᱥᱤᱨᱤ ᱠᱷᱟᱴᱚ ᱢᱟᱪᱷᱟ ᱵᱮᱵᱷᱟᱨ ᱢᱮ ᱞᱚᱜᱚᱱ ᱛᱮ Ecosia ᱠᱷᱩᱞᱟᱹᱭ ᱞᱚᱜᱤᱛ ᱥᱤᱨᱤ ᱥᱟᱣ ᱛᱮ"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "ᱥᱤᱨᱤ ᱠᱷᱟᱴᱚ ᱢᱟᱪᱷᱟ ᱠᱚ"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "ᱰᱷᱮᱨ ᱥᱮᱬᱟᱭ ᱢᱮ ᱾"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox ᱵᱚᱦᱟᱞ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱟᱨ ᱥᱚᱢᱚᱭ ᱠᱷᱚᱱ ᱥᱚᱢᱚᱨ ᱯᱟᱲᱦᱟᱣ ᱠᱚ ᱪᱟᱹᱞᱩᱜᱼᱟ ᱾"; +"Settings.Studies.Toggle.Message" = "Ecosia ᱵᱚᱦᱟᱞ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱟᱨ ᱥᱚᱢᱚᱭ ᱠᱷᱚᱱ ᱥᱚᱢᱚᱨ ᱯᱟᱲᱦᱟᱣ ᱠᱚ ᱪᱟᱹᱞᱩᱜᱼᱟ ᱾"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "ᱯᱟᱲᱦᱟᱣ ᱠᱚ"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ᱩᱯᱨᱩᱢ ᱨᱮ ᱞᱟᱫᱮ ᱢᱮ"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox ᱨᱮ ᱞᱟᱫᱮ ᱦᱩᱭᱩ ᱠᱟᱱᱟ"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia ᱨᱮ ᱞᱟᱫᱮ ᱦᱩᱭᱩ ᱠᱟᱱᱟ"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox ᱨᱮ ᱡᱷᱤᱜ ᱢᱮ"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia ᱨᱮ ᱡᱷᱤᱜ ᱢᱮ"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "ᱱᱚᱠᱚᱞ"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "ᱞᱟᱴᱷᱟᱭ ᱢᱮ ᱟᱨ ᱪᱦᱟᱞᱟ ᱢᱮ"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox ᱥᱟᱣ ᱛᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; +"UIMenuItem.SearchWithFirefox" = "Ecosia ᱥᱟᱣ ᱛᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ᱵᱟᱝ ᱯᱟᱲᱦᱟᱣ"; @@ -1793,7 +1793,7 @@ "Yesterday" = "ᱦᱚᱞᱟ"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "ᱟᱢ ᱴᱷᱮᱱ ᱚᱠᱟ ᱴᱮᱵᱽ ᱦᱚᱸ Firefox ᱨᱮᱭᱟᱜ ᱚᱞᱜᱟ ᱥᱟᱫᱷᱚᱸ ᱨᱮ ᱵᱟᱭ ᱠᱷᱩᱞᱟᱹ ᱟᱠᱟᱱᱟ ᱾"; +"You don’t have any tabs open in Firefox on your other devices." = "ᱟᱢ ᱴᱷᱮᱱ ᱚᱠᱟ ᱴᱮᱵᱽ ᱦᱚᱸ Ecosia ᱨᱮᱭᱟᱜ ᱚᱞᱜᱟ ᱥᱟᱫᱷᱚᱸ ᱨᱮ ᱵᱟᱭ ᱠᱷᱩᱞᱟᱹ ᱟᱠᱟᱱᱟ ᱾"; /* Your Rights settings section title */ "Your Rights" = "ᱟᱢᱟᱠ ᱦᱚᱠ"; diff --git a/firefox-ios/Shared/sat-Olck.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/sat-Olck.lproj/PrivateBrowsing.strings index 37df630392eb0..1488121d7b68e 100644 --- a/firefox-ios/Shared/sat-Olck.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/sat-Olck.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "ᱱᱟᱶᱟ ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵᱽ ᱨᱮ ᱡᱷᱤᱜ ᱢᱮ"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ᱫᱚ ᱟᱢᱟᱜ ᱦᱤᱛᱟᱹᱞ ᱟᱨ ᱠᱩᱠᱤ ᱠᱚ ᱵᱟᱭ ᱩᱭᱦᱟᱹᱨ ᱟᱭ, ᱦᱮᱞᱮ ᱱᱟᱶᱟ ᱵᱩᱠᱢᱟᱨᱠ ᱫᱚ ᱥᱟᱧᱪᱟᱣ ᱜᱮᱭᱟ ᱾"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ᱫᱚ ᱟᱢᱟᱜ ᱦᱤᱛᱟᱹᱞ ᱟᱨ ᱠᱩᱠᱤ ᱠᱚ ᱵᱟᱭ ᱩᱭᱦᱟᱹᱨ ᱟᱭ, ᱦᱮᱞᱮ ᱱᱟᱶᱟ ᱵᱩᱠᱢᱟᱨᱠ ᱫᱚ ᱥᱟᱧᱪᱟᱣ ᱜᱮᱭᱟ ᱾"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "ᱰᱷᱮᱨ ᱥᱮᱲᱟᱭ ᱢᱮ"; diff --git a/firefox-ios/Shared/sat-Olck.lproj/Today.strings b/firefox-ios/Shared/sat-Olck.lproj/Today.strings index 8ee01c86c491e..ab76f1997ed15 100644 --- a/firefox-ios/Shared/sat-Olck.lproj/Today.strings +++ b/firefox-ios/Shared/sat-Olck.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵᱽ ᱠᱚ ᱵᱚᱸᱫᱚ"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Firefox ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱟᱢᱟᱜ ᱚᱲᱟᱜ ᱤᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫ ᱢᱮ."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ecosia ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱟᱢᱟᱜ ᱚᱲᱟᱜ ᱤᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫ ᱢᱮ."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "ᱱᱚᱠᱚᱞ ᱟᱠᱟᱱ ᱞᱮᱸᱠ ᱛᱮ ᱪᱟᱞᱟᱜᱢᱮ"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "ᱴᱮᱵᱽ ᱠᱚ ᱡᱷᱤ ᱵᱚᱱᱩ-ᱟ."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox ᱡᱷᱤᱜ ᱢᱮ"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia ᱡᱷᱤᱜ ᱢᱮ"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "ᱱᱤᱡᱮᱨᱟᱠ ᱥᱮᱸᱫᱽᱨᱟ"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "ᱢᱤᱫ ᱴᱟᱝ Firefox ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱚᱲᱟᱜ ᱤᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫ ᱛᱟᱞᱟᱝ ᱢᱮ. ᱣᱤᱡᱮᱴ ᱥᱮᱞᱮᱫ ᱠᱟᱛᱮ ᱡᱚᱴᱮᱫ ᱢᱮ ᱟᱨ ᱚᱛᱟ ᱫᱚᱦᱚᱭ ᱢᱮ ᱥᱟᱯᱲᱟᱣ ᱞᱚᱜᱤᱛ ᱛᱮ ᱟᱨ ᱮᱴᱟᱜ-ᱟ ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱵᱟᱪᱷᱟᱣ ᱛᱟᱞᱟᱝ ᱢᱮ."; +"TodayWidget.QuickActionGalleryDescription" = "ᱢᱤᱫ ᱴᱟᱝ Ecosia ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱚᱲᱟᱜ ᱤᱥᱠᱨᱤᱱ ᱨᱮ ᱥᱮᱞᱮᱫ ᱛᱟᱞᱟᱝ ᱢᱮ. ᱣᱤᱡᱮᱴ ᱥᱮᱞᱮᱫ ᱠᱟᱛᱮ ᱡᱚᱴᱮᱫ ᱢᱮ ᱟᱨ ᱚᱛᱟ ᱫᱚᱦᱚᱭ ᱢᱮ ᱥᱟᱯᱲᱟᱣ ᱞᱚᱜᱤᱛ ᱛᱮ ᱟᱨ ᱮᱴᱟᱜ-ᱟ ᱠᱷᱟᱴᱚᱢᱟᱪᱷᱟ ᱵᱟᱪᱷᱟᱣ ᱛᱟᱞᱟᱝ ᱢᱮ."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "ᱞᱚᱜᱚᱱ ᱛᱮᱭᱟ ᱠᱚᱢᱤ"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox ᱥᱚᱴᱠᱚᱴ"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia ᱥᱚᱴᱠᱚᱴ"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "ᱟᱢᱟᱜ ᱠᱷᱩᱞᱟᱹ ᱟᱠᱟᱱ ᱴᱮᱵᱽ ᱠᱚ ᱨᱮᱭᱟᱜ ᱥᱚᱴᱠᱚᱴ ᱠᱚ ᱥᱮᱞᱮᱫ ᱢᱮ ᱾"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "ᱞᱚᱜᱚᱱ ᱛᱮ ᱧᱮᱞ"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵᱽ ᱨᱮ ᱥᱮᱸᱫᱽᱨᱟᱭ ᱢᱮ"; diff --git a/firefox-ios/Shared/ses.lproj/Localizable.strings b/firefox-ios/Shared/ses.lproj/Localizable.strings index 91516c3eb360d..498880ae37809 100644 --- a/firefox-ios/Shared/ses.lproj/Localizable.strings +++ b/firefox-ios/Shared/ses.lproj/Localizable.strings @@ -179,7 +179,7 @@ "ErrorPages.AdvancedWarning2.Text" = "A ga tee kaŋ woo mana hanse ka boori wala zanbakaw k'a tee. Bisa nda war ga allamaanaa bay."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ koyoo mana interneti nungoo hanse ka boori. Ka ganji war alhabar ma ši zayandi, Firefox mana yadda ka dobu nungoo woo ga."; +"ErrorPages.CertWarning.Description" = "%@ koyoo mana interneti nungoo hanse ka boori. Ka ganji war alhabar ma ši zayandi, Ecosia mana yadda ka dobu nungoo woo ga."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ciyaroo woo ši nda naanay"; @@ -197,13 +197,13 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox šintin moo"; +"Firefox.HomePage.Title" = "Ecosia šintin moo"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Jine"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox kontu"; +"FxA.FirefoxAccount" = "Ecosia kontu"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Kontu nda jinawey juwal"; @@ -215,7 +215,7 @@ "FxA.SyncNow" = "Hangandi sohõ"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync dobu %@ ga"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync dobu %@ ga"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync dobandi"; @@ -224,7 +224,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ dobuyanoo feera ka boori."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Jinaa woo doboo feeri ka hun Firefox Sync ga ka boori."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Jinaa woo doboo feeri ka hun Ecosia Sync ga ka boori."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync hun dobante"; @@ -233,7 +233,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync hun dobante"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Jinay dobante foo hun Firefox Sync ga"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Jinay dobante foo hun Ecosia Sync ga"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Faaba"; @@ -251,7 +251,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Huru ka dii kanjey kaŋ goo war jinay tanawey ga maaŋey."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Taariki"; @@ -362,7 +362,7 @@ "LoginsHelper.Update.Button" = "Taagandi"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "A hima kaŋ Firefox bay ka kaŋ. War ga boona ka kanjey yeeti?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "A hima kaŋ Ecosia bay ka kaŋ. War ga boona ka kanjey yeeti?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Šilbay sand'a cawandi"; @@ -425,7 +425,7 @@ "Okay" = "Ayyo"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Aywa, Firefox kaŋ ya!"; +"Oops! Firefox crashed" = "Aywa, Ecosia kaŋ ya!"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Citaabu biyoo kar nd'a bangay ka hantumey feeri cawkaw gunaroo ra maa zuwoo ra."; @@ -437,7 +437,7 @@ "Open Tabs" = "Kanji feerantey"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ši hin ka moɲoo feeri zama woo šii nda aderesu boryo."; +"OpenURL.Error.Message" = "Ecosia ši hin ka moɲoo feeri zama woo šii nda aderesu boryo."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Ši hin ka moɲoo feeri"; @@ -542,7 +542,7 @@ "Search.ThirdPartyEngines.OK" = "Ayyo"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Kaŋyan bayrandi sanba hala Mozilla ma hin ka layboo hanse?"; +"Send a crash report so Mozilla can fix the problem?" = "Kaŋyan bayrandi sanba hala Ecosia ma hin ka layboo hanse?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Zaabo sanba"; @@ -578,7 +578,7 @@ "SentTab.NoTabArrivingNotification.body" = "Naagu ka šintin"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Guna"; @@ -632,7 +632,7 @@ "Settings.DataManagement.Title" = "Bayhaya juwalyan"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ši war kontoo hangandi koyne, amm'a ši war ceeciyan taariki bayhaya kul tuusu jinaa woo ga."; +"Settings.Disconnect.Body" = "Ecosia ši war kontoo hangandi koyne, amm'a ši war ceeciyan taariki bayhaya kul tuusu jinaa woo ga."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Hun Sync ga"; @@ -668,7 +668,7 @@ "Settings.FxA.Sync.SectionName" = "Sync kayandiyaney"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox kontu"; +"Settings.FxA.Title" = "Ecosia kontu"; /* General settings section title */ "Settings.General.SectionName" = "Hayakul"; @@ -701,10 +701,10 @@ "Settings.NewTab.TopSectionNameFooter" = "Suuba woo kaŋ mma zumandi nda kanji taaga ga feera"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Waati kaŋ Firefox ga feera"; +"Settings.OfferClipboardBar.Status" = "Waati kaŋ Ecosia ga feera"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Waati kaŋ Firefox ga feera"; +"Settings.OfferClipboardBar.StatusV2" = "Waati kaŋ Ecosia ga feera"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Noo dobu berantey se"; @@ -725,7 +725,7 @@ "Settings.SendUsage.Link" = "Bay ka tonton."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ga durkutu ka alhabar de marga kaŋ ir g'a ka Firefox tee k'a boryandi borey kul se."; +"Settings.SendUsage.Message" = "Ecosia ga durkutu ka alhabar de marga kaŋ ir g'a ka Ecosia tee k'a boryandi borey kul se."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Goyyan bayhayey sanba"; @@ -734,7 +734,7 @@ "Settings.Siri.OpenTabShortcut" = "Kanji taaga feeri"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri duurandiyaney ka tanba ka Firefox feeri nda Siri"; +"Settings.Siri.SectionDescription" = "Siri duurandiyaney ka tanba ka Ecosia feeri nda Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri duurandiyaney"; @@ -773,13 +773,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Zumandi bandafaari ra"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Goo ma zumandi Firefox ra"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Goo ma zumandi Ecosia ra"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Feeri Firefox ra"; +"ShareExtension.OpenInFirefoxAction.Title" = "Feeri Ecosia ra"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Ceeci Firefox ra"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ceeci Ecosia ra"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "Ceeciyan honnandiyaney cebe"; @@ -869,7 +869,7 @@ "UIMenuItem.PasteGo" = "Dobu ka koy"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Ceeci nda Firefox"; +"UIMenuItem.SearchWithFirefox" = "Ceeci nda Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "mana cawandi"; @@ -896,7 +896,7 @@ "Yesterday" = "Bi"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "War ši nda kanji feerante kul Firefox ra war jinay taney ga."; +"You don’t have any tabs open in Firefox on your other devices." = "War ši nda kanji feerante kul Ecosia ra war jinay taney ga."; /* Your Rights settings section title */ "Your Rights" = "War alhakey"; diff --git a/firefox-ios/Shared/ses.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ses.lproj/PrivateBrowsing.strings index 77c4ca75c73bb..bd07246278bc0 100644 --- a/firefox-ios/Shared/ses.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ses.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Sutura kanjey daabu"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ši honga taariki wala aleewa kul, amm'a ga doo-šilbay taagey gaabu."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ši honga taariki wala aleewa kul, amm'a ga doo-šilbay taagey gaabu."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Bay ka tonton"; diff --git a/firefox-ios/Shared/si.lproj/Localizable.strings b/firefox-ios/Shared/si.lproj/Localizable.strings index ed2f75e52032f..677c9d853e7e9 100644 --- a/firefox-ios/Shared/si.lproj/Localizable.strings +++ b/firefox-ios/Shared/si.lproj/Localizable.strings @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "රූගතය සමඟ පිවිසෙන්න"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "පරිගණකයෙහි ෆයර්ෆොක්ස් විවෘත කර firefox.com/pair වෙත යන්න"; +"fxa.signin.qr-link-instruction" = "පරිගණකයෙහි ෆයර්ෆොක්ස් විවෘත කර ecosia.org/pair වෙත යන්න"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "පරිලෝකනයට ඔබන්න"; diff --git a/firefox-ios/Shared/sk.lproj/Default Browser.strings b/firefox-ios/Shared/sk.lproj/Default Browser.strings index 0231d3d6939b8..f785558fafcb3 100644 --- a/firefox-ios/Shared/sk.lproj/Default Browser.strings +++ b/firefox-ios/Shared/sk.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Nastavenie Firefoxu ako predvoleného prehliadača je hlasovaním za otvorený a prístupný internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Nastavenie Ecosiau ako predvoleného prehliadača je hlasovaním za otvorený a prístupný internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Predvolene pre lepší internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Ďalšie informácie"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Nastavte si automatické otváranie webových stránok, e‑mailov a správ vo Firefoxe."; +"DefaultBrowserCard.Description" = "Nastavte si automatické otváranie webových stránok, e‑mailov a správ vo Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Vyberte si Firefox ako svoj predvolený prehliadač, aby sa rýchlosť, bezpečnosť a súkromie stali automatickými."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Vyberte si Ecosia ako svoj predvolený prehliadač, aby sa rýchlosť, bezpečnosť a súkromie stali automatickými."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Zvýšte úroveň každodenného prehliadania"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox zablokuje každý mesiac v priemere viac ako 3 000 sledovacích prvkov na používateľa. Nastavte si z nás svoj predvolený prehliadač, aby ste mali súkromie pod kontrolou."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia zablokuje každý mesiac v priemere viac ako 3 000 sledovacích prvkov na používateľa. Nastavte si z nás svoj predvolený prehliadač, aby ste mali súkromie pod kontrolou."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox má súkromie pod kontrolou"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia má súkromie pod kontrolou"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Zmeňte svoj predvolený prehliadač"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Ťuknite na možnosť Predvolený prehliadač"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Vyberte Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Vyberte Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Predvolený prehliadač"; diff --git a/firefox-ios/Shared/sk.lproj/Intro.strings b/firefox-ios/Shared/sk.lproj/Intro.strings index b712c1f7b14c0..c2fcbfb747959 100644 --- a/firefox-ios/Shared/sk.lproj/Intro.strings +++ b/firefox-ios/Shared/sk.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Prihlásiť sa"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Víta vás Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Víta vás Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Začať prehliadanie"; diff --git a/firefox-ios/Shared/sk.lproj/Localizable.strings b/firefox-ios/Shared/sk.lproj/Localizable.strings index b4370cf1d3574..1d002feb2d56d 100644 --- a/firefox-ios/Shared/sk.lproj/Localizable.strings +++ b/firefox-ios/Shared/sk.lproj/Localizable.strings @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Domovská stránka prehliadača Firefox"; +"Firefox.HomePage.Title" = "Domovská stránka prehliadača Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Prispôsobiť domovskú stránku"; @@ -377,7 +377,7 @@ "Forward" = "Dopredu"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Účet Firefox"; +"FxA.FirefoxAccount" = "Účet Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Spravovať účet a zariadenia"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Prihláste sa pomocou fotoaparátu"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Spustite Firefox na svojom počítači a navštívte stránku firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Spustite Ecosia na svojom počítači a navštívte stránku ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pripravené na zosnímanie"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synchronizovať"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Služba Firefox Sync je pripojená k zariadeniu %@"; +"FxAPush_DeviceConnected_body" = "Služba Ecosia Sync je pripojená k zariadeniu %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Služba Sync je pripojená"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Zariadenie %@ bolo úspešne odpojené."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Toto zariadenie bolo úspešne odpojené od služby Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Toto zariadenie bolo úspešne odpojené od služby Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Služba Sync bola odpojená"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Služba Sync bola odpojená"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Zariadenie bolo odpojené od služby Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Zariadenie bolo odpojené od služby Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefoxu, zmena tapety"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosiau, zmena tapety"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomocník"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Prihlásením sa zobrazíte zoznam kariet z vašich ostatných zariadení."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "História"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Ďalšie informácie"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Ak chcete použiť funkciu Automatické dopĺňanie pre Firefox, musíte mať povolený prístupový kód zariadenia."; +"Logins.PasscodeRequirement.Warning" = "Ak chcete použiť funkciu Automatické dopĺňanie pre Ecosia, musíte mať povolený prístupový kód zariadenia."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Vezmite si svoje heslá so sebou"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Automatické dopĺňanie hesiel Firefoxu"; +"Logins.WelcomeView.Title2" = "Automatické dopĺňanie hesiel Ecosiau"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Zapnite Automatické dopĺňanie"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ULOŽENÉ PRIHLASOVACIE ÚDAJE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zdá sa, že Firefox naposledy zlyhal. Chcete obnoviť svoje karty?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Zdá sa, že Ecosia naposledy zlyhal. Chcete obnoviť svoje karty?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Označiť ako prečítané"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Začať prehliadanie"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! Firefox zlyhal"; +"Oops! Firefox crashed" = "Ups! Ecosia zlyhal"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Ťuknutím na ikonu knihy, keď sa objaví v paneli s titulkom, otvoríte stránku v zobrazení Čítačka."; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Toto vám umožní uložiť obrázok v albume Fotoaparát."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox žiada o prístup k vašim fotkám"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia žiada o prístup k vašim fotkám"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zavrieť"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Údaje sú neplatné"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "V zariadení prejdite na ‘Nastavenia’ > ‘Firefox’. Povoľte prehliadaču Firefox prístup k fotoaparátu."; +"ScanQRCode.PermissionError.Message.v100" = "V zariadení prejdite na ‘Nastavenia’ > ‘Ecosia’. Povoľte prehliadaču Ecosia prístup k fotoaparátu."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skenovanie QR kódu"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Prejsť na kartu"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Návrhy Firefoxu"; +"Search.SuggestSectionTitle.v102" = "Návrhy Ecosiau"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Nový vyhľadávací modul sa objaví v paneli rýchleho vyhľadávania."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Odoslať hlásenie o zlyhaní, aby mohla Mozilla opraviť problém?"; +"Send a crash report so Mozilla can fix the problem?" = "Odoslať hlásenie o zlyhaní, aby mohla Ecosia opraviť problém?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Odoslať spätnú väzbu"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Odoslať kartu"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "K svojmu účtu Firefox nemáte pripojené žiadne zariadenia dostupné na synchronizáciu."; +"SendTo.NoDevicesFound.Message" = "K svojmu účtu Ecosia nemáte pripojené žiadne zariadenia dostupné na synchronizáciu."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Spustite Firefox, prejdite do Nastavení a prihláste sa."; +"SendTo.NotSignedIn.Message" = "Spustite Ecosia, prejdite do Nastavení a prihláste sa."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Nie ste prihlásený k svojmu účtu Firefox."; +"SendTo.NotSignedIn.Title" = "Nie ste prihlásený k svojmu účtu Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Odoslať"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Začnite ťuknutím"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Zobraziť"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Správa údajov"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox sa už viac nebude synchronizovať s vašim účtom, ale neodstráni z toho zariadenia žiadne z údajov prehliadania."; +"Settings.Disconnect.Body" = "Ecosia sa už viac nebude synchronizovať s vašim účtom, ale neodstráni z toho zariadenia žiadne z údajov prehliadania."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Odpojiť Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Nastavenia synchronizácie"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Účet Firefox"; +"Settings.FxA.Title" = "Účet Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Všeobecné"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Vyberte, čo sa zobrazí ako domovská stránka."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Vyberte, čo je zahrnuté na domovskej stránke Firefoxu."; +"Settings.Home.Option.Description.v101" = "Vyberte, čo je zahrnuté na domovskej stránke Ecosiau."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Návrat späť"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Domovská stránka"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Vyberte si, čo chcete vidieť, keď sa vrátite do Firefoxu."; +"Settings.Home.Option.StartAtHome.Description" = "Vyberte si, čo chcete vidieť, keď sa vrátite do Ecosiau."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Posledná karta"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Tapeta"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tapeta Firefoxu, vzor ametystu"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Tapeta Ecosiau, vzor ametystu"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tapeta Firefoxu, vzor plážových kopcov"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Tapeta Ecosiau, vzor plážových kopcov"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tapeta Firefoxu, blankytný vzor"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Tapeta Ecosiau, blankytný vzor"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Predvolená čistá tapeta"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tapeta Firefoxu, vzor východu slnka"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Tapeta Ecosiau, vzor východu slnka"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Prepínač automatického prepínania tapety domovskej stránky"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tapeta Firefoxu, vzor Twilight Hills"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Tapeta Ecosiau, vzor Twilight Hills"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasický %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limitovaná edícia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Tapetu domovskej stránky zmeniť ťuknutím na logo Firefoxu"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Tapetu domovskej stránky zmeniť ťuknutím na logo Ecosiau"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Tapeta"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Vlastná"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Domovská stránka Firefoxu"; +"Settings.NewTab.Option.FirefoxHome" = "Domovská stránka Ecosiau"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Zobraziť vašu domovskú stránku"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blokovať obrázky"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Pri otvorení Firefoxu"; +"Settings.OfferClipboardBar.Status" = "Pri otvorení Ecosiau"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Pri otváraní aplikácie %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Pri otvorení Firefoxu"; +"Settings.OfferClipboardBar.StatusV2" = "Pri otvorení Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ponúknuť otvorenie skopírovaných odkazov"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Ďalšie informácie."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla sa usiluje zbierať len údaje, ktoré potrebujeme na vylepšenie Firefoxu pre všetkých."; +"Settings.SendUsage.Message" = "Ecosia sa usiluje zbierať len údaje, ktoré potrebujeme na vylepšenie Ecosiau pre všetkých."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Odosielať údaje o používaní"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Otvoriť novú kartu"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Použite skratku na otvorenie Firefoxu pomocou Siri"; +"Settings.Siri.SectionDescription" = "Použite skratku na otvorenie Ecosiau pomocou Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri - skratky"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Ďalšie informácie"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox môže občas inštalovať a spúšťať štúdie."; +"Settings.Studies.Toggle.Message" = "Ecosia môže občas inštalovať a spúšťať štúdie."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Štúdie"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Načítať na pozadí"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Načítavanie vo Firefoxe"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Načítavanie vo Ecosiae"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Otvoriť vo Firefoxe"; +"ShareExtension.OpenInFirefoxAction.Title" = "Otvoriť vo Ecosiae"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Hľadať vo Firefoxe"; +"ShareExtension.SeachInFirefoxAction.Title" = "Hľadať vo Ecosiae"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopírovať"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Prilepiť a prejsť"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Hľadať vo Firefoxe"; +"UIMenuItem.SearchWithFirefox" = "Hľadať vo Ecosiae"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "neprečítané"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Včera"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Vo Firefoxe na svojich ďalších zariadeniach nemáte otvorené žiadne karty."; +"You don’t have any tabs open in Firefox on your other devices." = "Vo Ecosiae na svojich ďalších zariadeniach nemáte otvorené žiadne karty."; /* Your Rights settings section title */ "Your Rights" = "Vaše práva"; diff --git a/firefox-ios/Shared/sk.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/sk.lproj/PrivateBrowsing.strings index fd761577db671..f5740a915d895 100644 --- a/firefox-ios/Shared/sk.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/sk.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Otvoriť na novej súkromnej karte"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox si nebude pamätať históriu prehliadania či cookies, ale nové záložky ostanú zachované."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia si nebude pamätať históriu prehliadania či cookies, ale nové záložky ostanú zachované."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Ďalšie informácie"; diff --git a/firefox-ios/Shared/sk.lproj/Today.strings b/firefox-ios/Shared/sk.lproj/Today.strings index 703aa8fac82b1..622ca6eba8ea7 100644 --- a/firefox-ios/Shared/sk.lproj/Today.strings +++ b/firefox-ios/Shared/sk.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zavrieť súkromné karty"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Pridajte si skratky Firefoxu na svoju plochu."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Pridajte si skratky Ecosia na svoju plochu."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Prejsť na skopírovaný odkaz"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Žiadne otvorené karty."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Otvoriť Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Otvoriť Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Súkromné vyhľadávanie"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Pridajte si skratku Firefoxu na plochu. Pre úpravu alebo výber inej skratky na widgete podržte prst."; +"TodayWidget.QuickActionGalleryDescription" = "Pridajte si skratku Ecosia na plochu. Pre úpravu alebo výber inej skratky na widgete podržte prst."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Rýchle akcie"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Skratky Firefoxu"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Skratky Ecosiau"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Pridajte si skratky na svoje otvorené karty."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Rýchle zobrazenie"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Hľadať vo Firefoxe"; +"TodayWidget.SearchInFirefoxV2" = "Hľadať vo Ecosiae"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Hľadať v súkromnej karte"; diff --git a/firefox-ios/Shared/sl.lproj/Default Browser.strings b/firefox-ios/Shared/sl.lproj/Default Browser.strings index 8d74c34ed821a..5a9bbdb0df6bb 100644 --- a/firefox-ios/Shared/sl.lproj/Default Browser.strings +++ b/firefox-ios/Shared/sl.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Izbira Firefoxa za privzeti brskalnik je glas za odprt in dostopen internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Izbira Ecosiaa za privzeti brskalnik je glas za odprt in dostopen internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Privzeto za boljši internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Več o tem"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Nastavite, naj se povezave s spletnih strani, iz e-pošte in sporočil odpirajo v Firefoxu."; +"DefaultBrowserCard.Description" = "Nastavite, naj se povezave s spletnih strani, iz e-pošte in sporočil odpirajo v Ecosiau."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Izberite Firefox za privzeti brskalnik, da hitrost, varnost in zasebnost postanejo samoumevne."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Izberite Ecosia za privzeti brskalnik, da hitrost, varnost in zasebnost postanejo samoumevne."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Povzdignite vsakodnevno brskanje"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox vsak mesec zavrne povprečno več kot 3000 sledilcev na uporabnika. Nastavite nas kot privzeti brskalnik za brezskrbno zasebnost."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia vsak mesec zavrne povprečno več kot 3000 sledilcev na uporabnika. Nastavite nas kot privzeti brskalnik za brezskrbno zasebnost."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox varuje zasebnost"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia varuje zasebnost"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Zamenjajte privzeti brskalnik"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tapnite \"Default Browser App\""; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Izberite Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Izberite Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Privzet brskalnik"; diff --git a/firefox-ios/Shared/sl.lproj/Intro.strings b/firefox-ios/Shared/sl.lproj/Intro.strings index 68be54e00d6c5..8428d45534111 100644 --- a/firefox-ios/Shared/sl.lproj/Intro.strings +++ b/firefox-ios/Shared/sl.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Prijava"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Dobrodošli v Firefoxu"; +"Intro.Slides.Welcome.Title.v2" = "Dobrodošli v Ecosiau"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Začnite z brskanjem"; diff --git a/firefox-ios/Shared/sl.lproj/Localizable.strings b/firefox-ios/Shared/sl.lproj/Localizable.strings index 0bceeb532d43d..9644e35c7ff4e 100644 --- a/firefox-ios/Shared/sl.lproj/Localizable.strings +++ b/firefox-ios/Shared/sl.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Lahko gre za napačne nastavitve ali poseg napadalca. Nadaljujte, če ste pripravljeni tvegati."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Lastnik %@ je nepravilno nastavil svojo spletno stran. Firefox se ni povezal nanjo, da bi preprečil krajo vaših podatkov."; +"ErrorPages.CertWarning.Description" = "Lastnik %@ je nepravilno nastavil svojo spletno stran. Ecosia se ni povezal nanjo, da bi preprečil krajo vaših podatkov."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Vaša povezava ni zasebna"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Domača stran Firefoxa"; +"Firefox.HomePage.Title" = "Domača stran Ecosiaa"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Prilagodi domačo stran"; @@ -377,7 +377,7 @@ "Forward" = "Naprej"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Račun"; +"FxA.FirefoxAccount" = "Ecosia Račun"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Upravljanje računa in naprav"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Prijavite se s kamero"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Na računalniku odprite Firefox in obiščite firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Na računalniku odprite Ecosia in obiščite ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Pripravljeno za skeniranje"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Sinhroniziraj zdaj"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync se je povezal z %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync se je povezal z %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync je povezan"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "Naprava %@ je bila uspešno odklopljena."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ta naprava ni več povezana s Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ta naprava ni več povezana s Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync je prekinjen"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync je prekinjen"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Naprava je bila odklopljena iz Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Naprava je bila odklopljena iz Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logtip Firefoxa, spremeni ozadje."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logtip Ecosiaa, spremeni ozadje."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pomoč"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Prijavite se za ogled seznama zavihkov drugih naprav."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Zgodovina"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Več o tem"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Za uporabo samodejnega izpolnjevanja v Firefoxu mora biti omogočeno geslo naprave."; +"Logins.PasscodeRequirement.Warning" = "Za uporabo samodejnega izpolnjevanja v Ecosiau mora biti omogočeno geslo naprave."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Vzemite gesla s seboj"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Samodejno izpolnjevanje gesel za Firefox"; +"Logins.WelcomeView.Title2" = "Samodejno izpolnjevanje gesel za Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Vklopi samodejno izpolnjevanje"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SHRANJENE PRIJAVE"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Očitno se je Firefox predhodno sesul. Želite obnoviti zavihke?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Očitno se je Ecosia predhodno sesul. Želite obnoviti zavihke?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Označi kot prebrano"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Začnite z brskanjem"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Ups! Firefox se je sesul"; +"Oops! Firefox crashed" = "Ups! Ecosia se je sesul"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Odprite članke v bralnem pogledu, tako da tapnete ikono knjige, ko se pojavi v naslovni vrstici."; @@ -905,7 +905,7 @@ "Open Tabs" = "Odprte zavihke"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ne more odpreti strani, ker je njen naslov neveljaven."; +"OpenURL.Error.Message" = "Ecosia ne more odpreti strani, ker je njen naslov neveljaven."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Strani ni mogoče odpreti"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "To vam omogoča, da shranite sliko v Camera Roll."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox želi dostop do vaših slik"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia želi dostop do vaših slik"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Zapri"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Podatek je neveljaven"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Izberite \"Nastavitve\" > \"Firefox\". Dajte Firefoxu dovoljenje za dostop do kamere."; +"ScanQRCode.PermissionError.Message.v100" = "Izberite \"Nastavitve\" > \"Ecosia\". Dajte Ecosiau dovoljenje za dostop do kamere."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skenirajte QR-kodo"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Preklopi na zavihek"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefoxovi predlogi"; +"Search.SuggestSectionTitle.v102" = "Ecosiaovi predlogi"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Novi isklanik se bo pojavil v vrstici za hitro iskanje."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "V redu"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Pošljem poročilo, da lahko Mozilla odpravi napako?"; +"Send a crash report so Mozilla can fix the problem?" = "Pošljem poročilo, da lahko Ecosia odpravi napako?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Pošlji povratne informacije"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Pošlji zavihek"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "S tem Firefox Računom nimate povezanih drugih naprav na voljo za sinhronizacijo."; +"SendTo.NoDevicesFound.Message" = "S tem Ecosia Računom nimate povezanih drugih naprav na voljo za sinhronizacijo."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Odprite Firefox, pojdite v Nastavitve in se prijavite za nadaljevanje."; +"SendTo.NotSignedIn.Message" = "Odprite Ecosia, pojdite v Nastavitve in se prijavite za nadaljevanje."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Niste prijavljeni v vaš Firefox Račun."; +"SendTo.NotSignedIn.Title" = "Niste prijavljeni v vaš Ecosia Račun."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Pošlji"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tapnite za začetek"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Prikaži"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Upravljanje s podatki"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox se bo prenehal sinhronizirati z vašim računom, vendar ne bo izbrisal podatkov o brskanju na tej napravi."; +"Settings.Disconnect.Body" = "Ecosia se bo prenehal sinhronizirati z vašim računom, vendar ne bo izbrisal podatkov o brskanju na tej napravi."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Prekini povezavo"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Nastavitve Synca"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Račun"; +"Settings.FxA.Title" = "Ecosia Račun"; /* General settings section title */ "Settings.General.SectionName" = "Splošno"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Izberite, kaj se prikaže kot domača stran."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Izberite vsebino, ki bo prikazana na Firefoxovi domači strani."; +"Settings.Home.Option.Description.v101" = "Izberite vsebino, ki bo prikazana na Ecosiaovi domači strani."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Skoči nazaj"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Domača stran"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Izberite, kaj naj se prikaže, ko se vrnete v Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Izberite, kaj naj se prikaže, ko se vrnete v Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Zadnji zavihek"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Ozadje"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ozadje za Firefox, vzorec ametista."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ozadje za Ecosia, vzorec ametista."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ozadje za Firefox, vzorec peščenih hribčkov."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ozadje za Ecosia, vzorec peščenih hribčkov."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ozadje za Firefox, vzorec nebeške modrine."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ozadje za Ecosia, vzorec nebeške modrine."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Privzeto prazno ozadje."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ozadje za Firefox, vzorec sončnega vzhoda."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ozadje za Ecosia, vzorec sončnega vzhoda."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Preklop na naslednje ozadje domače strani"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ozadje za Firefox, vzorec hribov v mraku."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ozadje za Ecosia, vzorec hribov v mraku."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasični %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Omejena izdaja"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Spremenite ozadje z dotikom logotipa domače strani Firefoxa"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Spremenite ozadje z dotikom logotipa domače strani Ecosiaa"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Ozadje"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Po meri"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Domača stran Firefoxa"; +"Settings.NewTab.Option.FirefoxHome" = "Domača stran Ecosiaa"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Domača stran"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Zavračaj slike"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Ko odprete Firefox"; +"Settings.OfferClipboardBar.Status" = "Ko odprete Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Ko odprete %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Ko odprete Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Ko odprete Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ponudi odpiranje kopiranih povezav"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Več o tem."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla se trudi, da zbira samo podatke, ki jih potrebuje za razvoj in izboljševanje Firefoxa."; +"Settings.SendUsage.Message" = "Ecosia se trudi, da zbira samo podatke, ki jih potrebuje za razvoj in izboljševanje Ecosiaa."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Pošiljaj podatke o uporabi"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Odpri nov zavihek"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Uporabi bližnjice za hitro odpiranje Firefoxa s Siri"; +"Settings.Siri.SectionDescription" = "Uporabi bližnjice za hitro odpiranje Ecosiaa s Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Bližnjice Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Več o tem."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox lahko občasno namesti in zažene raziskave."; +"Settings.Studies.Toggle.Message" = "Ecosia lahko občasno namesti in zažene raziskave."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Raziskave"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Naloži v ozadju"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Nalaganje v Firefoxu"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Nalaganje v Ecosiau"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Odpri v Firefoxu"; +"ShareExtension.OpenInFirefoxAction.Title" = "Odpri v Ecosiau"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Išči v Firefoxu"; +"ShareExtension.SeachInFirefoxAction.Title" = "Išči v Ecosiau"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiraj"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Prilepi in odpri"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Iskanje s Firefoxom"; +"UIMenuItem.SearchWithFirefox" = "Iskanje s Ecosiaom"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "neprebrano"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Včeraj"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "V Firefoxu na drugih napravah nimate odprtih drugih zavihkov."; +"You don’t have any tabs open in Firefox on your other devices." = "V Ecosiau na drugih napravah nimate odprtih drugih zavihkov."; /* Your Rights settings section title */ "Your Rights" = "Vaše pravice"; diff --git a/firefox-ios/Shared/sl.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/sl.lproj/PrivateBrowsing.strings index 7374b30d5c4b7..fcdbb220010a8 100644 --- a/firefox-ios/Shared/sl.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/sl.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Odpri v zasebnem zavihku"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox ne bo hranil vaše zgodovine in piškotkov, novi zaznamki pa bodo shranjeni."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia ne bo hranil vaše zgodovine in piškotkov, novi zaznamki pa bodo shranjeni."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Več o tem"; diff --git a/firefox-ios/Shared/sl.lproj/Today.strings b/firefox-ios/Shared/sl.lproj/Today.strings index e0e12b1b724ac..7ab61ed1a2201 100644 --- a/firefox-ios/Shared/sl.lproj/Today.strings +++ b/firefox-ios/Shared/sl.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Zapri zasebne zavihke"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodajte Firefoxove bližnjice na domač zaslon."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Dodajte Ecosiaove bližnjice na domač zaslon."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Pojdi na kopirano povezavo"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ni odprtih zavihkov."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Odpri Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Odpri Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Zasebno iskanje"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Dodajte bližnjico do Firefoxa na domač zaslon. Po dodajanju pripomočka se ga dotaknite in ga zadržite, da ga uredite in izberete drugo bližnjico."; +"TodayWidget.QuickActionGalleryDescription" = "Dodajte bližnjico do Ecosiaa na domač zaslon. Po dodajanju pripomočka se ga dotaknite in ga zadržite, da ga uredite in izberete drugo bližnjico."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Hitra dejanja"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Bližnjice za Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Bližnjice za Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Dodajte bližnjice do svojih odprtih zavihkov."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Hitri pogled"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Išči v Firefoxu"; +"TodayWidget.SearchInFirefoxV2" = "Išči v Ecosiau"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Išči v zasebnem zavihku"; diff --git a/firefox-ios/Shared/sq.lproj/Default Browser.strings b/firefox-ios/Shared/sq.lproj/Default Browser.strings index 24aaef79e5e1e..c9eecd95434bb 100644 --- a/firefox-ios/Shared/sq.lproj/Default Browser.strings +++ b/firefox-ios/Shared/sq.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Bërja e Firefox-it shfletuesin tuaj parazgjedhje është një votë për një internet të hapët, të përdorshëm."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Bërja e Ecosia-it shfletuesin tuaj parazgjedhje është një votë për një internet të hapët, të përdorshëm."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Kaloni në një Internet Më të Mirë"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Mësoni Se Si"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Caktoni lidhje prej sajtesh, email-esh dhe Mesazhe për hapje automatike në Firefox."; +"DefaultBrowserCard.Description" = "Caktoni lidhje prej sajtesh, email-esh dhe Mesazhe për hapje automatike në Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Zgjidhni Firefox-in si shfletuesin tuaj parazgjedhje, për t’i bërë të vetvetishme shpejtësinë, sigurinë dhe privatësinë."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Zgjidhni Ecosia-in si shfletuesin tuaj parazgjedhje, për t’i bërë të vetvetishme shpejtësinë, sigurinë dhe privatësinë."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Ngrijeni Më Lart Shfletimin e Përditshëm"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox-i bllokon mesatarisht mbi 3000 gjurmues për përdorues në muaj. Bëjeni shfletuesin tuaj parazgjedhje, për privatësi dhe mendje të fjetur."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia-i bllokon mesatarisht mbi 3000 gjurmues për përdorues në muaj. Bëjeni shfletuesin tuaj parazgjedhje, për privatësi dhe mendje të fjetur."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox-i e Ka të Mbuluar Privatësinë"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia-i e Ka të Mbuluar Privatësinë"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Këmbeni Shfletuesin Tuaj Parazgjedhje"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Prekni Aplikacionin Shfletues Parazgjedhje"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Përzgjidhni Firefox-in"; +"DefaultBrowserOnboarding.Description3" = "3. Përzgjidhni Ecosia-in"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplikacioni Shfletues Parazgjedhje"; diff --git a/firefox-ios/Shared/sq.lproj/Intro.strings b/firefox-ios/Shared/sq.lproj/Intro.strings index 1acf82f999f58..f31603d8aad06 100644 --- a/firefox-ios/Shared/sq.lproj/Intro.strings +++ b/firefox-ios/Shared/sq.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Hyni"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Mirë se vini në Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Mirë se vini në Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Filloni të Shfletoni"; diff --git a/firefox-ios/Shared/sq.lproj/Localizable.strings b/firefox-ios/Shared/sq.lproj/Localizable.strings index 6fca2829f801f..876c3edbb9272 100644 --- a/firefox-ios/Shared/sq.lproj/Localizable.strings +++ b/firefox-ios/Shared/sq.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Mund të jetë një keqformësim ose futje hundësh nga dikush që po sulmon. Vazhdoni, nëse e pranoni rrezikun potencial."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "I zoti i %@ e ka formësuar keq sajtin e tij. Që të mbrohen nga vjedhjet të dhënat tuaja, Firefox-i nuk u lidh me këtë sajt."; +"ErrorPages.CertWarning.Description" = "I zoti i %@ e ka formësuar keq sajtin e tij. Që të mbrohen nga vjedhjet të dhënat tuaja, Ecosia-i nuk u lidh me këtë sajt."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Kjo Lidhje Nuk Besohet"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Faqja Hyrëse e Firefox-it"; +"Firefox.HomePage.Title" = "Faqja Hyrëse e Ecosia-it"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Përshtatni Faqe Hyrëse"; @@ -377,7 +377,7 @@ "Forward" = "Përpara"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Llogari Firefox"; +"FxA.FirefoxAccount" = "Llogari Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Administroni Llogari & Pajisje"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Hyni Me Kamerën Tuaj"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Te kompjuteri juaj, hapni Firefox-in dhe kaloni te firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Te kompjuteri juaj, hapni Ecosia-in dhe kaloni te ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Gati për Skanim"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Njëkohësoji Tani"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync është lidhur me %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync është lidhur me %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Lidhur Me Sync"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ u shkëput me sukses."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Kjo pajisje është shkëputur me sukses nga shërbimi Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Kjo pajisje është shkëputur me sukses nga shërbimi Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Me Sync të Shkëputur"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Me Sync të Shkëputur"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Është shkëputur një pajisje nga Firefox Sync-u"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Është shkëputur një pajisje nga Ecosia Sync-u"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Stemë Firefox-i, ndryshoni sfondin."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Stemë Ecosia-i, ndryshoni sfondin."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ndihmë"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Bëni hyrjen që të shihni një listë skedash nga pajisjet tuaja të tjera."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historik"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Mësoni Më Tepër"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Që të përdorni veçorinë Vetëplotësim për Firefox, duhet të keni të aktivizuar një kodkalim pajisjeje."; +"Logins.PasscodeRequirement.Warning" = "Që të përdorni veçorinë Vetëplotësim për Ecosia, duhet të keni të aktivizuar një kodkalim pajisjeje."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Merrini fjalëkalimet tuaja kudo"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Vetëplotësim Fjalëkalimesh Firefox-i"; +"Logins.WelcomeView.Title2" = "Vetëplotësim Fjalëkalimesh Ecosia-i"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Aktivizo Vetëplotësim"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "KREDENCIALE HYRJESH TË RUAJTURA"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Duket se Firefox-i, më herët, u vithis. Do të donit të rikthehen skedat tuaja?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Duket se Ecosia-i, më herët, u vithis. Do të donit të rikthehen skedat tuaja?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Shënoje si të Lexuar"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Filloni të Shfletoni"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hëm! Firefox-i u vithis"; +"Oops! Firefox crashed" = "Hëm! Ecosia-i u vithis"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Artikujt nën Pamjen Lexues i hapni duke prekur ikonën libër, kur shfaqet te shtylla e titullit."; @@ -905,7 +905,7 @@ "Open Tabs" = "Hapi Skedat"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox-i s’e hap dot faqen, ngaqë ajo ka një adresë të pavlefshme."; +"OpenURL.Error.Message" = "Ecosia-i s’e hap dot faqen, ngaqë ajo ka një adresë të pavlefshme."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "S’hap Dot Faqen"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Kjo ju lejon të ruani figurën te Camera Roll juaj."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox-i do të donte të hynte te Fotot tuaja"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia-i do të donte të hynte te Fotot tuaja"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Mbylle"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Të dhënat janë të pavlefshme"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Kaloni te ‘Rregullime’ pajisjeje > ‘Firefox’. Lejoni Firefox-in të përdorë kamerën."; +"ScanQRCode.PermissionError.Message.v100" = "Kaloni te ‘Rregullime’ pajisjeje > ‘Ecosia’. Lejoni Ecosia-in të përdorë kamerën."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skanonin Kodin QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Kalo te skeda"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox-i Sugjeron"; +"Search.SuggestSectionTitle.v102" = "Ecosia-i Sugjeron"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Motori i ri i kërkimeve do të shfaqet te shtylla e kërkimeve të shpejta."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Të dërgohet një raport vithisjeje, që kështu Mozilla të mund të ndreqë problemin?"; +"Send a crash report so Mozilla can fix the problem?" = "Të dërgohet një raport vithisjeje, që kështu Ecosia të mund të ndreqë problemin?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Dërgoni Përshtypjet"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Dërgo Skedë"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "S’keni ndonjë pajisje tjetër të lidhur te kjo Llogari Firefox-i të gatshme për njëkohësim."; +"SendTo.NoDevicesFound.Message" = "S’keni ndonjë pajisje tjetër të lidhur te kjo Llogari Ecosia-i të gatshme për njëkohësim."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Ju lutemi, që të vazhdohet, hapni Firefox-in, shkoni te Rregullimet dhe bëni hyrjen."; +"SendTo.NotSignedIn.Message" = "Ju lutemi, që të vazhdohet, hapni Ecosia-in, shkoni te Rregullimet dhe bëni hyrjen."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "S’keni bërë hyrjen te Llogaria juaj Firefox."; +"SendTo.NotSignedIn.Title" = "S’keni bërë hyrjen te Llogaria juaj Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Dërgoje"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Prekeni që të fillohet"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Shiheni"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Administrim të Dhënash"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox-i do të reshtë së njëkohësuari me llogarinë tuaj, por nuk do të fshihet ndonjë nga të dhënat e shfletimeve tuaja në këtë pajisje."; +"Settings.Disconnect.Body" = "Ecosia-i do të reshtë së njëkohësuari me llogarinë tuaj, por nuk do të fshihet ndonjë nga të dhënat e shfletimeve tuaja në këtë pajisje."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Shkëputeni Sync-un"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Rregullime Njëkohësimesh"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Llogari Firefox"; +"Settings.FxA.Title" = "Llogari Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Të përgjithshme"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Zgjidhni ç’të shfaqe te faqja hyrëse."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Zgjidhni ç’të përfshihet te faqja hyrëse e Firefox-it."; +"Settings.Home.Option.Description.v101" = "Zgjidhni ç’të përfshihet te faqja hyrëse e Ecosia-it."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Hidhu Mbrapa Te"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Kreu"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Zgjidhni çtë shihni, kur ktheheni te Firefox-i."; +"Settings.Home.Option.StartAtHome.Description" = "Zgjidhni çtë shihni, kur ktheheni te Ecosia-i."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Skedën e fundit"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Sfond"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Sfond Firefox-i, mostër ametisti."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Sfond Ecosia-i, mostër ametisti."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Sfond Firefox-i, model me duna plazhi."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Sfond Ecosia-i, model me duna plazhi."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Sfond Firefox-i, mostër në blu të thellë."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Sfond Ecosia-i, mostër në blu të thellë."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Sfond parazgjedhje i qëruar."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Sfond Firefox-i, mostër agu."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Sfond Ecosia-i, mostër agu."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Aktivizim/Çaktivizim cikli sfondesh faqeje hyrëse"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Sfond Firefox-i, model me kodra në muzg."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Sfond Ecosia-i, model me kodra në muzg."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ Klasik"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Edicion i Kufizuar"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ndryshoni sfond duke prekur stemë faqeje hyrëse Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ndryshoni sfond duke prekur stemë faqeje hyrëse Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Sfond"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Vetjake"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Kreun e Firefox-it"; +"Settings.NewTab.Option.FirefoxHome" = "Kreun e Ecosia-it"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Kreu"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blloko Figura"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Kur Hapet Firefox-i"; +"Settings.OfferClipboardBar.Status" = "Kur Hapet Ecosia-i"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Kur hapet %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Kur Hapet Firefox-i"; +"Settings.OfferClipboardBar.StatusV2" = "Kur Hapet Ecosia-i"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Ofro Hapje Lidhjesh të Kopjuara"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Mësoni Më Tepër."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla rreket të grumbullojë vetëm sa na duhen për të ofruar dhe përmirësuar Firefox-in për këdo."; +"Settings.SendUsage.Message" = "Ecosia rreket të grumbullojë vetëm sa na duhen për të ofruar dhe përmirësuar Ecosia-in për këdo."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Dërgo të Dhëna Përdorimi"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Hap Skedë të Re"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Përdorni shkurtore Siri për ta hapur shpejt e shpejt Firefox-in përmes Siri-t"; +"Settings.Siri.SectionDescription" = "Përdorni shkurtore Siri për ta hapur shpejt e shpejt Ecosia-in përmes Siri-t"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Shkurtore Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Mësoni Më Tepër."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox-i mund të instalojë dhe zhvillojë studime herë pas here."; +"Settings.Studies.Toggle.Message" = "Ecosia-i mund të instalojë dhe zhvillojë studime herë pas here."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studime"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Ngarkoje në Prapaskenë"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Po ngarkohet në Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Po ngarkohet në Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Hape në Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Hape në Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Kërko në Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Kërko në Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopjoje"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Ngjite & Shko"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Kërko me Firefox"; +"UIMenuItem.SearchWithFirefox" = "Kërko me Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "të palexuara"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Dje"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "S’keni ndonjë skedë të hapur te Firefox-i në pajisje tuajat të tjera."; +"You don’t have any tabs open in Firefox on your other devices." = "S’keni ndonjë skedë të hapur te Ecosia-i në pajisje tuajat të tjera."; /* Your Rights settings section title */ "Your Rights" = "Të drejtat Tuaja"; diff --git a/firefox-ios/Shared/sq.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/sq.lproj/PrivateBrowsing.strings index b4faeba0d489c..6515bae0f33b0 100644 --- a/firefox-ios/Shared/sq.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/sq.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Hape në Skedë të Re Private"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox-i s’do të mbajë mend gjë nga historiku apo cookie-t, por faqerojtësit e rinj do të ruhen."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia-i s’do të mbajë mend gjë nga historiku apo cookie-t, por faqerojtësit e rinj do të ruhen."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Mësoni Më Tepër"; diff --git a/firefox-ios/Shared/sq.lproj/Today.strings b/firefox-ios/Shared/sq.lproj/Today.strings index 214a636a806b2..75ce44c5d97f4 100644 --- a/firefox-ios/Shared/sq.lproj/Today.strings +++ b/firefox-ios/Shared/sq.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Mbyll Skedat Private"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Shtoni te skena juaj e Kreut shkurtore Firefox."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Shtoni te skena juaj e Kreut shkurtore Ecosia."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Shko te lidhja e kopjuar"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "S’ka skeda të hapura."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Hap Firefox-in"; +"TodayWidget.OpenFirefoxLabel" = "Hap Ecosia-in"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Kërkim Privat"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Shtoni një shkurtore Firefox për te skena juaj Kreu. Pasi të shtohet widget-i, prekeni dhe mbajeni të prekur, që ta përpunoni dhe të përzgjidhni një tjetër shkurtore."; +"TodayWidget.QuickActionGalleryDescription" = "Shtoni një shkurtore Ecosia për te skena juaj Kreu. Pasi të shtohet widget-i, prekeni dhe mbajeni të prekur, që ta përpunoni dhe të përzgjidhni një tjetër shkurtore."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Veprime të Shpejta"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Shkurtore Firefox-i"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Shkurtore Ecosia-i"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Shtoni shkurtore për te skedat tuaja të hapura."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Parje e Shpejtë"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Kërko në Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Kërko në Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Kërkoni në Skedë Private"; diff --git a/firefox-ios/Shared/su.lproj/Default Browser.strings b/firefox-ios/Shared/su.lproj/Default Browser.strings index bc66b4ca561db..6c52f9ac23359 100644 --- a/firefox-ios/Shared/su.lproj/Default Browser.strings +++ b/firefox-ios/Shared/su.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Ngajadikeun Firefox panyungsi baku téh minangka pangrojong kana internét anu nembrak & kahontal."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ngajadikeun Ecosia panyungsi baku téh minangka pangrojong kana internét anu nembrak & kahontal."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Baku pikeun Internét anu Leuwih Hadé"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Lenyepan Carana"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Setél tutumbu ti raramatloka, surélék, jeung Surat pikeun muka otomatis dina Firefox."; +"DefaultBrowserCard.Description" = "Setél tutumbu ti raramatloka, surélék, jeung Surat pikeun muka otomatis dina Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Pilih Firefox salaku panyungsi baku pikeun ngarah otomatis gancang, aman, jeung pripasi."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Pilih Ecosia salaku panyungsi baku pikeun ngarah otomatis gancang, aman, jeung pripasi."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Ronjatkeun Nyungsi Sapopoé"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox meungpeuk rata-rata 3,000+ palacak unggal pamaké unggal bulan. Jadikeun kami panyungsi baku anjeun pikeun katengtreman pripasi."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia meungpeuk rata-rata 3,000+ palacak unggal pamaké unggal bulan. Jadikeun kami panyungsi baku anjeun pikeun katengtreman pripasi."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox Ngajaga Pripasi"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia Ngajaga Pripasi"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Alihkeun Panyungsi Baku Anjeun"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Toél Aplikasi Panyungsi Baku"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Pilih Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Pilih Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Aplikasi Panyungsi Baku"; diff --git a/firefox-ios/Shared/su.lproj/Intro.strings b/firefox-ios/Shared/su.lproj/Intro.strings index 0cc9afb81a8fb..4d9030d71297e 100644 --- a/firefox-ios/Shared/su.lproj/Intro.strings +++ b/firefox-ios/Shared/su.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Asup"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Wilujeng sumping di Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Wilujeng sumping di Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Mitembeyan Ngalanglang"; diff --git a/firefox-ios/Shared/su.lproj/Localizable.strings b/firefox-ios/Shared/su.lproj/Localizable.strings index fd445c43a12df..43204dd7f975c 100644 --- a/firefox-ios/Shared/su.lproj/Localizable.strings +++ b/firefox-ios/Shared/su.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Ieu téh meureun salah setélan atawa diruksak ku panarajang. Tuluykeun wé mun anjeun narima kana poténsi résikona."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Pamilik %@ nyetél raramatlokana teu mantes. Pikeun ngilungan sangkan émbaran anjeun teu dipaling, Firefox henteu nyambungkeun ka ieu raramatloka."; +"ErrorPages.CertWarning.Description" = "Pamilik %@ nyetél raramatlokana teu mantes. Pikeun ngilungan sangkan émbaran anjeun teu dipaling, Ecosia henteu nyambungkeun ka ieu raramatloka."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ieu Sambungan Teu Kapercaya"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Kaca Tepas Firefox"; +"Firefox.HomePage.Title" = "Kaca Tepas Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Kustomkeun Tepas"; @@ -377,7 +377,7 @@ "Forward" = "Maju"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Akun Firefox"; +"FxA.FirefoxAccount" = "Akun Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Atur Akun & Parangkat"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Asup Maké Kaméra Anjeun"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Dina komputer anjeun buka Firefox terus buka firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Dina komputer anjeun buka Ecosia terus buka ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Siap Mindai"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Singkronkeun Ayeuna"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Singkronan Firefox geus nyambung ka %@"; +"FxAPush_DeviceConnected_body" = "Singkronan Ecosia geus nyambung ka %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Singkronan Nyambung"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ geus laksana dipegatkeun."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ieu parangkat geus laksana dipegatkeun ti Singkronan Firefox"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ieu parangkat geus laksana dipegatkeun ti Singkronan Ecosia"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Singkronan Pegat"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Singkronan Pegat"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Parangkat geus dipegarkeun tina Singkronan Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Parangkat geus dipegarkeun tina Singkronan Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, ganti latar."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, ganti latar."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Pitulung"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Asup log pikeun nénjo daptar tab ti parangkat anjeun nu séjén."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Singkronan Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Singkronan Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Jujutan"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Lenyepan"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Pikeun maké pitur AutoFill dina Firefox, anjeun kudu ngaktipkeun kode konci perangkat."; +"Logins.PasscodeRequirement.Warning" = "Pikeun maké pitur AutoFill dina Ecosia, anjeun kudu ngaktipkeun kode konci perangkat."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Bawa kecap sandi anjeun kamanana"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "AutoFill Kecap Konci Firefox"; +"Logins.WelcomeView.Title2" = "AutoFill Kecap Konci Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Aktipkeun AutoFill"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ASUP LOG DITEUNDEUN"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Kawasna mah Firefox geus ruksak saméméhna. Anjeun rék malikkeun deui tabna?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Kawasna mah Ecosia geus ruksak saméméhna. Anjeun rék malikkeun deui tabna?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Cirian geus Dibaca"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Mitembeyan Ngalanglang"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Euleuh! Firefoxna ruksak ning"; +"Oops! Firefox crashed" = "Euleuh! Ecosiana ruksak ning"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Buka artikel di Pidangan Baca ku cara noél ikon buku nalika éta bijil di wilah judul."; @@ -905,7 +905,7 @@ "Open Tabs" = "Buka Tab"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox teu bisa muka ieu kaca lantaran alamatna henteu sohéh."; +"OpenURL.Error.Message" = "Ecosia teu bisa muka ieu kaca lantaran alamatna henteu sohéh."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Teu Bisa Muka Kaca"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Ku ieu anjeun bisa neundeun gambar kana Camera Roll anjeun."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox hayang ngaksés Poto anjeun"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia hayang ngaksés Poto anjeun"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Tutup"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Data henteu sohéh"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Buka ‘Setélan’ > ‘Firefox’. Idinan Firefox ngaksés kaméra."; +"ScanQRCode.PermissionError.Message.v100" = "Buka ‘Setélan’ > ‘Ecosia’. Idinan Ecosia ngaksés kaméra."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Pindéy kodeu QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Pindah ka tab"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Mesin pamaluruhan anyar bakal bijil dina wilah pamaluruhan gancang."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OKÉH"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Kirim laporan karuksakan sangkan Mozilla bisa ngoméan masalahna?"; +"Send a crash report so Mozilla can fix the problem?" = "Kirim laporan karuksakan sangkan Ecosia bisa ngoméan masalahna?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Kirim Pangdeudeul"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Kirim Tab"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Anjeun teu boga parangkat séjén anu nyambung kana Akun Firefox ieu anu bisa disaluyukeun."; +"SendTo.NoDevicesFound.Message" = "Anjeun teu boga parangkat séjén anu nyambung kana Akun Ecosia ieu anu bisa disaluyukeun."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Mangga buka Firefox, buka Setélan sarta asup pikeun neruskeun."; +"SendTo.NotSignedIn.Message" = "Mangga buka Ecosia, buka Setélan sarta asup pikeun neruskeun."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Anjeun can asup ka Akun Firefox."; +"SendTo.NotSignedIn.Title" = "Anjeun can asup ka Akun Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Kirim"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Toél jang ngamimitian"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Singkronisasi Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Singkronisasi Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Témbong"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Manajemén Data"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox bakal eureun nyingkronkeun jeung akun anjeun, ngan moal mupus sakur data langlangan anjeun dina ieu parangkat."; +"Settings.Disconnect.Body" = "Ecosia bakal eureun nyingkronkeun jeung akun anjeun, ngan moal mupus sakur data langlangan anjeun dina ieu parangkat."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Pegatkeun Singkronan"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Setélan Singkronan"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Akun Firefox"; +"Settings.FxA.Title" = "Akun Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Umum"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Pilih naon anu némbogan salaku kaca tepas."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Pilih naon anu diasupkeun dina kaca tepas Firefox."; +"Settings.Home.Option.Description.v101" = "Pilih naon anu diasupkeun dina kaca tepas Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Asup Deui"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Tepas"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Pilih naon anu katempo nalika balik deui ka Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Pilih naon anu katempo nalika balik deui ka Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Tab panungtung"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Latar"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Latar Firefox, pola kucubung."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Latar Ecosia, pola kucubung."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Latar Firefox, pola pasir basisir."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Latar Ecosia, pola pasir basisir."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Latar Firefox, pola biru langit."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Latar Ecosia, pola biru langit."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Latar polos baku."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Latar Firefox, pola balébat."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Latar Ecosia, pola balébat."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Atur daur latar tepas"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Latar Firefox, pola pasir layung."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Latar Ecosia, pola pasir layung."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ klasik"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Édisi Diwates"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Robah latar ku cara noél logo tepas Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Robah latar ku cara noél logo tepas Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Latar"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Sakahayang"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Tepas Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Tepas Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Tepas"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Peungpeuk Gambar"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Nalika Muka Firefox"; +"Settings.OfferClipboardBar.Status" = "Nalika Muka Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Nalika muka %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Nalika Muka Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Nalika Muka Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Tawaran Muka Tutumbu Tironan"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Lenyepan."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla bakal kukumpul data nu diperlukeun hungkul jang nyadiakeun jeung ngoméan Firefox pikeun balaréa."; +"Settings.SendUsage.Message" = "Ecosia bakal kukumpul data nu diperlukeun hungkul jang nyadiakeun jeung ngoméan Ecosia pikeun balaréa."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Kirim Data Pamakéan"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Buka Tab Anyar"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Paké tarabas Siri pikeun gancang muka Firefox tina Siri"; +"Settings.Siri.SectionDescription" = "Paké tarabas Siri pikeun gancang muka Ecosia tina Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Tarabas Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Lenyepan."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox bisa masang jeung ngajalankeun studi iraha baé."; +"Settings.Studies.Toggle.Message" = "Ecosia bisa masang jeung ngajalankeun studi iraha baé."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studi"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Muat di Kasangtukang"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Ngamuat di Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ngamuat di Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Buka di Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Buka di Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Paluruh di Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Paluruh di Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Tiron"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Terap & Indit"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Paluruh ku Firefox"; +"UIMenuItem.SearchWithFirefox" = "Paluruh ku Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "can dibaca"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Kamari"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Anjeun teu miboga tab bukaeun na Firefox di parangkat séjén."; +"You don’t have any tabs open in Firefox on your other devices." = "Anjeun teu miboga tab bukaeun na Ecosia di parangkat séjén."; /* Your Rights settings section title */ "Your Rights" = "Hak Anjeun"; diff --git a/firefox-ios/Shared/su.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/su.lproj/PrivateBrowsing.strings index c897a5c83e1af..4f535689054d6 100644 --- a/firefox-ios/Shared/su.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/su.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Buka dina Tab Nyamuni Anyar"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox moal nginget jujutan jeung réréméh anjeun, anu diteundeun ukur markah anyar."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia moal nginget jujutan jeung réréméh anjeun, anu diteundeun ukur markah anyar."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Lenyepan"; diff --git a/firefox-ios/Shared/su.lproj/Today.strings b/firefox-ios/Shared/su.lproj/Today.strings index 67cd228a18eb7..25a856957f9cf 100644 --- a/firefox-ios/Shared/su.lproj/Today.strings +++ b/firefox-ios/Shared/su.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Tutup Tab Nyamuni"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Tambah takulan Firefox ka layar Tepas anjeun."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Tambah takulan Ecosia ka layar Tepas anjeun."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Indit ka tutumbu tironan"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Taya tab muka."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Buka Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Buka Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Pamaluruhan Nyamuni"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Tambahkeun takulan Firefox ka layar Tepas anjeun. Sanggeus nambahkeun wijet, toél jeung tahan pikeun ngédit sarta milih takulan anu béda."; +"TodayWidget.QuickActionGalleryDescription" = "Tambahkeun takulan Ecosia ka layar Tepas anjeun. Sanggeus nambahkeun wijet, toél jeung tahan pikeun ngédit sarta milih takulan anu béda."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Lampah gancang"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Takulan Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Takulan Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Tambah takulan ka tab anu muka."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Témbongan Gancang"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Paluruh di Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Paluruh di Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Paluruh dina Tab Nyamuni"; diff --git a/firefox-ios/Shared/sv.lproj/Default Browser.strings b/firefox-ios/Shared/sv.lproj/Default Browser.strings index 452b0b75bc246..b7134f1bf91d3 100644 --- a/firefox-ios/Shared/sv.lproj/Default Browser.strings +++ b/firefox-ios/Shared/sv.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Att göra Firefox till din standardwebbläsare är en röst för ett öppet, tillgängligt internet."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Att göra Ecosia till din standardwebbläsare är en röst för ett öppet, tillgängligt internet."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Standard för ett bättre internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Lär dig hur"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Ställ in länkar från webbplatser, e-post och meddelanden så att de öppnas automatiskt i Firefox."; +"DefaultBrowserCard.Description" = "Ställ in länkar från webbplatser, e-post och meddelanden så att de öppnas automatiskt i Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Välj Firefox som din standardwebbläsare för att automatiskt få hastighet, säkerhet och sekretess."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Välj Ecosia som din standardwebbläsare för att automatiskt få hastighet, säkerhet och sekretess."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Förbättra din dagliga surfning"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox blockerar i genomsnitt över 3 000 spårare per användare varje månad. Gör oss till din standardwebbläsare för att skydda integriteten."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia blockerar i genomsnitt över 3 000 spårare per användare varje månad. Gör oss till din standardwebbläsare för att skydda integriteten."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox skyddar din integritet"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia skyddar din integritet"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Ändra din standardwebbläsare"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Tryck på Standardwebbläsar-app"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Välj Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Välj Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standardwebbläsar-app "; diff --git a/firefox-ios/Shared/sv.lproj/Intro.strings b/firefox-ios/Shared/sv.lproj/Intro.strings index eb24cb7e6453c..8153d999da024 100644 --- a/firefox-ios/Shared/sv.lproj/Intro.strings +++ b/firefox-ios/Shared/sv.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Logga in"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Välkommen till Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Välkommen till Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Börja surfa"; diff --git a/firefox-ios/Shared/sv.lproj/Localizable.strings b/firefox-ios/Shared/sv.lproj/Localizable.strings index 7a9045e4a02c6..afc9d9ca94727 100644 --- a/firefox-ios/Shared/sv.lproj/Localizable.strings +++ b/firefox-ios/Shared/sv.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Det kan vara en felaktig konfiguration eller manipulering av en angripare. Fortsätt om du accepterar risken."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Ägaren av %@ har konfigurerat sin webbplats felaktigt. För att skydda din information från att bli stulen har Firefox inte anslutit till denna webbplats."; +"ErrorPages.CertWarning.Description" = "Ägaren av %@ har konfigurerat sin webbplats felaktigt. För att skydda din information från att bli stulen har Ecosia inte anslutit till denna webbplats."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Den här anslutningen är inte tillförlitlig"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox startsida"; +"Firefox.HomePage.Title" = "Ecosia startsida"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Anpassa startsidan"; @@ -377,7 +377,7 @@ "Forward" = "Framåt"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox-konto"; +"FxA.FirefoxAccount" = "Ecosia-konto"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Hantera konton & enheter"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Logga in med din kamera"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Öppna Firefox på din dator och gå till firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Öppna Ecosia på din dator och gå till ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Redo att skanna"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Synkronisera nu"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync har anslutit till %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync har anslutit till %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync ansluten"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ har kopplats bort."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Den här enheten har kopplats bort från Firefox Sync."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Den här enheten har kopplats bort från Ecosia Sync."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync frånkopplad"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Sync frånkopplad"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "En enhet har kopplats från Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "En enhet har kopplats från Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logotyp, byt bakgrund."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logotyp, byt bakgrund."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Hjälp"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Logga in för att se en lista med flikar från dina andra enheter."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Historik"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Läs mer"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "För att använda autofyll-funktionen för Firefox måste du ha ett enhetslösenord aktiverat."; +"Logins.PasscodeRequirement.Warning" = "För att använda autofyll-funktionen för Ecosia måste du ha ett enhetslösenord aktiverat."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Ta med dina lösenord överallt"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Autofyll Firefox-lösenord"; +"Logins.WelcomeView.Title2" = "Autofyll Ecosia-lösenord"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Aktivera autofyll"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "SPARADE INLOGGNINGAR"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Verkar som Firefox kraschade tidigare. Vill du återställa dina flikar?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Verkar som Ecosia kraschade tidigare. Vill du återställa dina flikar?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Markera som läst"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Börja surfa"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Hoppsan! Firefox kraschade"; +"Oops! Firefox crashed" = "Hoppsan! Ecosia kraschade"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Öppna artiklar i läsvyn genom att trycka på bokikonen när den visas i adressfältet."; @@ -905,7 +905,7 @@ "Open Tabs" = "Öppna flikar"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox kan inte öppna sidan eftersom det har en ogiltig adress."; +"OpenURL.Error.Message" = "Ecosia kan inte öppna sidan eftersom det har en ogiltig adress."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Kan inte öppna sida"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Detta låter dig spara bilden till din kamerarulle."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox vill ha tillgång till dina foton"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia vill ha tillgång till dina foton"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Stäng"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Felaktig data"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Gå till enhetens ‘Inställningar’ > ‘Firefox’. Tillåt Firefox att komma åt kameran."; +"ScanQRCode.PermissionError.Message.v100" = "Gå till enhetens ‘Inställningar’ > ‘Ecosia’. Tillåt Ecosia att komma åt kameran."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Skanna QR-kod"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Växla till flik"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Den nya sökmotorn kommer att visas i snabbsökningsfältet."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Skicka en kraschrapport så Mozilla kan fixa problemet?"; +"Send a crash report so Mozilla can fix the problem?" = "Skicka en kraschrapport så Ecosia kan fixa problemet?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Skicka synpunkter"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Skicka flik"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Du har inga andra enheter anslutna till det här Firefox-kontot som är tillgängliga för synkronisering."; +"SendTo.NoDevicesFound.Message" = "Du har inga andra enheter anslutna till det här Ecosia-kontot som är tillgängliga för synkronisering."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Vänligen öppna Firefox, gå till Inställningar och logga in för att fortsätta."; +"SendTo.NotSignedIn.Message" = "Vänligen öppna Ecosia, gå till Inställningar och logga in för att fortsätta."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Du är inte inloggad med ditt Firefox-konto."; +"SendTo.NotSignedIn.Title" = "Du är inte inloggad med ditt Ecosia-konto."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Skicka"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Tryck för att starta"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Visa"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Datahantering"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox kommer att sluta synkronisera med ditt konto, men kommer inte att ta bort någon av dina webbinformation på den här enheten."; +"Settings.Disconnect.Body" = "Ecosia kommer att sluta synkronisera med ditt konto, men kommer inte att ta bort någon av dina webbinformation på den här enheten."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Koppla från Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Synkroniseringsinställningar"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox-konto"; +"Settings.FxA.Title" = "Ecosia-konto"; /* General settings section title */ "Settings.General.SectionName" = "Allmänt"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Välj vad som ska visas som startsida."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Välj vad som ingår på Firefox-startsidan."; +"Settings.Home.Option.Description.v101" = "Välj vad som ingår på Ecosia-startsidan."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Gå tillbaka till"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Startsida"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Välj vad som ska visas när du återvänder till Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Välj vad som ska visas när du återvänder till Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Senaste flik"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Bakgrundsbild"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox bakgrundsbild, ametistmönster."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia bakgrundsbild, ametistmönster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox bakgrundsbild, mönster med strandkullar."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia bakgrundsbild, mönster med strandkullar."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox bakgrundsbild, cerulean-mönster."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia bakgrundsbild, cerulean-mönster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Ren standardbakgrund."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox bakgrundsbild, soluppgångsmönster."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia bakgrundsbild, soluppgångsmönster."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Växla bakgrundsbildscykel på startsidan"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox bakgrundsbild, mönster med strandkullar i skymning."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia bakgrundsbild, mönster med strandkullar i skymning."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klassisk %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Begränsad utgåva"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ändra bakgrundsbild genom att trycka på Firefox logotyp på startsidan"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ändra bakgrundsbild genom att trycka på Ecosia logotyp på startsidan"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Bakgrundsbild"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Anpassad"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox startsida"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia startsida"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Startsida"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Blockera bilder"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "När du öppnar Firefox"; +"Settings.OfferClipboardBar.Status" = "När du öppnar Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "När du öppnar %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "När du öppnar Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "När du öppnar Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Erbjud att öppna kopierade länkar"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Läs mer."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla strävar efter att endast samla in det vi behöver för att tillhandahålla och förbättra Firefox för alla."; +"Settings.SendUsage.Message" = "Ecosia strävar efter att endast samla in det vi behöver för att tillhandahålla och förbättra Ecosia för alla."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Skicka användningsdata"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Öppna ny flik"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Använd Siri-genvägar för att snabbt öppna Firefox via Siri"; +"Settings.Siri.SectionDescription" = "Använd Siri-genvägar för att snabbt öppna Ecosia via Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri-genvägar"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Läs mer."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox kan installera och köra undersökningar då och då."; +"Settings.Studies.Toggle.Message" = "Ecosia kan installera och köra undersökningar då och då."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Undersökningar"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Ladda i bakgrunden"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Laddas i Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Laddas i Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Öppna i Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Öppna i Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Sök i Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Sök i Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopiera"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Klistra in och öppna"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Sök med Firefox"; +"UIMenuItem.SearchWithFirefox" = "Sök med Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "oläst"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Igår"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Du har inga flikar öppna i Firefox på dina andra enheter."; +"You don’t have any tabs open in Firefox on your other devices." = "Du har inga flikar öppna i Ecosia på dina andra enheter."; /* Your Rights settings section title */ "Your Rights" = "Dina rättigheter"; diff --git a/firefox-ios/Shared/sv.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/sv.lproj/PrivateBrowsing.strings index 5439be356eab3..a60e79047ce7b 100644 --- a/firefox-ios/Shared/sv.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/sv.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Öppna i ny privat flik"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox kommer inte komma ihåg något av din historik eller kakor, men nya bokmärken kommer att sparas som vanligt."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia kommer inte komma ihåg något av din historik eller kakor, men nya bokmärken kommer att sparas som vanligt."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Lär dig mer"; diff --git a/firefox-ios/Shared/sv.lproj/Today.strings b/firefox-ios/Shared/sv.lproj/Today.strings index c2039658325bd..4646dad8e93e7 100644 --- a/firefox-ios/Shared/sv.lproj/Today.strings +++ b/firefox-ios/Shared/sv.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Stäng privata flikar"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Lägg till Firefox-genvägar på din hemskärm."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Lägg till Ecosia-genvägar på din hemskärm."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Gå till kopierad länk"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Inga öppna flikar."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Öppna Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Öppna Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Privat sökning"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Lägg till en Firefox-genväg till din startskärm. När du har lagt till widgeten håller du ned den för att redigera och välja en annan genväg."; +"TodayWidget.QuickActionGalleryDescription" = "Lägg till en Ecosia-genväg till din startskärm. När du har lagt till widgeten håller du ned den för att redigera och välja en annan genväg."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Snabbåtgärder"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox-genvägar"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia-genvägar"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Lägg till genvägar till dina öppna flikar."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Snabbtitt"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Sök i Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Sök i Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Sök i privat flik"; diff --git a/firefox-ios/Shared/ta.lproj/Localizable.strings b/firefox-ios/Shared/ta.lproj/Localizable.strings index d7a23035300d8..1ae2b54ebe281 100644 --- a/firefox-ios/Shared/ta.lproj/Localizable.strings +++ b/firefox-ios/Shared/ta.lproj/Localizable.strings @@ -284,7 +284,7 @@ "fxa.signin.camera-signin" = "பகைப்படக்கருவி மூலம் உள்நுழைக"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "உங்கள் கணினியில் பயர்பாக்சைத் திறந்து firefox.com/pair பக்கத்திற்கு செல்லவும்"; +"fxa.signin.qr-link-instruction" = "உங்கள் கணினியில் பயர்பாக்சைத் திறந்து ecosia.org/pair பக்கத்திற்கு செல்லவும்"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "வருடி தயார்"; diff --git a/firefox-ios/Shared/te.lproj/Intro.strings b/firefox-ios/Shared/te.lproj/Intro.strings index 554ef4597710b..e3a0391a5986a 100644 --- a/firefox-ios/Shared/te.lproj/Intro.strings +++ b/firefox-ios/Shared/te.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ప్రవేశించండి"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefoxకి స్వాగతం"; +"Intro.Slides.Welcome.Title.v2" = "Ecosiaకి స్వాగతం"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "బ్రౌజింగ్ ను ప్రారంభించండి"; diff --git a/firefox-ios/Shared/te.lproj/Localizable.strings b/firefox-ios/Shared/te.lproj/Localizable.strings index 298b78047c725..adffa0798740a 100644 --- a/firefox-ios/Shared/te.lproj/Localizable.strings +++ b/firefox-ios/Shared/te.lproj/Localizable.strings @@ -272,7 +272,7 @@ "Facebook" = "ఫేస్‌బుక్"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox ముంగిలి పేజీ"; +"Firefox.HomePage.Title" = "Ecosia ముంగిలి పేజీ"; /* On each Pocket Stories on the Firefox Homepage, this label appears and indicates the number of minutes to read an article. Minutes should be abbreviated due to space constraints. %d represents the number of minutes */ "FirefoxHome.Pocket.Minutes.v99" = "%d నిమి"; @@ -290,7 +290,7 @@ "Forward" = "ఫార్వార్డ్"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox ఖాతా"; +"FxA.FirefoxAccount" = "Ecosia ఖాతా"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ఖాతా, పరికరాలను నిర్వహించండి"; @@ -305,7 +305,7 @@ "FxA.SyncNow" = "ఇప్పుడే సింక్ చేయి"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync %@కు అనుసంధానమయింది"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync %@కు అనుసంధానమయింది"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync అనుసంధానించబడింది"; @@ -314,7 +314,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ విజయవంతంగా అనుసంధానం తెంచబడింది."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Firefox Syncతో ఈ పరికర అనుసంధానం విజయవంతంగా తెంచివేయబడింది."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Ecosia Syncతో ఈ పరికర అనుసంధానం విజయవంతంగా తెంచివేయబడింది."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync అనుసంధానం తెంచివేయబడింది"; @@ -323,7 +323,7 @@ "FxAPush_DeviceDisconnected_title" = "Sync అనుసంధానం తెంచివేయబడింది"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Firefox Sync నుండి ఒక పరికర అనుసంధానం తెగిపోయింది"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Ecosia Sync నుండి ఒక పరికర అనుసంధానం తెగిపోయింది"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "సహాయం"; @@ -680,7 +680,7 @@ "Okay" = "సరే"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "అయ్యో! Firefox క్రాష్ అయింది"; +"Oops! Firefox crashed" = "అయ్యో! Ecosia క్రాష్ అయింది"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "టైటిల్ బార్ లో కనిపించినపుడు పుస్తకం చిహ్నాన్ని నొక్కడం ద్వారా రీడర్ లో వ్యాసాలు చూడండి."; @@ -692,7 +692,7 @@ "Open Tabs" = "తెరిచివున్న ట్యాబులు"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "అది ఒక చెల్లని చిరునామాను కలిగి ఉండటం వలన Firefox పేజీని తెరవలేదు."; +"OpenURL.Error.Message" = "అది ఒక చెల్లని చిరునామాను కలిగి ఉండటం వలన Ecosia పేజీని తెరవలేదు."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "పేజీలను తెరవలేదు"; @@ -704,7 +704,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "ఇది బొమ్మను మీ కేమెరా రోల్‌కు భద్రపరుచుకోనిస్తుంది."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox మీ ఫోటోలను యాక్సెస్ చేయాలనుకుంటుంది"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia మీ ఫోటోలను యాక్సెస్ చేయాలనుకుంటుంది"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "మూసివేయి"; @@ -815,7 +815,7 @@ "Search.ThirdPartyEngines.OK" = "సరే"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozilla సమస్యను పరిష్కరించాలి కాబట్టి క్రాష్ నివేదికను పంపాలా?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosia సమస్యను పరిష్కరించాలి కాబట్టి క్రాష్ నివేదికను పంపాలా?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "అభిప్రాయాన్ని పంపండి"; @@ -842,13 +842,13 @@ "SendTo.NavBar.Title" = "ట్యాబును పంపు"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "సింక్రనించడానికి ఈ Firefox ఖాతాకు అనుసంధానించబడిన పరికరాలు ఏమీ మీకు లేవు."; +"SendTo.NoDevicesFound.Message" = "సింక్రనించడానికి ఈ Ecosia ఖాతాకు అనుసంధానించబడిన పరికరాలు ఏమీ మీకు లేవు."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "కొనసాగించడానికి, దయచేసి Firefoxను తెరిచి, అమరికలకు వెళ్లి, ప్రవేశించండి."; +"SendTo.NotSignedIn.Message" = "కొనసాగించడానికి, దయచేసి Ecosiaను తెరిచి, అమరికలకు వెళ్లి, ప్రవేశించండి."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "మీరు మీ Firefox ఖాతా లోనికి ప్రవేశించిలేరు."; +"SendTo.NotSignedIn.Title" = "మీరు మీ Ecosia ఖాతా లోనికి ప్రవేశించిలేరు."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "పంపించు"; @@ -869,7 +869,7 @@ "SentTab.NoTabArrivingNotification.body" = "నిష్క్రమించడానికి తాకండి"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox సమకాలీకరణ"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia సమకాలీకరణ"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "చూడండి"; @@ -986,7 +986,7 @@ "Settings.FxA.Sync.SectionName" = "సింక్ అమరికలు"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox ఖాతా"; +"Settings.FxA.Title" = "Ecosia ఖాతా"; /* General settings section title */ "Settings.General.SectionName" = "సాధారణం"; @@ -1031,7 +1031,7 @@ "Settings.NewTab.Option.BlankPage" = "ఖాళీ పేజీ"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox ముంగిలి"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia ముంగిలి"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ముంగిలిపేజీ"; @@ -1052,10 +1052,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "బొమ్మలను నిరోధించు"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox తెరుస్తున్నపుడు"; +"Settings.OfferClipboardBar.Status" = "Ecosia తెరుస్తున్నపుడు"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox తెరుస్తున్నపుడు"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia తెరుస్తున్నపుడు"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "నకలు చేసిన లంకెలను తెరవడానికి సిద్దపడండి"; @@ -1079,7 +1079,7 @@ "Settings.SendUsage.Link" = "ఇంకా తెలుసుకోండి."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla ప్రతి ఒక్కరికీ Firefoxను అందించడానికి మరియు మెరుగుపరచడానికి మాత్రమే అవసరమైనవి మాత్రమే సేకరించేందుకు కృషి చేస్తుంది."; +"Settings.SendUsage.Message" = "Ecosia ప్రతి ఒక్కరికీ Ecosiaను అందించడానికి మరియు మెరుగుపరచడానికి మాత్రమే అవసరమైనవి మాత్రమే సేకరించేందుకు కృషి చేస్తుంది."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "వినియోగించిన డేటాను పంపు"; @@ -1100,7 +1100,7 @@ "Settings.Siri.OpenTabShortcut" = "కొత్త ట్యాబును తెరువు"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "సిరి ద్వారా Firefoxను త్వరగా తెరవడానికి సిరి షార్టుకట్లను వాడుకోండి"; +"Settings.Siri.SectionDescription" = "సిరి ద్వారా Ecosiaను త్వరగా తెరవడానికి సిరి షార్టుకట్లను వాడుకోండి"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "సిరిలో సత్వరమార్గాలు"; @@ -1169,13 +1169,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "వెనుతలంలో తెరువు"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox లో లోడ్ అవుతోంది"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia లో లోడ్ అవుతోంది"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox లో తెరవండి"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia లో తెరవండి"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox లో శోధించండి"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia లో శోధించండి"; /* Label for show search suggestions setting. */ "Show Search Suggestions" = "శోధన సలహాలను చూపించు"; @@ -1283,7 +1283,7 @@ "UIMenuItem.PasteGo" = "అతికించి వెళ్ళు"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox‌తో వెతుకు"; +"UIMenuItem.SearchWithFirefox" = "Ecosia‌తో వెతుకు"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "చదవ లేదు"; @@ -1310,7 +1310,7 @@ "Yesterday" = "నిన్నటి రోజు"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "మీరు మీ ఇతర పరికరాల్లో Firefoxలో ఏ ట్యాబులు తెరిచి లేదు."; +"You don’t have any tabs open in Firefox on your other devices." = "మీరు మీ ఇతర పరికరాల్లో Ecosiaలో ఏ ట్యాబులు తెరిచి లేదు."; /* Your Rights settings section title */ "Your Rights" = "మీ హక్కులు"; diff --git a/firefox-ios/Shared/th.lproj/Default Browser.strings b/firefox-ios/Shared/th.lproj/Default Browser.strings index bafaf7d0d7432..1af08fa053bba 100644 --- a/firefox-ios/Shared/th.lproj/Default Browser.strings +++ b/firefox-ios/Shared/th.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "การทำให้ Firefox เป็นเบราว์เซอร์เริ่มต้นของคุณเป็นการร่วมสนับสนุนอินเทอร์เน็ตที่เปิดกว้างและเข้าถึงได้"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "การทำให้ Ecosia เป็นเบราว์เซอร์เริ่มต้นของคุณเป็นการร่วมสนับสนุนอินเทอร์เน็ตที่เปิดกว้างและเข้าถึงได้"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "ตั้งค่าเริ่มต้นให้เป็นอินเทอร์เน็ตที่ดีกว่า"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "เรียนรู้วิธี"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "ตั้งลิงก์จากเว็บไซต์ อีเมล และข้อความให้เปิดโดยอัตโนมัติใน Firefox"; +"DefaultBrowserCard.Description" = "ตั้งลิงก์จากเว็บไซต์ อีเมล และข้อความให้เปิดโดยอัตโนมัติใน Ecosia"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "เลือก Firefox เป็นเบราว์เซอร์เริ่มต้นของคุณเพื่อทำให้ความเร็ว ความปลอดภัย และความเป็นส่วนตัวเป็นไปโดยอัตโนมัติ"; +"DefaultBrowserCard.NextLevel.Description.v108" = "เลือก Ecosia เป็นเบราว์เซอร์เริ่มต้นของคุณเพื่อทำให้ความเร็ว ความปลอดภัย และความเป็นส่วนตัวเป็นไปโดยอัตโนมัติ"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "ยกระดับการเรียกดูในแต่ละวัน"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ปิดกั้นตัวติดตามมากกว่า 3,000 ตัวต่อผู้ใช้ในแต่ละเดือนโดยเฉลี่ย ตั้งค่าให้เราเป็นเบราว์เซอร์เริ่มต้นเพื่อความอุ่นใจในความเป็นส่วนตัว"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ปิดกั้นตัวติดตามมากกว่า 3,000 ตัวต่อผู้ใช้ในแต่ละเดือนโดยเฉลี่ย ตั้งค่าให้เราเป็นเบราว์เซอร์เริ่มต้นเพื่อความอุ่นใจในความเป็นส่วนตัว"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox มีความเป็นส่วนตัวครอบคลุม"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia มีความเป็นส่วนตัวครอบคลุม"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "เปลี่ยนเบราว์เซอร์เริ่มต้นของคุณ"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. แตะแอปเบราว์เซอร์เริ่มต้น"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. เลือก Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. เลือก Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "แอปเบราว์เซอร์เริ่มต้น"; diff --git a/firefox-ios/Shared/th.lproj/Intro.strings b/firefox-ios/Shared/th.lproj/Intro.strings index 71735b3d9a2d0..e81cdbf042807 100644 --- a/firefox-ios/Shared/th.lproj/Intro.strings +++ b/firefox-ios/Shared/th.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "ลงชื่อเข้า"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "ยินดีต้อนรับสู่ Firefox"; +"Intro.Slides.Welcome.Title.v2" = "ยินดีต้อนรับสู่ Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "เริ่มการเรียกดู"; diff --git a/firefox-ios/Shared/th.lproj/Localizable.strings b/firefox-ios/Shared/th.lproj/Localizable.strings index 45b086f7e49a3..53fcf6aa44747 100644 --- a/firefox-ios/Shared/th.lproj/Localizable.strings +++ b/firefox-ios/Shared/th.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "สิ่งนี้อาจเป็นการกำหนดค่าที่ผิดพลาดหรือกำลังมีการดัดแปลงโดยผู้โจมตี ดำเนินการต่อหากคุณยอมรับในความเสี่ยงที่อาจเกิดขึ้น"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "เจ้าของ %@ ได้กำหนดค่าเว็บไซต์ของพวกเขาไม่เหมาะสม เพื่อปกป้องข้อมูลของคุณจากการถูกขโมย Firefox จึงไม่ได้เชื่อมต่อไปยังเว็บไซต์นี้"; +"ErrorPages.CertWarning.Description" = "เจ้าของ %@ ได้กำหนดค่าเว็บไซต์ของพวกเขาไม่เหมาะสม เพื่อปกป้องข้อมูลของคุณจากการถูกขโมย Ecosia จึงไม่ได้เชื่อมต่อไปยังเว็บไซต์นี้"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "การเชื่อมต่อนี้ไม่น่าเชื่อถือ"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "หน้าแรกของ Firefox"; +"Firefox.HomePage.Title" = "หน้าแรกของ Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "ปรับแต่งหน้าแรก"; @@ -377,7 +377,7 @@ "Forward" = "เดินหน้า"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "บัญชี Firefox"; +"FxA.FirefoxAccount" = "บัญชี Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "จัดการบัญชีและอุปกรณ์"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "ลงชื่อเข้าด้วยกล้องของคุณ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "บนคอมพิวเตอร์ของคุณ ให้เปิด Firefox แล้วไปที่ firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "บนคอมพิวเตอร์ของคุณ ให้เปิด Ecosia แล้วไปที่ ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "พร้อมที่จะสแกน"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "ซิงค์ตอนนี้"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync ได้เชื่อมต่อกับ %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync ได้เชื่อมต่อกับ %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "เชื่อมต่อ Sync แล้ว"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "ตัดการเชื่อมต่อ %@ สำเร็จ"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "ตัดการเชื่อมต่ออุปกรณ์นี้จาก Firefox Sync สำเร็จ"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "ตัดการเชื่อมต่ออุปกรณ์นี้จาก Ecosia Sync สำเร็จ"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ตัดการเชื่อมต่อ Sync แล้ว"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "ตัดการเชื่อมต่อ Sync แล้ว"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "อุปกรณ์ได้ตัดการเชื่อมต่อจาก Firefox Sync"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "อุปกรณ์ได้ตัดการเชื่อมต่อจาก Ecosia Sync"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "โลโก้ Firefoxเปลี่ยนวอลเปเปอร์"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "โลโก้ Ecosiaเปลี่ยนวอลเปเปอร์"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ช่วยเหลือ"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "ลงชื่อเข้าเพื่อดูรายการแท็บจากอุปกรณ์อื่น ๆ ของคุณ"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "ประวัติ"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "เรียนรู้เพิ่มเติม"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "ในการใช้คุณสมบัติป้อนอัตโนมัติสำหรับ Firefox คุณต้องเปิดใช้งานรหัสผ่านของอุปกรณ์"; +"Logins.PasscodeRequirement.Warning" = "ในการใช้คุณสมบัติป้อนอัตโนมัติสำหรับ Ecosia คุณต้องเปิดใช้งานรหัสผ่านของอุปกรณ์"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "นำรหัสผ่านของคุณไปทุกที่"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "ป้อนรหัสผ่าน Firefox อัตโนมัติ"; +"Logins.WelcomeView.Title2" = "ป้อนรหัสผ่าน Ecosia อัตโนมัติ"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "เปิดการป้อนอัตโนมัติ"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "การเข้าสู่ระบบที่บันทึกไว้"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ดูเหมือนว่า Firefox จะเกิดข้อขัดข้องก่อนหน้านี้ คุณต้องการเรียกคืนแท็บของคุณหรือไม่?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ดูเหมือนว่า Ecosia จะเกิดข้อขัดข้องก่อนหน้านี้ คุณต้องการเรียกคืนแท็บของคุณหรือไม่?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "ทำเครื่องหมายว่าอ่านแล้ว"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "เริ่มการเรียกดู"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "อุปส์! Firefox เกิดข้อขัดข้อง"; +"Oops! Firefox crashed" = "อุปส์! Ecosia เกิดข้อขัดข้อง"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "เปิดบทความในมุมมองผู้อ่านโดยแตะไอคอนหนังสือเมื่อปรากฏในแถบหัวข้อ"; @@ -905,7 +905,7 @@ "Open Tabs" = "แท็บที่เปิดอยู่"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox ไม่สามารถเปิดหน้าได้เนื่องจากที่อยู่ไม่ถูกต้อง"; +"OpenURL.Error.Message" = "Ecosia ไม่สามารถเปิดหน้าได้เนื่องจากที่อยู่ไม่ถูกต้อง"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "ไม่สามารถเปิดหน้า"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "สิ่งนี้อนุญาตให้คุณสามารถบันทึกภาพไปยัง ม้วนฟิล์ม ของคุณ"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox ต้องการเข้าถึงรูปถ่ายของคุณ"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia ต้องการเข้าถึงรูปถ่ายของคุณ"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "ปิด"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "ข้อมูลไม่ถูกต้อง"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "ไปที่ ‘การตั้งค่า’ ของอุปกรณ์ > ‘Firefox’ และอนุญาตให้ Firefox เข้าถึงกล้อง"; +"ScanQRCode.PermissionError.Message.v100" = "ไปที่ ‘การตั้งค่า’ ของอุปกรณ์ > ‘Ecosia’ และอนุญาตให้ Ecosia เข้าถึงกล้อง"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "สแกนคิวอาร์โค้ด"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "สลับไปยังแท็บ"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Suggest"; +"Search.SuggestSectionTitle.v102" = "Ecosia Suggest"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "เครื่องมือค้นหาใหม่จะปรากฏในแถบค้นหาด่วน"; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "ตกลง"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "ส่งรายงานข้อขัดข้องเพื่อให้ Mozilla สามารถแก้ไขปัญหา?"; +"Send a crash report so Mozilla can fix the problem?" = "ส่งรายงานข้อขัดข้องเพื่อให้ Ecosia สามารถแก้ไขปัญหา?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "ส่งข้อคิดเห็น"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "ส่งแท็บ"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "คุณไม่มีอุปกรณ์ใด ๆ ที่เชื่อมต่อกับบัญชี Firefox นี้ที่พร้อมซิงค์"; +"SendTo.NoDevicesFound.Message" = "คุณไม่มีอุปกรณ์ใด ๆ ที่เชื่อมต่อกับบัญชี Ecosia นี้ที่พร้อมซิงค์"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "โปรดเปิด Firefox ไปยังการตั้งค่าและลงชื่อเข้าเพื่อดำเนินการต่อ"; +"SendTo.NotSignedIn.Message" = "โปรดเปิด Ecosia ไปยังการตั้งค่าและลงชื่อเข้าเพื่อดำเนินการต่อ"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "คุณไม่ได้ลงชื่อเข้าบัญชี Firefox ของคุณ"; +"SendTo.NotSignedIn.Title" = "คุณไม่ได้ลงชื่อเข้าบัญชี Ecosia ของคุณ"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ส่ง"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "แตะเพื่อเริ่มต้น"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "ดู"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "การจัดการข้อมูล"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox จะหยุดการซิงค์กับบัญชีของคุณ แต่จะไม่ลบข้อมูลการเรียกดูใด ๆ ของคุณในอุปกรณ์นี้"; +"Settings.Disconnect.Body" = "Ecosia จะหยุดการซิงค์กับบัญชีของคุณ แต่จะไม่ลบข้อมูลการเรียกดูใด ๆ ของคุณในอุปกรณ์นี้"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ตัดการเชื่อมต่อ Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "การตั้งค่า Sync"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "บัญชี Firefox"; +"Settings.FxA.Title" = "บัญชี Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "ทั่วไป"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "เลือกสิ่งที่ต้องการให้แสดงบนหน้าแรก"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "เลือกสิ่งที่รวมอยู่ในหน้าแรกของ Firefox"; +"Settings.Home.Option.Description.v101" = "เลือกสิ่งที่รวมอยู่ในหน้าแรกของ Ecosia"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "กลับเข้าไป"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "หน้าแรก"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "เลือกสิ่งที่คุณเห็นเมื่อคุณกลับเข้าสู่ Firefox"; +"Settings.Home.Option.StartAtHome.Description" = "เลือกสิ่งที่คุณเห็นเมื่อคุณกลับเข้าสู่ Ecosia"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "แท็บสุดท้าย"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "รูปพื้นหลัง"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "วอลล์เปเปอร์ Firefox, ลายอะเมทิสต์"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "วอลล์เปเปอร์ Ecosia, ลายอะเมทิสต์"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "วอลล์เปเปอร์ Firefox, ลายเนินเขาชายหาด"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "วอลล์เปเปอร์ Ecosia, ลายเนินเขาชายหาด"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "วอลล์เปเปอร์ Firefox, ลายซีรูเลียน"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "วอลล์เปเปอร์ Ecosia, ลายซีรูเลียน"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "วอลล์เปเปอร์ว่างเริ่มต้น"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "วอลล์เปเปอร์ Firefox, ลายพระอาทิตย์ขึ้น"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "วอลล์เปเปอร์ Ecosia, ลายพระอาทิตย์ขึ้น"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "เปลี่ยนวอลล์เปเปอร์ของหน้าแรกแบบหมุนเวียน"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "วอลล์เปเปอร์ Firefox, ลายทิวเขายามพลบค่ำ"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "วอลล์เปเปอร์ Ecosia, ลายทิวเขายามพลบค่ำ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "คลาสสิค %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "เวลาจำกัด"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "เปลี่ยนวอลเปเปอร์โดยแตะโลโก้หน้าแรกของ Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "เปลี่ยนวอลเปเปอร์โดยแตะโลโก้หน้าแรกของ Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "รูปพื้นหลัง"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "กำหนดเอง"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "หน้าแรก Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "หน้าแรก Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "หน้าแรก"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "ปิดกั้นภาพ"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "เมื่อเปิด Firefox"; +"Settings.OfferClipboardBar.Status" = "เมื่อเปิด Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "เมื่อเปิด %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "เมื่อเปิด Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "เมื่อเปิด Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "เสนอให้เปิดลิงก์ที่คัดลอก"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "เรียนรู้เพิ่มเติม"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla มุ่งมั่นที่จะเก็บรวบรวมเฉพาะสิ่งที่เราจำเป็นต้องให้บริการและปรับปรุง Firefox สำหรับทุกคน"; +"Settings.SendUsage.Message" = "Ecosia มุ่งมั่นที่จะเก็บรวบรวมเฉพาะสิ่งที่เราจำเป็นต้องให้บริการและปรับปรุง Ecosia สำหรับทุกคน"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ส่งข้อมูลการใช้งาน"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "เปิดแท็บใหม่"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "ใช้คำสั่งลัด Siri เพื่อเปิด Firefox อย่างรวดเร็วผ่าน Siri"; +"Settings.Siri.SectionDescription" = "ใช้คำสั่งลัด Siri เพื่อเปิด Ecosia อย่างรวดเร็วผ่าน Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "คำสั่งลัด Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "เรียนรู้เพิ่มเติม"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox อาจติดตั้งและเรียกใช้การศึกษาเป็นครั้งคราว"; +"Settings.Studies.Toggle.Message" = "Ecosia อาจติดตั้งและเรียกใช้การศึกษาเป็นครั้งคราว"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "การศึกษา"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "โหลดในพื้นหลัง"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "กำลังโหลดใน Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "กำลังโหลดใน Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "เปิดใน Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "เปิดใน Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "ค้นหาใน Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "ค้นหาใน Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "คัดลอก"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "วางแล้วไป"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "ค้นหาด้วย Firefox"; +"UIMenuItem.SearchWithFirefox" = "ค้นหาด้วย Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ยังไม่ได้อ่าน"; @@ -1793,7 +1793,7 @@ "Yesterday" = "เมื่อวานนี้"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "คุณไม่มีแท็บใด ๆ ที่เปิดอยู่ใน Firefox ในอุปกรณ์อื่น ๆ ของคุณ"; +"You don’t have any tabs open in Firefox on your other devices." = "คุณไม่มีแท็บใด ๆ ที่เปิดอยู่ใน Ecosia ในอุปกรณ์อื่น ๆ ของคุณ"; /* Your Rights settings section title */ "Your Rights" = "สิทธิของคุณ"; diff --git a/firefox-ios/Shared/th.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/th.lproj/PrivateBrowsing.strings index f95f6461035db..73392af49fdf0 100644 --- a/firefox-ios/Shared/th.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/th.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "เปิดในแท็บส่วนตัวใหม่"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox จะไม่จดจำประวัติหรือคุกกี้ใด ๆ ของคุณ แต่จะบันทึกที่คั่นหน้าใหม่ไว้"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia จะไม่จดจำประวัติหรือคุกกี้ใด ๆ ของคุณ แต่จะบันทึกที่คั่นหน้าใหม่ไว้"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "เรียนรู้เพิ่มเติม"; diff --git a/firefox-ios/Shared/th.lproj/Today.strings b/firefox-ios/Shared/th.lproj/Today.strings index 52932059c0b9d..e2ac1c9a7bdc9 100644 --- a/firefox-ios/Shared/th.lproj/Today.strings +++ b/firefox-ios/Shared/th.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "ปิดแท็บส่วนตัว"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "เพิ่มทางลัด Firefox ไปยังหน้าจอหลักของคุณ"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "เพิ่มทางลัด Ecosia ไปยังหน้าจอหลักของคุณ"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "ไปยังลิงก์ที่คัดลอก"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "ไม่มีแท็บที่เปิดอยู่"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "เปิด Firefox"; +"TodayWidget.OpenFirefoxLabel" = "เปิด Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "การค้นหาแบบส่วนตัว"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "เพิ่มทางลัด Firefox ลงในหน้าจอโฮมของคุณ หลังจากเพิ่มวิดเจ็ตแล้ว ให้แตะค้างไว้เพื่อแก้ไขและเลือกทางลัดอื่น"; +"TodayWidget.QuickActionGalleryDescription" = "เพิ่มทางลัด Ecosia ลงในหน้าจอโฮมของคุณ หลังจากเพิ่มวิดเจ็ตแล้ว ให้แตะค้างไว้เพื่อแก้ไขและเลือกทางลัดอื่น"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "การกระทำด่วน"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox Shortcuts"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia Shortcuts"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "เพิ่มทางลัดไปยังแท็บที่เปิดอยู่ของคุณ"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "การดูแบบด่วน"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "ค้นหาใน Firefox"; +"TodayWidget.SearchInFirefoxV2" = "ค้นหาใน Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "ค้นหาในแท็บส่วนตัว"; diff --git a/firefox-ios/Shared/tr.lproj/Default Browser.strings b/firefox-ios/Shared/tr.lproj/Default Browser.strings index 9f2378bef562f..a5fa8e311f124 100644 --- a/firefox-ios/Shared/tr.lproj/Default Browser.strings +++ b/firefox-ios/Shared/tr.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox’u saptanmış tarayıcınız yaparak açık ve erişilebilir bir interneti desteklemiş olursunuz."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia’u saptanmış tarayıcınız yaparak açık ve erişilebilir bir interneti desteklemiş olursunuz."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Hep daha iyi bir internet"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Nasıl yapılacağını öğren"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Web siteleri, e-postalar ve Mesajlar’daki bağlantılar Firefox’ta açılsın."; +"DefaultBrowserCard.Description" = "Web siteleri, e-postalar ve Mesajlar’daki bağlantılar Ecosia’ta açılsın."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Firefox’u saptanmış tarayıcınız olarak ayarlayın; hız, güvenlik ve gizliliği otomatik hale getirin."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Ecosia’u saptanmış tarayıcınız olarak ayarlayın; hız, güvenlik ve gizliliği otomatik hale getirin."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Günlük gezintilerinizi iyileştirin"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox, her ay kullanıcı başına ortalama 3.000 takip kodunu engelliyor. Gizliliğinizi korumak için bizi saptanmış tarayıcınız yapın."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia, her ay kullanıcı başına ortalama 3.000 takip kodunu engelliyor. Gizliliğinizi korumak için bizi saptanmış tarayıcınız yapın."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox gizliliğinizi korur"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia gizliliğinizi korur"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Saptanmış tarayıcınızı değiştirin"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Saptanmış Tarayıcı Uygulaması’na dokunun"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox’u seçin"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia’u seçin"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Saptanmış Tarayıcı Uygulaması"; diff --git a/firefox-ios/Shared/tr.lproj/Intro.strings b/firefox-ios/Shared/tr.lproj/Intro.strings index 1003f2994e0a7..74f735d3db6db 100644 --- a/firefox-ios/Shared/tr.lproj/Intro.strings +++ b/firefox-ios/Shared/tr.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Giriş yap"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox’a hoş geldiniz"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia’a hoş geldiniz"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Gezinmeye başla"; diff --git a/firefox-ios/Shared/tr.lproj/Localizable.strings b/firefox-ios/Shared/tr.lproj/Localizable.strings index a7091015debdf..d313543558998 100644 --- a/firefox-ios/Shared/tr.lproj/Localizable.strings +++ b/firefox-ios/Shared/tr.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Bu, hatalı yapılandırmadan veya saldırganların yaptığı tahrifattan kaynaklanabilir. Olası riski kabul ediyorsanız devam edebilirsiniz."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ web sitesinin sahibi siteyi hatalı yapılandırmış. Bilgilerinizin çalınmasını önlemek için Firefox bu web sitesine bağlanmadı."; +"ErrorPages.CertWarning.Description" = "%@ web sitesinin sahibi siteyi hatalı yapılandırmış. Bilgilerinizin çalınmasını önlemek için Ecosia bu web sitesine bağlanmadı."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Bu bağlantı güvenilir değil"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox Giriş Sayfası"; +"Firefox.HomePage.Title" = "Ecosia Giriş Sayfası"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Giriş sayfasını özelleştir"; @@ -377,7 +377,7 @@ "Forward" = "İleri"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Hesabı"; +"FxA.FirefoxAccount" = "Ecosia Hesabı"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Hesabı ve cihazları yönet"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Kameranızla giriş yapın"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Bilgisayarınızda Firefox’u açıp firefox.com/pair adresine gidin"; +"fxa.signin.qr-link-instruction" = "Bilgisayarınızda Ecosia’u açıp ecosia.org/pair adresine gidin"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Taramaya hazırım"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Şimdi eşitle"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Sync yeni bir cihaza bağlandı: %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Sync yeni bir cihaza bağlandı: %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Sync bağlandı"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ adlı cihazın bağlantısı başarıyla kesildi."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Bu cihazın Firefox Sync bağlantısı başarıyla kesildi."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Bu cihazın Ecosia Sync bağlantısı başarıyla kesildi."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Sync bağlantısı kesildi"; @@ -422,7 +422,7 @@ "FxAPush_DeviceDisconnected_UnknownDevice_body" = "Bir cihazın Sync bağlantısı kesildi"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox logosu, duvar kâğıdını değiştir."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia logosu, duvar kâğıdını değiştir."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Yardım"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Diğer cihazlarınızdaki sekmeleri görmek için giriş yapın."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Geçmiş"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Daha fazla bilgi al"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox’un otomatik doldurma özelliğini kullanmak için cihaz parolasını ayarlamalısınız."; +"Logins.PasscodeRequirement.Warning" = "Ecosia’un otomatik doldurma özelliğini kullanmak için cihaz parolasını ayarlamalısınız."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Parolalarınızı yanınızda taşıyın"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox parolalarını otomatik doldur"; +"Logins.WelcomeView.Title2" = "Ecosia parolalarını otomatik doldur"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Otomatik doldurmayı aç"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "KAYITLI HESAPLAR"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox daha önce çökmüş gibi görünüyor. Sekmelerinizi geri yüklemek ister misiniz?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia daha önce çökmüş gibi görünüyor. Sekmelerinizi geri yüklemek ister misiniz?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Okundu olarak işaretle"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Gezinmeye başla"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Firefox çöktü"; +"Oops! Firefox crashed" = "Ecosia çöktü"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Makaleleri Okuyucu görünümünde açmak için başlık çubuğunda kitap simgesi göründüğünde ona dokunun."; @@ -905,7 +905,7 @@ "Open Tabs" = "Açık sekmeler"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Adresi geçersiz olduğu için Firefox bu sayfayı açamıyor."; +"OpenURL.Error.Message" = "Adresi geçersiz olduğu için Ecosia bu sayfayı açamıyor."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Sayfa açılamadı"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Seçtiğiniz resmin Film Rulosu’na kaydedilebilmesi için gereklidir."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox fotoğraflarınıza erişmek istiyor"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia fotoğraflarınıza erişmek istiyor"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Kapat"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Veri geçersiz"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Cihazınızda ‘Ayarlar’ > ‘Firefox’a gidin. Firefox’un kameraya erişmesine izin verin."; +"ScanQRCode.PermissionError.Message.v100" = "Cihazınızda ‘Ayarlar’ > ‘Ecosia’a gidin. Ecosia’un kameraya erişmesine izin verin."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR kodu tara"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Sekmeye geç"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox Önerileri"; +"Search.SuggestSectionTitle.v102" = "Ecosia Önerileri"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Yeni arama motoru hızlı arama çubuğunda görünecektir."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "Tamam"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Sorunu giderebilmemiz için Mozilla’ya çökme raporu gönderilsin mi?"; +"Send a crash report so Mozilla can fix the problem?" = "Sorunu giderebilmemiz için Ecosia’ya çökme raporu gönderilsin mi?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Geri bildirim gönder"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Sekmeyi Gönder"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Bu Firefox Hesabı’na bağlanmış ve eşitleme yapabileceğiniz başka cihazınız yok."; +"SendTo.NoDevicesFound.Message" = "Bu Ecosia Hesabı’na bağlanmış ve eşitleme yapabileceğiniz başka cihazınız yok."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Devam etmek için lütfen Firefox’u açın, Ayarlar’a gidin ve giriş yapın."; +"SendTo.NotSignedIn.Message" = "Devam etmek için lütfen Ecosia’u açın, Ayarlar’a gidin ve giriş yapın."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Firefox Hesabı’nıza giriş yapmadınız."; +"SendTo.NotSignedIn.Title" = "Ecosia Hesabı’nıza giriş yapmadınız."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Gönder"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Başlamak için dokunun"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Görüntüle"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Veri yönetimi"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox artık hesabınızla eşitlenmeyecek ama bu cihazdaki gezinti geçmişiniz silinmeyecek."; +"Settings.Disconnect.Body" = "Ecosia artık hesabınızla eşitlenmeyecek ama bu cihazdaki gezinti geçmişiniz silinmeyecek."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Sync bağlantısını kes"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Sync ayarları"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Hesabı"; +"Settings.FxA.Title" = "Ecosia Hesabı"; /* General settings section title */ "Settings.General.SectionName" = "Genel"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Giriş sayfasında nelerin görüneceğini seçin."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox giriş sayfasında neleri göreceğinizi seçin."; +"Settings.Home.Option.Description.v101" = "Ecosia giriş sayfasında neleri göreceğinizi seçin."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Açık sekmeler"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Giriş sayfası"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox’a döndüğünüzde neleri göreceğinizi seçin."; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia’a döndüğünüzde neleri göreceğinizi seçin."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Son sekme"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Duvar kâğıdı"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox duvar kâğıdı, ametist deseni."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia duvar kâğıdı, ametist deseni."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox duvar kâğıdı, sahil tepeleri deseni."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia duvar kâğıdı, sahil tepeleri deseni."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox duvar kâğıdı, gök mavisi deseni."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia duvar kâğıdı, gök mavisi deseni."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Varsayılan sade duvar kâğıdı."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox duvar kâğıdı, gün doğumu deseni."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia duvar kâğıdı, gün doğumu deseni."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Giriş sayfası duvar kâğıdı değiştirme düğmesi"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox duvar kâğıdı, alacakaranlık tepeleri deseni."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia duvar kâğıdı, alacakaranlık tepeleri deseni."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasik %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Limited Edition"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Giriş sayfasındaki Firefox logosuna dokunarak duvar kâğıdını değiştirin"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Giriş sayfasındaki Ecosia logosuna dokunarak duvar kâğıdını değiştirin"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Duvar kâğıdı"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Özel"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox Başlangıç"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia Başlangıç"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Giriş sayfası"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Resimleri engelle"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox açılırken"; +"Settings.OfferClipboardBar.Status" = "Ecosia açılırken"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@ açılırken"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox açılırken"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia açılırken"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Kopyalanan bağlantıları açmayı öner"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Ayrıntılı bilgi alın."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla, yalnızca Firefox’u daha da geliştirmek için ihtiyaç duyduğumuz verileri toplar."; +"Settings.SendUsage.Message" = "Ecosia, yalnızca Ecosia’u daha da geliştirmek için ihtiyaç duyduğumuz verileri toplar."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Kullanım verilerini gönder"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Yeni sekme aç"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Firefox’u Siri ile hızlıca açmak için Siri kestirmelerini kullanabilirsiniz"; +"Settings.Siri.SectionDescription" = "Ecosia’u Siri ile hızlıca açmak için Siri kestirmelerini kullanabilirsiniz"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri kestirmeleri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Daha fazla bilgi alın."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox zaman zaman araştırmalar yükleyip çalıştırabilir."; +"Settings.Studies.Toggle.Message" = "Ecosia zaman zaman araştırmalar yükleyip çalıştırabilir."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Araştırmalar"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Arka planda yükle"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox’ta yükleniyor"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia’ta yükleniyor"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox’ta aç"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia’ta aç"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox’ta ara"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia’ta ara"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Kopyala"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Yapıştır ve git"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox ile ara"; +"UIMenuItem.SearchWithFirefox" = "Ecosia ile ara"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "okunmadı"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Dün"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Diğer cihazlarınızdaki Firefox’larda hiç açık sekme yok."; +"You don’t have any tabs open in Firefox on your other devices." = "Diğer cihazlarınızdaki Ecosia’larda hiç açık sekme yok."; /* Your Rights settings section title */ "Your Rights" = "Haklarınız"; diff --git a/firefox-ios/Shared/tr.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/tr.lproj/PrivateBrowsing.strings index 3dc3203972602..4de62cfdff481 100644 --- a/firefox-ios/Shared/tr.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/tr.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Yeni gizli sekmede aç"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox geçmişinizi ve çerezleri hatırlamayacaktır ama kaydettiğiniz yer imleri saklanacaktır."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia geçmişinizi ve çerezleri hatırlamayacaktır ama kaydettiğiniz yer imleri saklanacaktır."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Daha fazla bilgi alın"; diff --git a/firefox-ios/Shared/tr.lproj/Today.strings b/firefox-ios/Shared/tr.lproj/Today.strings index 3c64f5f9b48c0..29a2ad085b0c7 100644 --- a/firefox-ios/Shared/tr.lproj/Today.strings +++ b/firefox-ios/Shared/tr.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Gizlileri kapat"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Ana ekranınıza Firefox kısayollarını ekleyin."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Ana ekranınıza Ecosia kısayollarını ekleyin."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Kopyalanan linke git"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Açık sekme yok."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox’u aç"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia’u aç"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Gizli arama"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Ana ekranınıza Firefox kısayolu ekleyin. Araç takımını ekledikten sonra düzenlemek isterseniz basılı tutup farklı bir kısayol seçebilirsiniz."; +"TodayWidget.QuickActionGalleryDescription" = "Ana ekranınıza Ecosia kısayolu ekleyin. Araç takımını ekledikten sonra düzenlemek isterseniz basılı tutup farklı bir kısayol seçebilirsiniz."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Hızlı Eylemler"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox Kestirmeleri"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia Kestirmeleri"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Açık sekmelerinize kısayollar ekleyin."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Hızlı Görünüm"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox’ta ara"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia’ta ara"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Gizlide ara"; diff --git a/firefox-ios/Shared/tt.lproj/Default Browser.strings b/firefox-ios/Shared/tt.lproj/Default Browser.strings index 62633e50a22fa..05994df44ba6f 100644 --- a/firefox-ios/Shared/tt.lproj/Default Browser.strings +++ b/firefox-ios/Shared/tt.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox-ны төп браузерыгыз итү — ачык, кулланышлы интернетка тавыш бирү."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia-ны төп браузерыгыз итү — ачык, кулланышлы интернетка тавыш бирү."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Яхшырак Интернетка күчегез"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Ничеклеген белү"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Вебсайтлардан, эл. почталардан һәм хәбәрләрдән сылтамаларны автоматик рәвештә Firefox'та ачу."; +"DefaultBrowserCard.Description" = "Вебсайтлардан, эл. почталардан һәм хәбәрләрдән сылтамаларны автоматик рәвештә Ecosia'та ачу."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Тизлекне, иминлекны һәм хосусыйлыкны автоматлаштыру өчен стандарт браузер итеп Firefox-ны сайлагыз."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Тизлекне, иминлекны һәм хосусыйлыкны автоматлаштыру өчен стандарт браузер итеп Ecosia-ны сайлагыз."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Көндәлек гизүегезне яхшыртыгыз"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox уртача алганда ай саен бер кулланучыга 3000+ трекерны блоклый. Хосусыйлык тынычлыгы өчен безне төп браузерыгыз итегез."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia уртача алганда ай саен бер кулланучыга 3000+ трекерны блоклый. Хосусыйлык тынычлыгы өчен безне төп браузерыгыз итегез."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox хосусыйлыгыгызны саклый"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia хосусыйлыгыгызны саклый"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Башка браузерны төп браузер итү"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Төп браузер кушымтасына басыгыз"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox-ны сайлагыз"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia-ны сайлагыз"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Төп браузер кушымтасы"; diff --git a/firefox-ios/Shared/tt.lproj/Intro.strings b/firefox-ios/Shared/tt.lproj/Intro.strings index 354a2744bdb66..8c76f570c5ed0 100644 --- a/firefox-ios/Shared/tt.lproj/Intro.strings +++ b/firefox-ios/Shared/tt.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Керү"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox-ка рәхим итегез"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia-ка рәхим итегез"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Гизүне башлау"; diff --git a/firefox-ios/Shared/tt.lproj/Localizable.strings b/firefox-ios/Shared/tt.lproj/Localizable.strings index a8b7655bba6e1..afeb9b9c7e2ee 100644 --- a/firefox-ios/Shared/tt.lproj/Localizable.strings +++ b/firefox-ios/Shared/tt.lproj/Localizable.strings @@ -290,7 +290,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Бу хаталы көйләүләр яки һөҗүм итүченең явыз эшләре аркасында булырга мөмкин. Мөмкин булган рискны кабул итсәгез, дәвам итә аласыз."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ хуҗасы үз сайтын дөрес көйләмәгән. Мәгълүматыгызны урланудан саклау өчен, Firefox бу вебсайтка тоташмады."; +"ErrorPages.CertWarning.Description" = "%@ хуҗасы үз сайтын дөрес көйләмәгән. Мәгълүматыгызны урланудан саклау өчен, Ecosia бу вебсайтка тоташмады."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Бу бәйләнеш ышанычсыз"; @@ -311,7 +311,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox баш бит"; +"Firefox.HomePage.Title" = "Ecosia баш бит"; /* At the end of the Pocket section on the Firefox Homepage, this button appears and indicates tapping it will navigate the user to more Pocket Stories. */ "FirefoxHome.Pocket.DiscoverMore" = "Күбрәген күрү"; @@ -353,7 +353,7 @@ "Forward" = "Алга"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox Хисабы"; +"FxA.FirefoxAccount" = "Ecosia Хисабы"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Хисап һәм җиһазлар белән идарә итү"; @@ -365,7 +365,7 @@ "fxa.signin.camera-signin" = "Камерагыз ярдәмендә керегез"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Компьютерыгызда Firefox-ны ачыгыз һәм firefox.com/pair адресына күчегез"; +"fxa.signin.qr-link-instruction" = "Компьютерыгызда Ecosia-ны ачыгыз һәм ecosia.org/pair адресына күчегез"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Сканерларга әзер"; @@ -377,7 +377,7 @@ "FxA.SyncNow" = "Хәзер синхронлау"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox Синхронлау яңа җиһазга тоташтырылды: %@"; +"FxAPush_DeviceConnected_body" = "Ecosia Синхронлау яңа җиһазга тоташтырылды: %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Синхронлау тоташтырылды"; @@ -386,7 +386,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ уңышлы өзелде."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Бу җиһаз Firefox синхронлау хезмәтеннән өзелде."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Бу җиһаз Ecosia синхронлау хезмәтеннән өзелде."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Синхронлау тоташмаган"; @@ -395,10 +395,10 @@ "FxAPush_DeviceDisconnected_title" = "Синхронлау тоташмаган"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Җиһаз Firefox синхронлау хезмәтеннән өзелде"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Җиһаз Ecosia синхронлау хезмәтеннән өзелде"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox логотибы, фон рәсемне үзгәртү."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia логотибы, фон рәсемне үзгәртү."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Ярдәм"; @@ -431,7 +431,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Башка җиһазларыгыздагы таблар исемлеген карау өчен керегез."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Тарих"; @@ -614,7 +614,7 @@ "Logins" = "Логиннар"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox өчен автотутыру функциясен куллану өчен, җиһаз серсүзе кабынган булырга тиеш."; +"Logins.PasscodeRequirement.Warning" = "Ecosia өчен автотутыру функциясен куллану өчен, җиһаз серсүзе кабынган булырга тиеш."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Серсүзләрегезне үзегез белән алыгыз"; @@ -650,7 +650,7 @@ "LoginsList.Title" = "САКЛАНГАН ЛОГИННАР"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Моңарчы Firefox ватылып алды ахрысы. Табларыгыз яңадан торгызылсызмы?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Моңарчы Ecosia ватылып алды ахрысы. Табларыгыз яңадан торгызылсызмы?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Укылган дип билгеләү"; @@ -800,7 +800,7 @@ "Okay" = "Ярар"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Абау! Firefox ватылды"; +"Oops! Firefox crashed" = "Абау! Ecosia ватылды"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Мәкаләләрне уку режимында ачу өчен, исем юлында китап тамгачыгы пәйда булгач, аңа басыгыз."; @@ -812,7 +812,7 @@ "Open Tabs" = "Ачык таблар"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox бу битне ача алмый, чөнки аның адресы яраксыз."; +"OpenURL.Error.Message" = "Ecosia бу битне ача алмый, чөнки аның адресы яраксыз."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Битне ачып булмый"; @@ -824,7 +824,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Бу Сезгә рәсемне фото-галереягә сакларга мөмкинлек бирә."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox сурәтләрегезгә ирешергә тели"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia сурәтләрегезгә ирешергә тели"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Ябу"; @@ -902,7 +902,7 @@ "ScanQRCode.InvalidDataError.Message" = "Мәгълүмат яраксыз"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Җиһазда \"Көйләүләр\" > \"Firefox\"ка керегез. Firefox-ка камера куллануны рөхсәт итегез."; +"ScanQRCode.PermissionError.Message.v100" = "Җиһазда \"Көйләүләр\" > \"Ecosia\"ка керегез. Ecosia-ка камера куллануны рөхсәт итегез."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR кодны сканерлау"; @@ -914,7 +914,7 @@ "Search or enter address" = "Эзләү яки адрес язу"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox тәкъдиме"; +"Search.SuggestSectionTitle.v102" = "Ecosia тәкъдиме"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Яңа эзләү системасы тиз эзләү юлында күренәчәк."; @@ -950,7 +950,7 @@ "Search.ThirdPartyEngines.OK" = "ОК"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozilla проблеманы хәл итә алсын өчен, ватылу турында хәбәр җибәрелсенме?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosia проблеманы хәл итә алсын өчен, ватылу турында хәбәр җибәрелсенме?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Кире элемтә хәбәрен җибәрү"; @@ -980,13 +980,13 @@ "SendTo.NavBar.Title" = "Табны җибәрү"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Бу Firefox Хисабына бәйләнгән һәм синхронлауга әзер башка җиһазларыгыз юк."; +"SendTo.NoDevicesFound.Message" = "Бу Ecosia Хисабына бәйләнгән һәм синхронлауга әзер башка җиһазларыгыз юк."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Зинһар, дәвам итү өчен Firefox-ны ачып, көйләүләргә күчегез һәм керегез."; +"SendTo.NotSignedIn.Message" = "Зинһар, дәвам итү өчен Ecosia-ны ачып, көйләүләргә күчегез һәм керегез."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Сез Firefox Хисабыгызга кермәгәнсез."; +"SendTo.NotSignedIn.Title" = "Сез Ecosia Хисабыгызга кермәгәнсез."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Җибәрү"; @@ -1007,7 +1007,7 @@ "SentTab.NoTabArrivingNotification.body" = "Башлау өчен басыгыз"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Карап чыгу"; @@ -1064,7 +1064,7 @@ "Settings.DataManagement.Title" = "Мәгълүмат белән идарә итү"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox хисабыгыз белән синхронлауны туктатачак, ләкин бу җиһаздагы гизү мәгълүматларының берсен дә бетермәячәк."; +"Settings.Disconnect.Body" = "Ecosia хисабыгыз белән синхронлауны туктатачак, ләкин бу җиһаздагы гизү мәгълүматларының берсен дә бетермәячәк."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Синхронлауны өзү"; @@ -1130,7 +1130,7 @@ "Settings.FxA.Sync.SectionName" = "Синхронлау көйләүләре"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox Хисабы"; +"Settings.FxA.Title" = "Ecosia Хисабы"; /* General settings section title */ "Settings.General.SectionName" = "Гомуми"; @@ -1139,7 +1139,7 @@ "Settings.Home.Current.Description.v101" = "Баш бит итеп нәрсә күрсәтелүен сайлагыз."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox-ның баш битенә нәрсәләр керүен сайлагыз."; +"Settings.Home.Option.Description.v101" = "Ecosia-ның баш битенә нәрсәләр керүен сайлагыз."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Кире кайту"; @@ -1163,7 +1163,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Баш бит"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox-ка кире кайтканда нәрсә күрәчәгегезне сайлагыз."; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia-ка кире кайтканда нәрсә күрәчәгегезне сайлагыз."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Соңгы таб"; @@ -1178,25 +1178,25 @@ "Settings.Home.Option.Wallpaper" = "Фон рәсемe"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox фон рәсеме, аметист бизәге."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia фон рәсеме, аметист бизәге."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox фон рәсеме, пляж калкулыклары белән бизәк."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia фон рәсеме, пляж калкулыклары белән бизәк."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox фон рәсеме, зәңгәр күк бизәге."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia фон рәсеме, зәңгәр күк бизәге."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Стандарт ачык фон рәсеме."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox фон рәсеме, таң ату бизәге."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia фон рәсеме, таң ату бизәге."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Баш битнең фон рәсем циклын үзгәртү төймәсе"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox фон рәсеме, эңгер-меңгер вакытында калкулыклар бизәге."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia фон рәсеме, эңгер-меңгер вакытында калкулыклар бизәге."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Классик %@"; @@ -1217,7 +1217,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Чикләнгән тираж"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox баш бит логотибына басып фон рәсемен үзгәртү"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia баш бит логотибына басып фон рәсемен үзгәртү"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Фон рәсеме"; @@ -1271,7 +1271,7 @@ "Settings.NewTab.Option.Custom" = "Үзгә"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox баш бит"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia баш бит"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Баш бит"; @@ -1292,10 +1292,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Рәсемнәрне блоклау"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox ачылганда"; +"Settings.OfferClipboardBar.Status" = "Ecosia ачылганда"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox ачылганда"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia ачылганда"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Күчереп алынган сылтамаларны ачарга тәкъдим итү"; @@ -1331,7 +1331,7 @@ "Settings.SendUsage.Link" = "Күбрәк белү."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla, Firefox'ны һәркемгә дә тәкъдим итү һәм яхшырту өчен кирәкле мәгълүматларны гына җыярга тырыша."; +"Settings.SendUsage.Message" = "Ecosia, Ecosia'ны һәркемгә дә тәкъдим итү һәм яхшырту өчен кирәкле мәгълүматларны гына җыярга тырыша."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Куллану мәгълүматларын җибәрү"; @@ -1352,13 +1352,13 @@ "Settings.Siri.OpenTabShortcut" = "Яңа табны ачу"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Firefox-ны Siri аша тиз ачу өчен Siri ярлыкларын кулланыгыз"; +"Settings.Siri.SectionDescription" = "Ecosia-ны Siri аша тиз ачу өчен Siri ярлыкларын кулланыгыз"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri ярлыклары"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox ара-тирә тикшеренүләр урнаштырырга һәм үткәрергә мөмкин."; +"Settings.Studies.Toggle.Message" = "Ecosia ара-тирә тикшеренүләр урнаштырырга һәм үткәрергә мөмкин."; /* Ddescription that appears in the settings screen to explain what Firefox Sync is useful for. */ "Settings.Sync.ButtonDescription.v103" = "Табларны, кыстыргычларны, серсүзләрне һәм башкаларны синхронлау өчен сайтка керегез."; @@ -1472,13 +1472,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Фонда йөкләү"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox-та йөкләнә"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia-та йөкләнә"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox-та ачу"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia-та ачу"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox-та эзләү"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia-та эзләү"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Күчереп алу"; @@ -1610,7 +1610,7 @@ "UIMenuItem.PasteGo" = "Ябыштыру һәм күчү"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox белән эзләү"; +"UIMenuItem.SearchWithFirefox" = "Ecosia белән эзләү"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "укылмаган"; @@ -1658,7 +1658,7 @@ "Yesterday" = "Кичә"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Башка җиһазларыгыздагы Firefox-ларда ачык таблар юк."; +"You don’t have any tabs open in Firefox on your other devices." = "Башка җиһазларыгыздагы Ecosia-ларда ачык таблар юк."; /* Your Rights settings section title */ "Your Rights" = "Хокукларыгыз"; diff --git a/firefox-ios/Shared/tt.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/tt.lproj/PrivateBrowsing.strings index b26465e08bea0..88d10ded756e8 100644 --- a/firefox-ios/Shared/tt.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/tt.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Яңа хосусый табта ачу"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox сезнең гизү тарихыгызны яки кукиларны хәтерләмәячәк, ләкин яңа кыстыргычлар сакланачак."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia сезнең гизү тарихыгызны яки кукиларны хәтерләмәячәк, ләкин яңа кыстыргычлар сакланачак."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Күбрәк белү"; diff --git a/firefox-ios/Shared/tt.lproj/Today.strings b/firefox-ios/Shared/tt.lproj/Today.strings index daeeb8dd7c1db..7a48c5ebf50d1 100644 --- a/firefox-ios/Shared/tt.lproj/Today.strings +++ b/firefox-ios/Shared/tt.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Хосусый\nтабларны ябу"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Өй экранына Firefox ярлыкларын өстәгез."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Өй экранына Ecosia ярлыкларын өстәгез."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Копияләнгән сылтамага күчү"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Ачык таблар юк."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox-ны ачу"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia-ны ачу"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Хосусый эзләү"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Өй экранына Firefox ярлыгын өстәгез. Виджетны өстәгәннән соң, аны үзгәртү өчен өстенә басып торыгыз һәм бүтән бер ярлык сайлагыз."; +"TodayWidget.QuickActionGalleryDescription" = "Өй экранына Ecosia ярлыгын өстәгез. Виджетны өстәгәннән соң, аны үзгәртү өчен өстенә басып торыгыз һәм бүтән бер ярлык сайлагыз."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Тиз гамәлләр"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox ярлыклары"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia ярлыклары"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Ачык табларыгызга ярлыклар өстәгез."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Тиз карау"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox-та\nэзләү"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia-та\nэзләү"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Хосусый\nтабта эзләү"; diff --git a/firefox-ios/Shared/tzm.lproj/Localizable.strings b/firefox-ios/Shared/tzm.lproj/Localizable.strings index 27d0d56df072e..e32182ddf1b52 100644 --- a/firefox-ios/Shared/tzm.lproj/Localizable.strings +++ b/firefox-ios/Shared/tzm.lproj/Localizable.strings @@ -158,7 +158,7 @@ "FxA.RemoveAccountAlertTitle" = "ⵙⵉⵜⵜⵢ ⴰⵎⵉⴹⴰⵏ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "ⴳ ⵓⵎⵙⵙⵓⴷⵙ ⵏⵏⵓⵏ, ⵕⵥⵎⴰⵜ ⴼⴰⵢⵔⴼⵓⴽⵙ, ⵜⴷⴷⵓⵎ ⵖⵔ firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "ⴳ ⵓⵎⵙⵙⵓⴷⵙ ⵏⵏⵓⵏ, ⵕⵥⵎⴰⵜ ⴼⴰⵢⵔⴼⵓⴽⵙ, ⵜⴷⴷⵓⵎ ⵖⵔ Ecosia.com/pair"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ⵜⵉⵡⵉⵙⵉ"; diff --git a/firefox-ios/Shared/ug.lproj/Default Browser.strings b/firefox-ios/Shared/ug.lproj/Default Browser.strings index a58ce61d17bb1..0f8f40bcf8b6e 100644 --- a/firefox-ios/Shared/ug.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ug.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Firefox نى سۈكۈتتىكى توركۆرگۈچگە تەڭشەپ، ئوچۇق ۋە قولايلىق تور مۇھىتى يارىتىش ئۈچۈن بىر ھەسسە كۈچ قوشۇڭ."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Ecosia نى سۈكۈتتىكى توركۆرگۈچگە تەڭشەپ، ئوچۇق ۋە قولايلىق تور مۇھىتى يارىتىش ئۈچۈن بىر ھەسسە كۈچ قوشۇڭ."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "تېخىمۇ گۈزەل ئىنتېرنېت ئۈچۈن كۆڭۈلدىكىگە تەڭشەيدۇ"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "قانداق ئىشلىتىشنى ئۆگىنىش"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "تور بېكەت، ئېلخەت ۋە ئۇچۇر ئۇلانمىلىرىنى Firefox تا ئاپتوماتىك ئېچىشقا تەڭشەيدۇ."; +"DefaultBrowserCard.Description" = "تور بېكەت، ئېلخەت ۋە ئۇچۇر ئۇلانمىلىرىنى Ecosia تا ئاپتوماتىك ئېچىشقا تەڭشەيدۇ."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "سۈرئەت، بىخەتەرلىك ۋە مەخپىيەتلىكنى ئاپتوماتىك قىلىش ئۈچۈن Firefox نى كۆڭۈلدىكى توركۆرگۈڭىز قىلىپ تاللاڭ."; +"DefaultBrowserCard.NextLevel.Description.v108" = "سۈرئەت، بىخەتەرلىك ۋە مەخپىيەتلىكنى ئاپتوماتىك قىلىش ئۈچۈن Ecosia نى كۆڭۈلدىكى توركۆرگۈڭىز قىلىپ تاللاڭ."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "كۈندىلىك زىيارەتنى يۈكسەلتىدۇ"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox ھەر ئايدا ئوتتۇرا ھېساب بىلەن ھەر بىر ئىشلەتكۈچىگە 3000 دىن كۆپ ئىز قوغلىغۇچنى توسىدۇ. شەخسىي خاتىرجەملىك ئۈچۈن بىزنى كۆڭۈلدىكى توركۆرگۈڭىزگە ئايلاندۇرۇڭ."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia ھەر ئايدا ئوتتۇرا ھېساب بىلەن ھەر بىر ئىشلەتكۈچىگە 3000 دىن كۆپ ئىز قوغلىغۇچنى توسىدۇ. شەخسىي خاتىرجەملىك ئۈچۈن بىزنى كۆڭۈلدىكى توركۆرگۈڭىزگە ئايلاندۇرۇڭ."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox شەخسىيەتنى قوغدايدۇ"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia شەخسىيەتنى قوغدايدۇ"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "كۆڭۈلدىكى توركۆرگۈڭىزنى ئالماشتۇرۇڭ"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. كۆڭۈلدىكى توركۆرگۈ ئەپنى چېكىڭ"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox نى تاللاڭ"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia نى تاللاڭ"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "كۆڭۈلدىكى توركۆرگۈ ئەپى"; diff --git a/firefox-ios/Shared/ug.lproj/Intro.strings b/firefox-ios/Shared/ug.lproj/Intro.strings index 4a2068c599b42..658403b66908b 100644 --- a/firefox-ios/Shared/ug.lproj/Intro.strings +++ b/firefox-ios/Shared/ug.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "تىزىمغا كىرىڭ"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox غا خۇش كەپسىز"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia غا خۇش كەپسىز"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "زىيارەتنى باشلاڭ"; diff --git a/firefox-ios/Shared/ug.lproj/Localizable.strings b/firefox-ios/Shared/ug.lproj/Localizable.strings index ddcc5b7afd089..917aaac72adaa 100644 --- a/firefox-ios/Shared/ug.lproj/Localizable.strings +++ b/firefox-ios/Shared/ug.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "بۇ بەلكىم خاتا تەڭشەك ياكى ھۇجۇمچىنىڭ ھەرىكىتى سەۋەبىدىن بولۇشى مۇمكىن. ئەگەر يوشۇرۇن خەتەرنى قوبۇل قىلسىڭىز داۋاملاشتۇرۇڭ."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ نىڭ باشقۇرۇغۇچىسى تور بېكەتنى توغرا سەپلىمىگەن. ئۇچۇرلىرىڭىزنىڭ ئوغرىلىنىپ كېتىشىدىن ساقلىنىش ئۈچۈن، Firefox بۇ تور بېكەتكە باغلانمىدى."; +"ErrorPages.CertWarning.Description" = "%@ نىڭ باشقۇرۇغۇچىسى تور بېكەتنى توغرا سەپلىمىگەن. ئۇچۇرلىرىڭىزنىڭ ئوغرىلىنىپ كېتىشىدىن ساقلىنىش ئۈچۈن، Ecosia بۇ تور بېكەتكە باغلانمىدى."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "بۇ باغلىنىش ئىشەنچسىز"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox باش بېتى"; +"Firefox.HomePage.Title" = "Ecosia باش بېتى"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "باش بەتنى خاسلاشتۇر"; @@ -377,7 +377,7 @@ "Forward" = "ئالدىغا"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox ھېساباتى"; +"FxA.FirefoxAccount" = "Ecosia ھېساباتى"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "ھېسابات ۋە ئۈسكۈنىلەرنى باشقۇرۇش"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "كامېرايىڭىز بىلەن تىزىمغا كىرىڭ"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "كومپيۇتېرىڭىزدىكى Firefox دا firefox.com/pair نى ئاچىدۇ"; +"fxa.signin.qr-link-instruction" = "كومپيۇتېرىڭىزدىكى Ecosia دا ecosia.org/pair نى ئاچىدۇ"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "تاراشقا تەييار"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "ھازىر قەدەمداشلا"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox قەدەمداش %@ غا باغلاندى"; +"FxAPush_DeviceConnected_body" = "Ecosia قەدەمداش %@ غا باغلاندى"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "قەدەمداش باغلاندى"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ مۇۋەپپەقىيەتلىك ئۈزۈلدى."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "بۇ ئۈسكۈنە Firefox قەدەمداشتىن مۇۋەپپەقىيەتلىك ئۈزۈلدى."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "بۇ ئۈسكۈنە Ecosia قەدەمداشتىن مۇۋەپپەقىيەتلىك ئۈزۈلدى."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "قەدەمداش ئۈزۈلدى"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "قەدەمداش ئۈزۈلدى"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "بىر ئۈسكۈنە Firefox Sync قەدەمداشتىن ئۈزۈلدى"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "بىر ئۈسكۈنە Ecosia Sync قەدەمداشتىن ئۈزۈلدى"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox تۇغى، تام قەغىزىنى ئۆزگەرتىدۇ."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia تۇغى، تام قەغىزىنى ئۆزگەرتىدۇ."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "ياردەم"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "باشقا ئۈسكۈنىلىرىڭىزدىكى بەتكۈچ تىزىمىنى كۆرۈش ئۈچۈن تىزىمغا كىرىڭ."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox قەدەمداش"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia قەدەمداش"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "تارىخ"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "تەپسىلاتى"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Firefox نىڭ ئۆزلۈكىدىن تولدۇرۇش ئىقتىدارىنى ئىشلىتىش ئۈچۈن، چوقۇم ئۈسكۈنىنىڭ ئىم كودىنى قوزغىتىشىڭىز كېرەك."; +"Logins.PasscodeRequirement.Warning" = "Ecosia نىڭ ئۆزلۈكىدىن تولدۇرۇش ئىقتىدارىنى ئىشلىتىش ئۈچۈن، چوقۇم ئۈسكۈنىنىڭ ئىم كودىنى قوزغىتىشىڭىز كېرەك."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "ئېلىپ يۈرۈشكە ئەپلىك ئىم"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Firefox ئىمنى ئۆزلۈكىدىن تولدۇر"; +"Logins.WelcomeView.Title2" = "Ecosia ئىمنى ئۆزلۈكىدىن تولدۇر"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "ئۆزلۈكىدىن تولدۇرنى ئاچ"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ساقلانغان تىزىمغا كىرىش"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "قارىغاندا Firefox ئالدىنقى قېتىم يىمىرىلگەن ئوخشايدۇ. بەتكۈچلىرىڭىزنى ئەسلىگە كەلتۈرەمسىز؟"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "قارىغاندا Ecosia ئالدىنقى قېتىم يىمىرىلگەن ئوخشايدۇ. بەتكۈچلىرىڭىزنى ئەسلىگە كەلتۈرەمسىز؟"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "ئوقۇلغان دەپ بەلگىلەش"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "زىيارەتنى باشلاڭ"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "ئاپلا! Firefox يىمىرىلدى"; +"Oops! Firefox crashed" = "ئاپلا! Ecosia يىمىرىلدى"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "ماۋزۇ بالداقتا كىتاب سىنبەلگىسى كۆرۈنگەندە ئۇنى چېكىپ ئوقۇش كۆرۈنۈشىدە ماقالىنى ئاچالايسىز."; @@ -905,7 +905,7 @@ "Open Tabs" = "بەتكۈچ ئاچ"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox بۇ بەتنى ئاچالمايدۇ، چۈنكى ئادرېس ئىناۋەتسىز."; +"OpenURL.Error.Message" = "Ecosia بۇ بەتنى ئاچالمايدۇ، چۈنكى ئادرېس ئىناۋەتسىز."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "بەتنى ئاچقىلى بولمايدۇ"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "بۇ ئارقىلىق سۈرەتنى كامېرا لىنتىسىغا ساقلىشىڭىزغا يول قويىدۇ."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox سىزنىڭ سۈرەتلىرىڭىزنى زىيارەت قىلماقچى"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia سىزنىڭ سۈرەتلىرىڭىزنى زىيارەت قىلماقچى"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "تاقا"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "سانلىق مەلۇمات ئىناۋەتسىز"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "ئۈسكۈنىنىڭ «تەڭشەكلەر»›«Firefox» قا يۆتكىلىڭ. Firefox نىڭ كامېرانى زىيارەت قىلىشىغا يول قويۇڭ."; +"ScanQRCode.PermissionError.Message.v100" = "ئۈسكۈنىنىڭ «تەڭشەكلەر»›«Ecosia» قا يۆتكىلىڭ. Ecosia نىڭ كامېرانى زىيارەت قىلىشىغا يول قويۇڭ."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "QR كودىنى تارا"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "بەتكۈچكە ئالماشتۇرۇش"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox تەكلىپى"; +"Search.SuggestSectionTitle.v102" = "Ecosia تەكلىپى"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "يېڭى ئىزدەش موتورى تېز ئىزدەش بالداقتا كۆرۈنىدۇ."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "جەزملە"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "مەسىلىنى ئوڭشاش ئۈچۈن Mozilla غا يىمىرىلىش دوكلاتى يوللامدۇ؟"; +"Send a crash report so Mozilla can fix the problem?" = "مەسىلىنى ئوڭشاش ئۈچۈن Ecosia غا يىمىرىلىش دوكلاتى يوللامدۇ؟"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "قايتۇرما ئىنكاس يوللا"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "بەتكۈچ يوللا"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "بۇ Firefox ھېساباتىغا باغلانغان قەدەمداشلىنىدىغان باشقا ئۈسكۈنە يوق."; +"SendTo.NoDevicesFound.Message" = "بۇ Ecosia ھېساباتىغا باغلانغان قەدەمداشلىنىدىغان باشقا ئۈسكۈنە يوق."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Firefox نى ئېچىپ، تەڭشەككە يۆتكىلىپ ئاندىن داۋاملاشتۇرۇش ئۈچۈن تىزىمغا كىرىڭ."; +"SendTo.NotSignedIn.Message" = "Ecosia نى ئېچىپ، تەڭشەككە يۆتكىلىپ ئاندىن داۋاملاشتۇرۇش ئۈچۈن تىزىمغا كىرىڭ."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "سىز تىخى Firefox ھېساباتىڭىزغا كىرمىدىڭىز."; +"SendTo.NotSignedIn.Title" = "سىز تىخى Ecosia ھېساباتىڭىزغا كىرمىدىڭىز."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "يوللا"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "بۇ يەرنى چېكىپ باشلاڭ"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox قەدەمداش"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia قەدەمداش"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "كۆرۈنۈش"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "سانلىق مەلۇمات باشقۇرۇش"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox ھېساباتىڭىز بىلەن قەدەمداشلاشنى توختىتىدۇ، ئەمما بۇ ئۈسكۈنىدىكى توركۆرگۈ سانلىق مەلۇماتلىرىڭىزنى ئۆچۈرمەيدۇ."; +"Settings.Disconnect.Body" = "Ecosia ھېساباتىڭىز بىلەن قەدەمداشلاشنى توختىتىدۇ، ئەمما بۇ ئۈسكۈنىدىكى توركۆرگۈ سانلىق مەلۇماتلىرىڭىزنى ئۆچۈرمەيدۇ."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "قەدەمداشنى ئۈز"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "قەدەمداش تەڭشەكلەر"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox ھېساباتى"; +"Settings.FxA.Title" = "Ecosia ھېساباتى"; /* General settings section title */ "Settings.General.SectionName" = "ئادەتتىكى"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "باش بەتتە نېمە كۆرسىتىدىغانلىقى تاللىنىدۇ."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Firefox نىڭ باش بېتى نېمىنى ئۆز ئىچىگە ئالىدىغانلىقى تاللىنىدۇ."; +"Settings.Home.Option.Description.v101" = "Ecosia نىڭ باش بېتى نېمىنى ئۆز ئىچىگە ئالىدىغانلىقى تاللىنىدۇ."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "ئالدىنقى بەتكە قايت"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "باش بەت"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Firefox قا قايتقاندا كۆرۈنىدىغان مەزمۇن تاللىنىدۇ."; +"Settings.Home.Option.StartAtHome.Description" = "Ecosia قا قايتقاندا كۆرۈنىدىغان مەزمۇن تاللىنىدۇ."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "ئاخىرقى بەتكۈچ"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "تام قەغىزى"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox تام قەغىزى ، ئامېتىست ئەندىزىسى."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia تام قەغىزى ، ئامېتىست ئەندىزىسى."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox تام قەغىزى ، ساھىل تاغ ئەندىزىسى."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia تام قەغىزى ، ساھىل تاغ ئەندىزىسى."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox تام قەغىزى، ھاۋا رەڭ ئەندىزە."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia تام قەغىزى، ھاۋا رەڭ ئەندىزە."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "كۆڭۈلدىكى يورۇق تام قەغەز."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox تام قەغىزى، كۈن چىقىش ئەندىزە."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia تام قەغىزى، كۈن چىقىش ئەندىزە."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "باش بەت تام قەغىزىنى ئالماشتۇرۇش ئۈزچاتى"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox تام قەغىزى ، شەپەقتىكى تاغ ئەندىزىسى."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia تام قەغىزى ، شەپەقتىكى تاغ ئەندىزىسى."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "ئەنئەنىۋى %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "چەكلىك نەشرى"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Firefox نىڭ باش بەت تۇغىنى چېكىپ تام قەغىزىنى ئۆزگەرتكىلى بولىدۇ"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Ecosia نىڭ باش بەت تۇغىنى چېكىپ تام قەغىزىنى ئۆزگەرتكىلى بولىدۇ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "تام قەغىزى"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "ئىختىيارىچە"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox باش بەت"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia باش بەت"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "باش بەت"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "سۈرەتنى توس"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox نى ئاچقاندا"; +"Settings.OfferClipboardBar.Status" = "Ecosia نى ئاچقاندا"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "%@ نى ئاچقاندا"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox نى ئاچقاندا"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia نى ئاچقاندا"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "كۆچۈرۈلگەن ئۇلانمىنى ئېچىشقا تەمىنلەيدۇ"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "تەپسىلاتى."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla پەقەت كۆپچىلىككە تەمىنلەيدىغان ۋە Firefox نى ياخشىلاش ئۈچۈن زۆرۈر بولغان ئۇچۇرلارنىلا توپلايدۇ."; +"Settings.SendUsage.Message" = "Ecosia پەقەت كۆپچىلىككە تەمىنلەيدىغان ۋە Ecosia نى ياخشىلاش ئۈچۈن زۆرۈر بولغان ئۇچۇرلارنىلا توپلايدۇ."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "ئىشلىتىش سانلىق مەلۇماتلىرىنى يوللايدۇ"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "يېڭى بەتكۈچ ئاچ"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Siri تېزلەتمىسىنى ئىشلىتىپ Siri ئارقىلىق Firefox نى تېز ئاچىدۇ"; +"Settings.Siri.SectionDescription" = "Siri تېزلەتمىسىنى ئىشلىتىپ Siri ئارقىلىق Ecosia نى تېز ئاچىدۇ"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri تېزلەتمىسى"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "تەپسىلاتى."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox پات-پات تەتقىقاتلارنى ئورنىتىپ ئىجرا قىلالايدۇ."; +"Settings.Studies.Toggle.Message" = "Ecosia پات-پات تەتقىقاتلارنى ئورنىتىپ ئىجرا قىلالايدۇ."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "تەتقىقات"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "ئارقا سۇپىدا يۈكلە"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox تا يۈكلەۋاتىدۇ"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia تا يۈكلەۋاتىدۇ"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox تا ئاچ"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia تا ئاچ"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox تا ئىزدە"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia تا ئىزدە"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "كۆچۈر"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "چاپلاپ يۆتكەل"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox تا ئىزدە"; +"UIMenuItem.SearchWithFirefox" = "Ecosia تا ئىزدە"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "ئوقۇلمىغان"; @@ -1793,7 +1793,7 @@ "Yesterday" = "تۈنۈگۈن"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "باشقا ئۈسكۈنىڭىزدە Firefox دا ھېچقانداق بەتكۈچ ئاچمىغان."; +"You don’t have any tabs open in Firefox on your other devices." = "باشقا ئۈسكۈنىڭىزدە Ecosia دا ھېچقانداق بەتكۈچ ئاچمىغان."; /* Your Rights settings section title */ "Your Rights" = "ھوقۇقىڭىز"; diff --git a/firefox-ios/Shared/ug.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ug.lproj/PrivateBrowsing.strings index c0a703486fcda..6c58886578ee2 100644 --- a/firefox-ios/Shared/ug.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ug.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "يېڭى شەخسىي بەتكۈچ ئاچ"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox سىزنىڭ زىيارەت تارىخىڭىز ياكى ساقلانمىڭىزنى ئەستە تۇتمايدۇ، ئەمما خەتكۈچلىرىڭىزنى ساقلايدۇ."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia سىزنىڭ زىيارەت تارىخىڭىز ياكى ساقلانمىڭىزنى ئەستە تۇتمايدۇ، ئەمما خەتكۈچلىرىڭىزنى ساقلايدۇ."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "تەپسىلاتى"; diff --git a/firefox-ios/Shared/ug.lproj/Today.strings b/firefox-ios/Shared/ug.lproj/Today.strings index 41ff5671a6bd0..7c9facc29ec44 100644 --- a/firefox-ios/Shared/ug.lproj/Today.strings +++ b/firefox-ios/Shared/ug.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "شەخسىي \nبەتكۈچنى ياپ"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "باش بېتىڭىزگە Firefox تېزلەتمىسىنى قوشىدۇ."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "باش بېتىڭىزگە Ecosia تېزلەتمىسىنى قوشىدۇ."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "كۆچۈرۈلگەن ئۇلانمىغا يۆتكەل"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "ئوچۇق بەتكۈچ يوق."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox نى ئاچ"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia نى ئاچ"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "شەخسىي ئىزدە"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "باش بېتىڭىزگە Firefox تېزلەتمىسىنى قوشىدۇ. ئەپچە قوشقاندىن كېيىن، بېسىپ تۇرسا ئۇنى تەھرىرلەيدۇ ۋە باشقا تېزلەتمە تاللىغىلى بولىدۇ."; +"TodayWidget.QuickActionGalleryDescription" = "باش بېتىڭىزگە Ecosia تېزلەتمىسىنى قوشىدۇ. ئەپچە قوشقاندىن كېيىن، بېسىپ تۇرسا ئۇنى تەھرىرلەيدۇ ۋە باشقا تېزلەتمە تاللىغىلى بولىدۇ."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "تېز مەشغۇلات"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox تېزلەتمىسى"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia تېزلەتمىسى"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "ئوچۇق بەتكۈچكە تېزلەتمە قوشىدۇ."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "تېز كۆرۈش"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox تا\nئىزدە"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia تا\nئىزدە"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "شەخسىي \nبەتكۈچتە ئىزدە"; diff --git a/firefox-ios/Shared/uk.lproj/Default Browser.strings b/firefox-ios/Shared/uk.lproj/Default Browser.strings index 98f69cf94a5b7..70d9944764253 100644 --- a/firefox-ios/Shared/uk.lproj/Default Browser.strings +++ b/firefox-ios/Shared/uk.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Зробивши Firefox типовим браузером, ви голосуєте за відкритий, доступний інтернет."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Зробивши Ecosia типовим браузером, ви голосуєте за відкритий, доступний інтернет."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Перейдіть до усталено кращого інтернету"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Навчитися"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Відкривайте посилання з пошти й повідомлень автоматично в Firefox."; +"DefaultBrowserCard.Description" = "Відкривайте посилання з пошти й повідомлень автоматично в Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Оберіть Firefox типовим браузером, щоб швидкість, безпека та приватність стали автоматичними."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Оберіть Ecosia типовим браузером, щоб швидкість, безпека та приватність стали автоматичними."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Поліпшуйте повсякденний вебперегляд"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox блокує в середньому понад 3000 елементів стеження на користувача щомісяця. Зробіть наш браузер типовим, щоб бути впевненими у своїй приватності."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia блокує в середньому понад 3000 елементів стеження на користувача щомісяця. Зробіть наш браузер типовим, щоб бути впевненими у своїй приватності."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox захищає приватність"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia захищає приватність"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Змініть свій типовий браузер"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Торкніться Типовий браузер"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Виберіть Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Виберіть Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Типовий браузер"; diff --git a/firefox-ios/Shared/uk.lproj/Intro.strings b/firefox-ios/Shared/uk.lproj/Intro.strings index d7fc8961d9661..cad310bcf38b4 100644 --- a/firefox-ios/Shared/uk.lproj/Intro.strings +++ b/firefox-ios/Shared/uk.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Увійти"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Вітаємо у Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Вітаємо у Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Почати перегляд"; diff --git a/firefox-ios/Shared/uk.lproj/Localizable.strings b/firefox-ios/Shared/uk.lproj/Localizable.strings index 5cc610a6fd2c2..70df81379c31b 100644 --- a/firefox-ios/Shared/uk.lproj/Localizable.strings +++ b/firefox-ios/Shared/uk.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Це може бути через неправильну конфігурацію або атаку зловмисника. Продовжуйте, якщо ви усвідомлюєте потенційний ризик."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Власник %@ налаштував свій вебсайт неналежним чином. Для захисту вашої інформації від викрадання, Firefox не з'єднався з цим вебсайтом."; +"ErrorPages.CertWarning.Description" = "Власник %@ налаштував свій вебсайт неналежним чином. Для захисту вашої інформації від викрадання, Ecosia не з'єднався з цим вебсайтом."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Це недовірене з'єднання"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Домівка Firefox"; +"Firefox.HomePage.Title" = "Домівка Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Налаштувати домівку"; @@ -377,7 +377,7 @@ "Forward" = "Вперед"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Обліковий запис Firefox"; +"FxA.FirefoxAccount" = "Обліковий запис Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Керувати обліковим записом і пристроями"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Увійдіть за допомогою камери"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "На своєму комп'ютері відкрийте Firefox і перейдіть до firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "На своєму комп'ютері відкрийте Ecosia і перейдіть до ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Сканувати"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Синхронізувати зараз"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Синхронізація Firefox під'єднана до %@"; +"FxAPush_DeviceConnected_body" = "Синхронізація Ecosia під'єднана до %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Синхронізацію під'єднано"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ було успішно від'єднано."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Пристрій успішно від'єднаний від синхронізації Firefox."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Пристрій успішно від'єднаний від синхронізації Ecosia."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Синхронізацію від'єднано"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Синхронізацію від'єднано"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Пристрій від'єднано від синхронізації Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Пристрій від'єднано від синхронізації Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Логотип Firefox, змінити шпалери."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Логотип Ecosia, змінити шпалери."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Довідка"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Увійдіть, щоб переглядати список вкладок з інших ваших пристроїв."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Синхронізація Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Синхронізація Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Історія"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Докладніше"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Щоб використовувати функцію автозаповнення Firefox, увімкніть пароль пристрою."; +"Logins.PasscodeRequirement.Warning" = "Щоб використовувати функцію автозаповнення Ecosia, увімкніть пароль пристрою."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Майте доступ до своїх паролів всюди"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Автозаповнення паролів Firefox"; +"Logins.WelcomeView.Title2" = "Автозаповнення паролів Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Увімкнути автозаповнення"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "ЗБЕРЕЖЕНІ ПАРОЛІ"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Схоже, минулого разу Firefox аварійно завершив роботу. Бажаєте відновити свої вкладки?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Схоже, минулого разу Ecosia аварійно завершив роботу. Бажаєте відновити свої вкладки?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Позначити прочитаним"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Почати перегляд"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Отакої! Стався збій Firefox"; +"Oops! Firefox crashed" = "Отакої! Стався збій Ecosia"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Відкривайте статті в режимі читання дотиком до піктограми книги, коли вона з'являється в панелі заголовка."; @@ -905,7 +905,7 @@ "Open Tabs" = "Відкриті вкладки"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox не може відкрити сторінку, тому що її адреса неправильна."; +"OpenURL.Error.Message" = "Ecosia не може відкрити сторінку, тому що її адреса неправильна."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Не вдалося відкрити сторінку"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Це дозволяє вам зберігати зображення до своєї фотоплівки."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox бажає отримати доступ до ваших фотографій"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia бажає отримати доступ до ваших фотографій"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Закрити"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Неприпустимі дані"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Відкрийте «Параметри» пристрою > «Firefox». Дозвольте Firefox отримати доступ до камери."; +"ScanQRCode.PermissionError.Message.v100" = "Відкрийте «Параметри» пристрою > «Ecosia». Дозвольте Ecosia отримати доступ до камери."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Сканувати QR-код"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Перейти на вкладку"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Пропозиції Firefox"; +"Search.SuggestSectionTitle.v102" = "Пропозиції Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Нова пошукова система з'явиться на панелі швидкого пошуку."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Надіслати звіт про збій, щоб Mozilla змогла виправити цю проблему?"; +"Send a crash report so Mozilla can fix the problem?" = "Надіслати звіт про збій, щоб Ecosia змогла виправити цю проблему?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Надіслати відгук"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Надіслати вкладку"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "У вас немає інших доступних для синхронізації пристроїв, підключених до цього облікового запису Firefox."; +"SendTo.NoDevicesFound.Message" = "У вас немає інших доступних для синхронізації пристроїв, підключених до цього облікового запису Ecosia."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Щоб продовжити, відкрийте Firefox, перейдіть в Параметри та виконайте вхід."; +"SendTo.NotSignedIn.Message" = "Щоб продовжити, відкрийте Ecosia, перейдіть в Параметри та виконайте вхід."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Ви не увійшли в свій обліковий запис Firefox."; +"SendTo.NotSignedIn.Title" = "Ви не увійшли в свій обліковий запис Ecosia."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Надіслати"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Торкніться, щоб почати"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Синхронізація Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Синхронізація Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Вигляд"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Керування даними"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox припинить синхронізацію з вашим обліковим записом, але не вилучить жодних даних перегляду на цьому пристрої."; +"Settings.Disconnect.Body" = "Ecosia припинить синхронізацію з вашим обліковим записом, але не вилучить жодних даних перегляду на цьому пристрої."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Від'єднатися"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Параметри синхронізації"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Обліковий запис Firefox"; +"Settings.FxA.Title" = "Обліковий запис Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Загальні"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Виберіть, що показується у домівці."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Виберіть, що включатиме домівка Firefox."; +"Settings.Home.Option.Description.v101" = "Виберіть, що включатиме домівка Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Відкрити знову"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Домівка"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Оберіть, що показувати при поверненні в Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Оберіть, що показувати при поверненні в Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Остання вкладка"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Шпалери"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Шпалери Firefox, аметистовий візерунок."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Шпалери Ecosia, аметистовий візерунок."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Шпалери Firefox, візерунок з пляжними пагорбами."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Шпалери Ecosia, візерунок з пляжними пагорбами."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Шпалери Firefox, блакитний візерунок."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Шпалери Ecosia, блакитний візерунок."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Типові шпалери."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Шпалери Firefox, візерунок світанку."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Шпалери Ecosia, візерунок світанку."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Перемикач циклу шпалер домівки"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Шпалери Firefox, візерунок пагорбів у сутінках."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Шпалери Ecosia, візерунок пагорбів у сутінках."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Класичний %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Обмежений випуск"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Змінювати шпалери торканням логотипу домівки Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Змінювати шпалери торканням логотипу домівки Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Шпалери"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Власна"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Домівка Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Домівка Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Домівка"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Блокувати зображення"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "При запуску Firefox"; +"Settings.OfferClipboardBar.Status" = "При запуску Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Під час відкриття %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "При запуску Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "При запуску Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Пропонувати відкриття посилань"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Докладніше."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla намагається збирати лише дані, необхідні для роботи й вдосконалення Firefox."; +"Settings.SendUsage.Message" = "Ecosia намагається збирати лише дані, необхідні для роботи й вдосконалення Ecosia."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Надсилати дані про використання"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Нова вкладка"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Використовуйте команди Siri, щоб швидко відкривати Firefox через Siri"; +"Settings.Siri.SectionDescription" = "Використовуйте команди Siri, щоб швидко відкривати Ecosia через Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Команди Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Докладніше."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox може час від часу встановлювати та виконувати дослідження."; +"Settings.Studies.Toggle.Message" = "Ecosia може час від часу встановлювати та виконувати дослідження."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Дослідження"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Завантажити на задньому плані"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Завантаження в Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Завантаження в Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Відкрити в Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Відкрити в Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Шукати в Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Шукати в Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Копіювати"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Вставити і перейти"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Шукати з Firefox"; +"UIMenuItem.SearchWithFirefox" = "Шукати з Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "непрочитане"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Вчора"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "У вас немає вкладок, відкритих в Firefox на інших пристроях."; +"You don’t have any tabs open in Firefox on your other devices." = "У вас немає вкладок, відкритих в Ecosia на інших пристроях."; /* Your Rights settings section title */ "Your Rights" = "Ваші права"; diff --git a/firefox-ios/Shared/uk.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/uk.lproj/PrivateBrowsing.strings index a86d0217fe30d..8cebef4a9ee5d 100644 --- a/firefox-ios/Shared/uk.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/uk.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Відкрити в приватній вкладці"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox не пам'ятатиме історію чи файли cookie, але нові закладки будуть збережені."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia не пам'ятатиме історію чи файли cookie, але нові закладки будуть збережені."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Докладніше"; diff --git a/firefox-ios/Shared/uk.lproj/Today.strings b/firefox-ios/Shared/uk.lproj/Today.strings index 74b53cf6124d2..d676289054f9e 100644 --- a/firefox-ios/Shared/uk.lproj/Today.strings +++ b/firefox-ios/Shared/uk.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Закрити приватні вкладки"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Додайте ярлики Firefox на головний екран."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Додайте ярлики Ecosia на головний екран."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Відкрити скопійоване посилання"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Немає відкритих вкладок."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Відкрити Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Відкрити Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Приватний пошук"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Додайте ярлик Firefox на головний екран. Після додавання віджета торкніться й утримуйте, щоб змінити його та вибрати інший ярлик."; +"TodayWidget.QuickActionGalleryDescription" = "Додайте ярлик Ecosia на головний екран. Після додавання віджета торкніться й утримуйте, щоб змінити його та вибрати інший ярлик."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Швидкі дії"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Ярлики Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ярлики Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Додайте ярлики для відкритих вкладок."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Швидкий перегляд"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Шукати в Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Шукати в Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Шукати у приватній вкладці"; diff --git a/firefox-ios/Shared/ur.lproj/Default Browser.strings b/firefox-ios/Shared/ur.lproj/Default Browser.strings index a7c579504db34..768a8ac09773e 100644 --- a/firefox-ios/Shared/ur.lproj/Default Browser.strings +++ b/firefox-ios/Shared/ur.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "کیسے سیکھیں "; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "ویب سائٹ، ای میل اور میسج کے لنک Firefox میں خود بخود کھلنے کے لئے سیٹ کریں۔"; +"DefaultBrowserCard.Description" = "ویب سائٹ، ای میل اور میسج کے لنک Ecosia میں خود بخود کھلنے کے لئے سیٹ کریں۔"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "اپنا طے شدہ براؤزر سوئچ کریں"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. طے شدہ براؤزر ایپ کو ٹیپ کریں"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefox کو منتخب کریں"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosia کو منتخب کریں"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "طے شدہ براؤزر ایپ"; diff --git a/firefox-ios/Shared/ur.lproj/Intro.strings b/firefox-ios/Shared/ur.lproj/Intro.strings index ca5f33c1ac37c..11bdb8b90982c 100644 --- a/firefox-ios/Shared/ur.lproj/Intro.strings +++ b/firefox-ios/Shared/ur.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "سائن ان کریں"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Firefox میں خوش آمدید"; +"Intro.Slides.Welcome.Title.v2" = "Ecosia میں خوش آمدید"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "براؤزنگ کا آغاز کریں"; diff --git a/firefox-ios/Shared/ur.lproj/Localizable.strings b/firefox-ios/Shared/ur.lproj/Localizable.strings index 2e39f7ec24c47..9a9c7ca5fed9e 100644 --- a/firefox-ios/Shared/ur.lproj/Localizable.strings +++ b/firefox-ios/Shared/ur.lproj/Localizable.strings @@ -305,7 +305,7 @@ "ErrorPages.AdvancedWarning2.Text" = "یہ ایک حملہ آور کی طرف سے ایک غلط کنفیگریشن یا چھیڑنا ہو سکتا ہے. اگر آپ ممکنہ خطرے کو قبول کرتے ہیں تو آگے بڑھیں۔"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ کے مالک نے انکی ویب سائٹ نامناسب طرحہ سے تشکیل کی ہیں۔ اپنی معلومات کو چوری ہونے سے حفاظت کے لئے، Firefox نے اس ویب سائٹ سے نہیں جڑا ہے۔"; +"ErrorPages.CertWarning.Description" = "%@ کے مالک نے انکی ویب سائٹ نامناسب طرحہ سے تشکیل کی ہیں۔ اپنی معلومات کو چوری ہونے سے حفاظت کے لئے، Ecosia نے اس ویب سائٹ سے نہیں جڑا ہے۔"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "اس کنکشن کا بھروسا نہیں ہے"; @@ -326,7 +326,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox کا ابتائی صفحہ"; +"Firefox.HomePage.Title" = "Ecosia کا ابتائی صفحہ"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "ابتدائی صفحہ کی تخصیص کریں"; @@ -359,7 +359,7 @@ "Forward" = "آگے"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox اکاؤنٹ"; +"FxA.FirefoxAccount" = "Ecosia اکاؤنٹ"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "اکاؤنٹ اور آلات کا بندرست کریں"; @@ -371,7 +371,7 @@ "fxa.signin.camera-signin" = "اپنے کیمرے کے ساتھ سائن ان کریں"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "اپنے کمپیوٹر پر Firefox کھولیں اور firefox.com/pair پر جائیں"; +"fxa.signin.qr-link-instruction" = "اپنے کمپیوٹر پر Ecosia کھولیں اور ecosia.org/pair پر جائیں"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "اسکین کرنے کے لئے تیار"; @@ -383,7 +383,7 @@ "FxA.SyncNow" = "ابھی ہمہ وقت سازی کریں"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox ہمہ وقت سازی %@ سے جڑِی ہے"; +"FxAPush_DeviceConnected_body" = "Ecosia ہمہ وقت سازی %@ سے جڑِی ہے"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "ہمہ وقت سازی جڑ گئی"; @@ -392,7 +392,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ کو کامیابی سے منقطع کر دیا گیا ہے۔"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "اس آلہ کو Firefox ہمہ وقت سازی سے کامیابی سے منقیع کر دیا گیا ہے۔"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "اس آلہ کو Ecosia ہمہ وقت سازی سے کامیابی سے منقیع کر دیا گیا ہے۔"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "ہم وقت سازی منقطع کر دی گئی"; @@ -401,10 +401,10 @@ "FxAPush_DeviceDisconnected_title" = "ہمہ وقت سازی منقطع ہو گئی"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ایک آلہ Firefoxہمہ وقت سازی سے منقطع ہوا ہے"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "ایک آلہ Ecosiaہمہ وقت سازی سے منقطع ہوا ہے"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox لوگو، وال پیپر تبدیل کریں۔"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia لوگو، وال پیپر تبدیل کریں۔"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "مدد"; @@ -437,7 +437,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "ٹیبیں کی فہرست دیکھنے کے لئے اپنے دیگر آلات سے سائن ان کریں۔"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "سابقات"; @@ -671,7 +671,7 @@ "LoginsList.Title" = "SAVED LOGINS"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ایسا لگتا ہے Firefox پہلے تباہ ہوا ہے۔ کیا آپ اپنے ٹیب بحال کرنا چاہیں گے؟"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "ایسا لگتا ہے Ecosia پہلے تباہ ہوا ہے۔ کیا آپ اپنے ٹیب بحال کرنا چاہیں گے؟"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "بطور مطالعہ شدہ نشان زد کریں"; @@ -827,7 +827,7 @@ "Onboarding.WhatsNew.Button.Title" = "براؤزنگ شروع کریں"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "اف! Firefox تباہ ہوگیا"; +"Oops! Firefox crashed" = "اف! Ecosia تباہ ہوگیا"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "عبارات کو پڑھنے کا نظارے میں تھابپ کر جب کتاب کا آئکن عنوان بار پر نظر آئے۔"; @@ -839,7 +839,7 @@ "Open Tabs" = "ٹیب کھولیں"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox اس صفحہ کو نہیں کھول سکتا کونکہ اسکا پتہ ناجائز ہے۔"; +"OpenURL.Error.Message" = "Ecosia اس صفحہ کو نہیں کھول سکتا کونکہ اسکا پتہ ناجائز ہے۔"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "صفحہ نہیں کھول سکتا"; @@ -851,7 +851,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "یہ آپ کی تصاویر کو اپنے کیمرے رول پر محفوظ کرنے کی اجازت دیتا ہے۔"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox آپ کو تصاویر تکرسائی حاصل کرنا چاہے گا"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia آپ کو تصاویر تکرسائی حاصل کرنا چاہے گا"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "بند کریں"; @@ -977,7 +977,7 @@ "Search.ThirdPartyEngines.OK" = "ٹھیک ہے"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "ایک تباہ رپورٹ ارسال کریں تاکہ Mozilla مسلہ کو جل کرسکے؟"; +"Send a crash report so Mozilla can fix the problem?" = "ایک تباہ رپورٹ ارسال کریں تاکہ Ecosia مسلہ کو جل کرسکے؟"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "جواب الجواب ارسال کریں"; @@ -1007,13 +1007,13 @@ "SendTo.NavBar.Title" = "ٹیب ارسال کریں"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "آپ کے کوئی بھی دیگر آلات ہمہ وقت سازی پر موجود Firefox اکائونٹ سے جڑے نہیں ہوئے۔"; +"SendTo.NoDevicesFound.Message" = "آپ کے کوئی بھی دیگر آلات ہمہ وقت سازی پر موجود Ecosia اکائونٹ سے جڑے نہیں ہوئے۔"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "براہ مہربانی Firefox کھولیں، سیٹینگز میں جائیں اور جاری رکھنے کے لیئے سائن ان کریں۔"; +"SendTo.NotSignedIn.Message" = "براہ مہربانی Ecosia کھولیں، سیٹینگز میں جائیں اور جاری رکھنے کے لیئے سائن ان کریں۔"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "آپ اپنے Firefox اکائونٹ میں سائن ان نہیں ہیں۔"; +"SendTo.NotSignedIn.Title" = "آپ اپنے Ecosia اکائونٹ میں سائن ان نہیں ہیں۔"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "ارسال کریں"; @@ -1034,7 +1034,7 @@ "SentTab.NoTabArrivingNotification.body" = "آغاز کرنے کے لیئے تھبتھبائیں"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "نظارہ"; @@ -1091,7 +1091,7 @@ "Settings.DataManagement.Title" = "کوائف بندرستگی"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox آپ کے اکائونٹ کے ساتھ ہمہ وقت سازی بند کر دے گا۔ لیکن اس آلہ پر آپ کے کوئی برائوزنگ کوائف حزف نہیں کرے گا۔"; +"Settings.Disconnect.Body" = "Ecosia آپ کے اکائونٹ کے ساتھ ہمہ وقت سازی بند کر دے گا۔ لیکن اس آلہ پر آپ کے کوئی برائوزنگ کوائف حزف نہیں کرے گا۔"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "ہمہ وقت سازی منقطع کریں"; @@ -1157,7 +1157,7 @@ "Settings.FxA.Sync.SectionName" = "ہمہ وقت ساز سیٹنگیں"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox اکاؤنٹس"; +"Settings.FxA.Title" = "Ecosia اکاؤنٹس"; /* General settings section title */ "Settings.General.SectionName" = "عمومی"; @@ -1244,7 +1244,7 @@ "Settings.NewTab.Option.Custom" = "مخصوص"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox ابتدائی صفحہ"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia ابتدائی صفحہ"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "ابتدائی صفحہ"; @@ -1265,10 +1265,10 @@ "Settings.NoImageModeBlockImages.Label.v99" = "تصویر مسدود کریں"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Firefox کھولتے وقت"; +"Settings.OfferClipboardBar.Status" = "Ecosia کھولتے وقت"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Firefox کھولتے وقت"; +"Settings.OfferClipboardBar.StatusV2" = "Ecosia کھولتے وقت"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "نقل شدہ ربط کھولنے پیشکش"; @@ -1298,7 +1298,7 @@ "Settings.SendUsage.Link" = "مزید سیکھیں."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla صرف اس کو جمع کرنے کی کوشش کرتا ہے جو ہمیں ہر کسی کے لئے Firefox فراہم کرنے اور بہتر بنانے کی ضرورت ہے۔"; +"Settings.SendUsage.Message" = "Ecosia صرف اس کو جمع کرنے کی کوشش کرتا ہے جو ہمیں ہر کسی کے لئے Ecosia فراہم کرنے اور بہتر بنانے کی ضرورت ہے۔"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "استعمال شدہ کوائف ارسال کریں"; @@ -1412,13 +1412,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "پس منظر لوڈ ہو گیا"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Firefox میں لوڈ ہو رہا ہے"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Ecosia میں لوڈ ہو رہا ہے"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Firefox میں کھولیں"; +"ShareExtension.OpenInFirefoxAction.Title" = "Ecosia میں کھولیں"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Firefox میں تلاش کریں"; +"ShareExtension.SeachInFirefoxAction.Title" = "Ecosia میں تلاش کریں"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "نقل کریں"; @@ -1565,7 +1565,7 @@ "UIMenuItem.PasteGo" = "چسپاں کریں اور چلیں"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Firefox کے ساتھ تلاش کریں"; +"UIMenuItem.SearchWithFirefox" = "Ecosia کے ساتھ تلاش کریں"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "غير مطالعہ"; @@ -1601,7 +1601,7 @@ "Yesterday" = "‏‏گزشتہ کل"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "آپ کے دوسرے آلات پر Firefox میں کوئی ٹیب نہیں کھلے ہوئِے ہیں۔"; +"You don’t have any tabs open in Firefox on your other devices." = "آپ کے دوسرے آلات پر Ecosia میں کوئی ٹیب نہیں کھلے ہوئِے ہیں۔"; /* Your Rights settings section title */ "Your Rights" = "آپ کے حقوق"; diff --git a/firefox-ios/Shared/ur.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/ur.lproj/PrivateBrowsing.strings index 8b47ad2dfa6d9..bec42b3dd1681 100644 --- a/firefox-ios/Shared/ur.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/ur.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "نئے نجی ٹیب میں کھولیں"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox آپ کے سابقات ہا کوکیزوں کو یاد نہیں رکھے گا لیکن نئی بک مارکس محفوظ کی جائیں گی۔"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia آپ کے سابقات ہا کوکیزوں کو یاد نہیں رکھے گا لیکن نئی بک مارکس محفوظ کی جائیں گی۔"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "مزید سیکھیں"; diff --git a/firefox-ios/Shared/ur.lproj/Today.strings b/firefox-ios/Shared/ur.lproj/Today.strings index d93f6ecdb4f86..872105dd8cec2 100644 --- a/firefox-ios/Shared/ur.lproj/Today.strings +++ b/firefox-ios/Shared/ur.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "نجی ٹیب بند کریں"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "اپنے ہوم اسکرین پر Firefox شارٹ کٹ جوڑیں۔"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "اپنے ہوم اسکرین پر Ecosia شارٹ کٹ جوڑیں۔"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "نقل شدہ ربط پر جائیں"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "کوئی کھلے ٹیب نہیں"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Firefox کھولیں"; +"TodayWidget.OpenFirefoxLabel" = "Ecosia کھولیں"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "نجی تلاش"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "اپنے ہوم اسکرین پر Firefox شارٹ کٹ جوڑیں۔ جوڑنے کے بعد شارٹ کٹ کو ترمیم کرنے کے لئے دبا کر رکھیں اور کوئی دوسرا شارٹ کاٹ منتخب کریں۔"; +"TodayWidget.QuickActionGalleryDescription" = "اپنے ہوم اسکرین پر Ecosia شارٹ کٹ جوڑیں۔ جوڑنے کے بعد شارٹ کٹ کو ترمیم کرنے کے لئے دبا کر رکھیں اور کوئی دوسرا شارٹ کاٹ منتخب کریں۔"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "فوری کارروائی"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox شارٹ کٹس"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia شارٹ کٹس"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "اپنی کھلی ٹیبز میں شارٹ کٹ شامل کریں۔"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "فوری نظارہ"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Firefox میں تلاش کریں"; +"TodayWidget.SearchInFirefoxV2" = "Ecosia میں تلاش کریں"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "نجی ٹیب میں تلاش کریں"; diff --git a/firefox-ios/Shared/uz.lproj/Default Browser.strings b/firefox-ios/Shared/uz.lproj/Default Browser.strings index d77b0c24b23c2..c697e611e4723 100644 --- a/firefox-ios/Shared/uz.lproj/Default Browser.strings +++ b/firefox-ios/Shared/uz.lproj/Default Browser.strings @@ -2,7 +2,7 @@ "DefaultBrowserCard.Button.v2" = "Qanday amalga oshirishni oʻrganish"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Saytlar, elektron pochta va xabarlar havolalarini Firefoxda avtomatik ravishda ochiladigan qilib sozlang."; +"DefaultBrowserCard.Description" = "Saytlar, elektron pochta va xabarlar havolalarini Ecosiada avtomatik ravishda ochiladigan qilib sozlang."; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Standart brauzeringizga almashing"; @@ -17,7 +17,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Standart brauzer ilovasi ustiga bosing"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Firefoxni tanlang"; +"DefaultBrowserOnboarding.Description3" = "3. Ecosiani tanlang"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Standart brauzer ilovasi"; diff --git a/firefox-ios/Shared/uz.lproj/Intro.strings b/firefox-ios/Shared/uz.lproj/Intro.strings index a8e0d4a4caeda..784fdcd797fca 100644 --- a/firefox-ios/Shared/uz.lproj/Intro.strings +++ b/firefox-ios/Shared/uz.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Kirish"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "\"Firefox\"ga xush kelibsiz"; +"Intro.Slides.Welcome.Title.v2" = "\"Ecosia\"ga xush kelibsiz"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Gezinmeye başla"; diff --git a/firefox-ios/Shared/uz.lproj/Localizable.strings b/firefox-ios/Shared/uz.lproj/Localizable.strings index caa0ba6379f7d..95b27c3c18ea6 100644 --- a/firefox-ios/Shared/uz.lproj/Localizable.strings +++ b/firefox-ios/Shared/uz.lproj/Localizable.strings @@ -164,7 +164,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Bunga sabab noto‘g‘ri sozlash yoki xakkerlar harakati sabab bo‘lishi mumkin. Kuchli xavfni o‘z bo‘yningizga olsangiz, davom etishingiz mumkin."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ egasi saytni noto‘g‘ri sozlagan. Ma’lumotlarni o‘g‘rilanishidan saqlash uchun Firefox bu saytga ulanmadi."; +"ErrorPages.CertWarning.Description" = "%@ egasi saytni noto‘g‘ri sozlagan. Ma’lumotlarni o‘g‘rilanishidan saqlash uchun Ecosia bu saytga ulanmadi."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Ushbu ulanish ishonchsiz."; @@ -179,7 +179,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox bosh sahifasi"; +"Firefox.HomePage.Title" = "Ecosia bosh sahifasi"; /* Accessibility Label for the tab toolbar Forward button */ "Forward" = "Oldinga"; @@ -197,7 +197,7 @@ "HistoryPanel.EmptySyncedTabsNullState.Description" = "Boshqa qurilmalaringizdagi ichki oynalar bu yerda ko‘rinadi."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox’ni sinxronlash"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia’ni sinxronlash"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Tarix"; @@ -299,7 +299,7 @@ "LoginsHelper.Update.Button" = "Yangilash"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Firefox’da oldinroq nosozlik ro‘y berganga o‘xshaydi. Ichki oynalarni tiklashni xohlaysizmi?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Ecosia’da oldinroq nosozlik ro‘y berganga o‘xshaydi. Ichki oynalarni tiklashni xohlaysizmi?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "O‘qilgan deb belgilash"; @@ -335,7 +335,7 @@ "Okay" = "OK"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Obbo! Firefox’da nosozlik ro‘y berdi"; +"Oops! Firefox crashed" = "Obbo! Ecosia’da nosozlik ro‘y berdi"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Kitobning rasmchasi nom panelida paydo bo‘lsa, uning ustiga bosib, maqolalarni \"O‘qish rejimi\"da ochishingiz mumkin."; @@ -344,7 +344,7 @@ "Open Settings" = "Sozlamalarni ochish"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Xato manzil kiritilganligi uchun Firefox bu sahifani ocha olmaydi."; +"OpenURL.Error.Message" = "Xato manzil kiritilganligi uchun Ecosia bu sahifani ocha olmaydi."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Sahifa ochilmadi"; @@ -434,7 +434,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Mozilla muammolarni bartaraf qilish uchun nosozlik hisobotlari jo‘natilsinmi?"; +"Send a crash report so Mozilla can fix the problem?" = "Ecosia muammolarni bartaraf qilish uchun nosozlik hisobotlari jo‘natilsinmi?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Mulohaza jo‘natish"; @@ -602,7 +602,7 @@ "Yesterday" = "Kecha"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Bosha qurilmalarda Firefox’da ochilgan hech qanday ichki oynalaringiz yo‘q."; +"You don’t have any tabs open in Firefox on your other devices." = "Bosha qurilmalarda Ecosia’da ochilgan hech qanday ichki oynalaringiz yo‘q."; /* Your Rights settings section title */ "Your Rights" = "Huquqlaringiz"; diff --git a/firefox-ios/Shared/uz.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/uz.lproj/PrivateBrowsing.strings index f98a786c3eae1..a4e4a9d1d6429 100644 --- a/firefox-ios/Shared/uz.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/uz.lproj/PrivateBrowsing.strings @@ -2,7 +2,7 @@ "Close Private Tabs" = "Maxfiy ichki oynalarni yopish"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox tarix yoki kukilarni xotirada olib qolmaydi, lekin yangi xatcho‘plar saqlanib qoladi."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia tarix yoki kukilarni xotirada olib qolmaydi, lekin yangi xatcho‘plar saqlanib qoladi."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Batafsil ma’lumot"; diff --git a/firefox-ios/Shared/vi.lproj/Default Browser.strings b/firefox-ios/Shared/vi.lproj/Default Browser.strings index f906e184b5691..541ecfd8f53ff 100644 --- a/firefox-ios/Shared/vi.lproj/Default Browser.strings +++ b/firefox-ios/Shared/vi.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "Đặt Firefox làm trình duyệt mặc định của bạn là một phiếu bầu cho một Internet mở, có thể truy cập."; +"DefaultBrowserCard.BetterInternet.Description.v108" = "Đặt Ecosia làm trình duyệt mặc định của bạn là một phiếu bầu cho một Internet mở, có thể truy cập."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "Đặt làm mặc định cho Internet tốt hơn"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "Tìm hiểu cách thức"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "Đặt các liên kết từ trang web, email và tin nhắn để tự động mở trong Firefox."; +"DefaultBrowserCard.Description" = "Đặt các liên kết từ trang web, email và tin nhắn để tự động mở trong Ecosia."; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "Chọn Firefox làm trình duyệt mặc định của bạn để tự động tăng tốc độ, an toàn và quyền riêng tư."; +"DefaultBrowserCard.NextLevel.Description.v108" = "Chọn Ecosia làm trình duyệt mặc định của bạn để tự động tăng tốc độ, an toàn và quyền riêng tư."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "Nâng cao trình duyệt hàng ngày"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox chặn trung bình hơn 3.000 trình theo dõi trên mỗi người dùng mỗi tháng. Đặt chúng tôi làm trình duyệt mặc định của bạn để yên tâm về quyền riêng tư."; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia chặn trung bình hơn 3.000 trình theo dõi trên mỗi người dùng mỗi tháng. Đặt chúng tôi làm trình duyệt mặc định của bạn để yên tâm về quyền riêng tư."; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox bảo vệ riêng tư của bạn"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia bảo vệ riêng tư của bạn"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Đặt làm trình duyệt mặc định của bạn"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. Nhấn vào Ứng dụng trình duyệt mặc định"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. Chọn Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. Chọn Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "Ứng dụng trình duyệt mặc định"; diff --git a/firefox-ios/Shared/vi.lproj/Intro.strings b/firefox-ios/Shared/vi.lproj/Intro.strings index 6c306b0f13159..d33659818cfe9 100644 --- a/firefox-ios/Shared/vi.lproj/Intro.strings +++ b/firefox-ios/Shared/vi.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "Đăng nhập"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "Chào mừng đến với Firefox"; +"Intro.Slides.Welcome.Title.v2" = "Chào mừng đến với Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "Bắt đầu duyệt web"; diff --git a/firefox-ios/Shared/vi.lproj/Localizable.strings b/firefox-ios/Shared/vi.lproj/Localizable.strings index aab060eae8bc4..3e0d2ff998b33 100644 --- a/firefox-ios/Shared/vi.lproj/Localizable.strings +++ b/firefox-ios/Shared/vi.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "Nó có thể là một cấu hình sai hoặc giả mạo bởi kẻ tấn công. Tiến hành nếu bạn chấp nhận rủi ro tiềm ẩn."; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "Chủ sở hữu của %@ đã cấu hình trang web của họ không đúng cách. Để bảo vệ thông tin của bạn khỏi bị đánh cắp, Firefox đã không kết nối với trang web này."; +"ErrorPages.CertWarning.Description" = "Chủ sở hữu của %@ đã cấu hình trang web của họ không đúng cách. Để bảo vệ thông tin của bạn khỏi bị đánh cắp, Ecosia đã không kết nối với trang web này."; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "Kết nối này không đáng tin"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Trang chủ Firefox"; +"Firefox.HomePage.Title" = "Trang chủ Ecosia"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "Tùy chỉnh trang chủ"; @@ -377,7 +377,7 @@ "Forward" = "Tiến"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Tài khoản Firefox"; +"FxA.FirefoxAccount" = "Tài khoản Ecosia"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "Quản lý tài khoản và thiết bị"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "Đăng nhập bằng máy ảnh của bạn"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "Trên máy tính của bạn, hãy mở Firefox và truy cập firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "Trên máy tính của bạn, hãy mở Ecosia và truy cập ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "Sẵn sàng để quét"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "Đồng bộ ngay"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Đồng bộ hóa Firefox đã kết nối với %@"; +"FxAPush_DeviceConnected_body" = "Đồng bộ hóa Ecosia đã kết nối với %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "Đồng bộ hóa được kết nối"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ đã ngắt kết nối thành công."; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "Thiết bị này đã được ngắt kết nối thành công khỏi Đồng bộ hóa Firefox."; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "Thiết bị này đã được ngắt kết nối thành công khỏi Đồng bộ hóa Ecosia."; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "Đồng bộ hóa đã bị ngắt kết nối"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "Đồng bộ hóa đã bị ngắt kết nối"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Một thiết bị đã ngắt kết nối khỏi Đồng bộ hóa Firefox"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "Một thiết bị đã ngắt kết nối khỏi Đồng bộ hóa Ecosia"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Firefox, thay đổi hình nền."; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Logo Ecosia, thay đổi hình nền."; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "Trợ giúp"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "Đăng nhập để xem danh sách thẻ từ các thiết bị khác của bạn."; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Đồng bộ hóa Firefox"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Đồng bộ hóa Ecosia"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "Lịch sử"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "Tìm hiểu thêm"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "Để sử dụng tính năng tự động điền của Firefox, bạn phải bật mật mã thiết bị."; +"Logins.PasscodeRequirement.Warning" = "Để sử dụng tính năng tự động điền của Ecosia, bạn phải bật mật mã thiết bị."; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "Mang mật khẩu của bạn đi khắp mọi nơi"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "Tự động điền mật khẩu Firefox"; +"Logins.WelcomeView.Title2" = "Tự động điền mật khẩu Ecosia"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "Bật Tự động điền"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "THÔNG TIN ĐĂNG NHẬP ĐÃ LƯU"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Có vẻ như Firefox đã gặp sự cố trước đó. Bạn có muốn khôi phục các thẻ của bạn?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "Có vẻ như Ecosia đã gặp sự cố trước đó. Bạn có muốn khôi phục các thẻ của bạn?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "Đánh dấu là đã đọc"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "Bắt đầu duyệt web"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "Oops! Firefox đã gặp sự cố"; +"Oops! Firefox crashed" = "Oops! Ecosia đã gặp sự cố"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "Mở bài viết trong chế độ đọc sách bằng cách nhấn vào biểu tượng sách khi nó xuất hiện trên thanh tiêu đề."; @@ -905,7 +905,7 @@ "Open Tabs" = "Các thẻ đang mở"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "Firefox không thể mở trang vì nó có địa chỉ không hợp lệ."; +"OpenURL.Error.Message" = "Ecosia không thể mở trang vì nó có địa chỉ không hợp lệ."; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "Không thể mở trang"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "Điều này cho phép bạn lưu hình ảnh vào Camera Roll của bạn."; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox muốn truy cập Ảnh của bạn"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia muốn truy cập Ảnh của bạn"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "Đóng"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "Dữ liệu không hợp lệ"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "Đi tới ‘Cài đặt’ của thiết bị > ‘Firefox’. Cho phép Firefox truy cập máy ảnh."; +"ScanQRCode.PermissionError.Message.v100" = "Đi tới ‘Cài đặt’ của thiết bị > ‘Ecosia’. Cho phép Ecosia truy cập máy ảnh."; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "Quét mã QR"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "Chuyển sang thẻ"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Đề xuất của Firefox"; +"Search.SuggestSectionTitle.v102" = "Đề xuất của Ecosia"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "Công cụ tìm kiếm mới sẽ xuất hiện trong thanh tìm kiếm nhanh."; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "OK"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "Gửi báo cáo lỗi để Mozilla có thể khắc phục sự cố?"; +"Send a crash report so Mozilla can fix the problem?" = "Gửi báo cáo lỗi để Ecosia có thể khắc phục sự cố?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "Gửi phản hồi"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "Gửi thẻ"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "Bạn không có thiết bị nào khác được kết nối với tài khoản Firefox này để đồng bộ hóa."; +"SendTo.NoDevicesFound.Message" = "Bạn không có thiết bị nào khác được kết nối với tài khoản Ecosia này để đồng bộ hóa."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "Vui lòng mở Firefox, truy cập Cài đặt và đăng nhập để tiếp tục."; +"SendTo.NotSignedIn.Message" = "Vui lòng mở Ecosia, truy cập Cài đặt và đăng nhập để tiếp tục."; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "Bạn chưa đăng nhập vào tài khoản Firefox của mình."; +"SendTo.NotSignedIn.Title" = "Bạn chưa đăng nhập vào tài khoản Ecosia của mình."; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "Gửi"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "Chạm để bắt đầu"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Đồng bộ hóa Firefox"; +"SentTab.NoTabArrivingNotification.title" = "Đồng bộ hóa Ecosia"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "Xem"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "Quản lý dữ liệu"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox sẽ ngừng đồng bộ hóa với tài khoản của bạn, nhưng sẽ không xóa mọi dữ liệu duyệt web của bạn trên thiết bị này."; +"Settings.Disconnect.Body" = "Ecosia sẽ ngừng đồng bộ hóa với tài khoản của bạn, nhưng sẽ không xóa mọi dữ liệu duyệt web của bạn trên thiết bị này."; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "Ngắt kết nối đồng bộ hóa"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "Cài đặt đồng bộ hóa"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Tài khoản Firefox"; +"Settings.FxA.Title" = "Tài khoản Ecosia"; /* General settings section title */ "Settings.General.SectionName" = "Tổng quát"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "Chọn những gì hiển thị làm trang chủ."; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "Chọn những gì được hiển thị trên trang chủ Firefox."; +"Settings.Home.Option.Description.v101" = "Chọn những gì được hiển thị trên trang chủ Ecosia."; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Trang đã xem gần đây"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "Trang chủ"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "Chọn những gì bạn sẽ thấy khi quay lại Firefox."; +"Settings.Home.Option.StartAtHome.Description" = "Chọn những gì bạn sẽ thấy khi quay lại Ecosia."; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "Thẻ cuối cùng"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "Hình nền"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Hình nền Firefox, mẫu thạch anh tím."; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Hình nền Ecosia, mẫu thạch anh tím."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Hình nền Firefox, mô hình đồi bãi biển."; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Hình nền Ecosia, mô hình đồi bãi biển."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Hình nền Firefox, mẫu cerulean."; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Hình nền Ecosia, mẫu cerulean."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Hình nền rõ ràng mặc định."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Hình nền Firefox, mô hình mặt trời mọc."; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Hình nền Ecosia, mô hình mặt trời mọc."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Chuyển đổi xoay hình nền trang chủ"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Hình nền Firefox, mô hình đồi hoàng hôn."; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Hình nền Ecosia, mô hình đồi hoàng hôn."; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ cổ điển"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "Phiên bản giới hạn"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Thay đổi hình nền bằng cách nhấn vào biểu trưng trang chủ của Firefox"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "Thay đổi hình nền bằng cách nhấn vào biểu trưng trang chủ của Ecosia"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "Hình nền"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "Tùy chỉnh"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Trang chủ Firefox"; +"Settings.NewTab.Option.FirefoxHome" = "Trang chủ Ecosia"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "Trang chủ"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "Chặn hình ảnh"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "Khi mở Firefox"; +"Settings.OfferClipboardBar.Status" = "Khi mở Ecosia"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "Khi mở %@"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "Khi mở Firefox"; +"Settings.OfferClipboardBar.StatusV2" = "Khi mở Ecosia"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "Đề nghị mở các liên kết được sao chép"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "Tìm hiểu thêm."; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla chỉ thu thập những gì chúng tôi cần cung cấp và cải tiến Firefox cho tất cả mọi người."; +"Settings.SendUsage.Message" = "Ecosia chỉ thu thập những gì chúng tôi cần cung cấp và cải tiến Ecosia cho tất cả mọi người."; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "Gửi dữ liệu sử dụng"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "Mở thẻ mới"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "Sử dụng phím tắt Siri để nhanh chóng mở Firefox qua Siri"; +"Settings.Siri.SectionDescription" = "Sử dụng phím tắt Siri để nhanh chóng mở Ecosia qua Siri"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Phím tắt Siri"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "Tìm hiểu thêm."; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox có thể cài đặt và chạy các nghiên cứu theo thời gian."; +"Settings.Studies.Toggle.Message" = "Ecosia có thể cài đặt và chạy các nghiên cứu theo thời gian."; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Nghiên cứu"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "Tải trong nền"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "Đang tải trong Firefox"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "Đang tải trong Ecosia"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "Mở trong Firefox"; +"ShareExtension.OpenInFirefoxAction.Title" = "Mở trong Ecosia"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "Tìm kiếm trong Firefox"; +"ShareExtension.SeachInFirefoxAction.Title" = "Tìm kiếm trong Ecosia"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "Sao chép"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "Dán & mở"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "Tìm kiếm bằng Firefox"; +"UIMenuItem.SearchWithFirefox" = "Tìm kiếm bằng Ecosia"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "chưa đọc"; @@ -1793,7 +1793,7 @@ "Yesterday" = "Hôm qua"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "Bạn không có bất kỳ thẻ nào mở trong Firefox trên các thiết bị khác của bạn."; +"You don’t have any tabs open in Firefox on your other devices." = "Bạn không có bất kỳ thẻ nào mở trong Ecosia trên các thiết bị khác của bạn."; /* Your Rights settings section title */ "Your Rights" = "Quyền lợi của bạn"; diff --git a/firefox-ios/Shared/vi.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/vi.lproj/PrivateBrowsing.strings index c660bbe4f8387..0130cfb7726dd 100644 --- a/firefox-ios/Shared/vi.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/vi.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "Mở trong thẻ riêng tư mới"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox sẽ không nhớ bất kỳ lịch sử hoặc cookie của bạn, nhưng dấu trang mới sẽ được lưu."; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia sẽ không nhớ bất kỳ lịch sử hoặc cookie của bạn, nhưng dấu trang mới sẽ được lưu."; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "Tìm hiểu thêm"; diff --git a/firefox-ios/Shared/vi.lproj/Today.strings b/firefox-ios/Shared/vi.lproj/Today.strings index 9ea3f357151dd..4c96e4fa02cc4 100644 --- a/firefox-ios/Shared/vi.lproj/Today.strings +++ b/firefox-ios/Shared/vi.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "Đóng thẻ riêng tư"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "Thêm lối tắt Firefox tới màn hình chính của bạn."; +"TodayWidget.FirefoxShortcutGalleryDescription" = "Thêm lối tắt Ecosia tới màn hình chính của bạn."; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "Chuyển đến liên kết đã sao chép"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "Không có thẻ đang mở."; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "Mở Firefox"; +"TodayWidget.OpenFirefoxLabel" = "Mở Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "Tìm kiếm riêng tư"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "Thêm lối tắt Firefox vào màn hình chính của bạn. Sau khi thêm tiện ích, hãy chạm và giữ để chỉnh sửa tiện ích và chọn một phím tắt khác."; +"TodayWidget.QuickActionGalleryDescription" = "Thêm lối tắt Ecosia vào màn hình chính của bạn. Sau khi thêm tiện ích, hãy chạm và giữ để chỉnh sửa tiện ích và chọn một phím tắt khác."; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "Hành động nhanh"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Lối tắt Firefox"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Lối tắt Ecosia"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "Thêm phím tắt vào các thẻ đang mở của bạn."; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "Xem nhanh"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "Tìm kiếm trong Firefox"; +"TodayWidget.SearchInFirefoxV2" = "Tìm kiếm trong Ecosia"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "Tìm kiếm trong thẻ riêng tư"; diff --git a/firefox-ios/Shared/zh-CN.lproj/Default Browser.strings b/firefox-ios/Shared/zh-CN.lproj/Default Browser.strings index d9011cd4d85ca..805783c2d9c08 100644 --- a/firefox-ios/Shared/zh-CN.lproj/Default Browser.strings +++ b/firefox-ios/Shared/zh-CN.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "将 Firefox 设为默认浏览器,为开放且人人可用的互联网添一份力量。"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "将 Ecosia 设为默认浏览器,为开放且人人可用的互联网添一份力量。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "将更美好的互联网设为默认"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "了解如何操作"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "将网站、电子邮件及聊天工具中的链接设为在 Firefox 中自动打开。"; +"DefaultBrowserCard.Description" = "将网站、电子邮件及聊天工具中的链接设为在 Ecosia 中自动打开。"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "将 Firefox 设为默认浏览器,时刻享受更快速、更安全、更私密的浏览体验。"; +"DefaultBrowserCard.NextLevel.Description.v108" = "将 Ecosia 设为默认浏览器,时刻享受更快速、更安全、更私密的浏览体验。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "助力日常浏览"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox 平均每月为每位用户拦截 3000+ 个跟踪器。将 Firefox 设为默认浏览器,隐私保护再无忧。"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia 平均每月为每位用户拦截 3000+ 个跟踪器。将 Ecosia 设为默认浏览器,隐私保护再无忧。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox 为您的隐私撑起保护伞"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia 为您的隐私撑起保护伞"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "切换默认浏览器"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. 轻点“默认浏览器 App”"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. 选择 Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. 选择 Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "默认浏览器 App"; diff --git a/firefox-ios/Shared/zh-CN.lproj/Intro.strings b/firefox-ios/Shared/zh-CN.lproj/Intro.strings index 552dc1dfc19a2..0d5bfab6e7755 100644 --- a/firefox-ios/Shared/zh-CN.lproj/Intro.strings +++ b/firefox-ios/Shared/zh-CN.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "登录"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "欢迎使用 Firefox"; +"Intro.Slides.Welcome.Title.v2" = "欢迎使用 Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "开始浏览"; diff --git a/firefox-ios/Shared/zh-CN.lproj/Localizable.strings b/firefox-ios/Shared/zh-CN.lproj/Localizable.strings index 2b756784d6848..ccc3b78f234b9 100644 --- a/firefox-ios/Shared/zh-CN.lproj/Localizable.strings +++ b/firefox-ios/Shared/zh-CN.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "这可能是配置错误,也可能是被攻击者篡改。继续访问需要承担潜在的风险。"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ 的管理员未正确配置网站。为避免您的信息被窃,Firefox 没有建立与该网站的连接。"; +"ErrorPages.CertWarning.Description" = "%@ 的管理员未正确配置网站。为避免您的信息被窃,Ecosia 没有建立与该网站的连接。"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "此连接不受信任"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox 主页"; +"Firefox.HomePage.Title" = "Ecosia 主页"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "定制主页"; @@ -377,7 +377,7 @@ "Forward" = "前进"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox 账户"; +"FxA.FirefoxAccount" = "Ecosia 账户"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "管理账户和设备"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "使用相机登录"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "在计算机上使用 Firefox 打开 firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "在计算机上使用 Ecosia 打开 ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "立即扫码"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "立即同步"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox 同步已连接至 %@"; +"FxAPush_DeviceConnected_body" = "Ecosia 同步已连接至 %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "同步已连接"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "%@ 已成功断开。"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "此设备已成功断开 Firefox 同步。"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "此设备已成功断开 Ecosia 同步。"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "同步已断开"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "同步已断开"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "一台设备已断开 Firefox 同步"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "一台设备已断开 Ecosia 同步"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox 徽标,更换壁纸。"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia 徽标,更换壁纸。"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "帮助"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "登录以查看来自您的其他设备的标签页列表。"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox 同步"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia 同步"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "历史记录"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "详细了解"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "要使用 Firefox 的“自动填充”功能,须先启用设备密码。"; +"Logins.PasscodeRequirement.Warning" = "要使用 Ecosia 的“自动填充”功能,须先启用设备密码。"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "随身携带密码"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "自动填充 Firefox 密码"; +"Logins.WelcomeView.Title2" = "自动填充 Ecosia 密码"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "启用“自动填充”"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "已保存的登录信息"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "看起来 Firefox 此前崩溃了。您想恢复您的标签页吗?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "看起来 Ecosia 此前崩溃了。您想恢复您的标签页吗?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "标记为已读"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "开始浏览"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "啊哦!Firefox 崩溃了"; +"Oops! Firefox crashed" = "啊哦!Ecosia 崩溃了"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "当书本图标出现在标题栏时,轻点它即可在阅读模式下阅读文章。"; @@ -905,7 +905,7 @@ "Open Tabs" = "打开的标签页"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "地址无效,因此Firefox 无法打开该页面。"; +"OpenURL.Error.Message" = "地址无效,因此Ecosia 无法打开该页面。"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "无法打开页面"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "这将允许您保存图像到您的“相机胶卷”。"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox 想要访问您的照片"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia 想要访问您的照片"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "关闭"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "数据无效"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "请到设备的“设置”>“Firefox”,允许 Firefox 访问相机。"; +"ScanQRCode.PermissionError.Message.v100" = "请到设备的“设置”>“Ecosia”,允许 Ecosia 访问相机。"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "扫描二维码"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "切换到标签页"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox 建议"; +"Search.SuggestSectionTitle.v102" = "Ecosia 建议"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "新的搜索引擎将在快捷搜索栏中显示。"; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "确定"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "发送一则崩溃报告给 Mozilla,以便我们修复此问题?"; +"Send a crash report so Mozilla can fix the problem?" = "发送一则崩溃报告给 Ecosia,以便我们修复此问题?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "发送反馈"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "发送标签页"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "您没有其他已连接此 Firefox 账户的设备可用于同步。"; +"SendTo.NoDevicesFound.Message" = "您没有其他已连接此 Ecosia 账户的设备可用于同步。"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "请打开 Firefox,转至“设置”并登录以继续。"; +"SendTo.NotSignedIn.Message" = "请打开 Ecosia,转至“设置”并登录以继续。"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "您尚未登录您的 Firefox 账户。"; +"SendTo.NotSignedIn.Title" = "您尚未登录您的 Ecosia 账户。"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "发送"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "轻点以开始"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox 同步"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia 同步"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "查看"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "数据管理"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox 将停止与您的账户的同步,但不会删除此设备上的任何浏览数据。"; +"Settings.Disconnect.Body" = "Ecosia 将停止与您的账户的同步,但不会删除此设备上的任何浏览数据。"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "断开同步"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "同步设置"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox 账户"; +"Settings.FxA.Title" = "Ecosia 账户"; /* General settings section title */ "Settings.General.SectionName" = "常规"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "选择要在主页显示的内容。"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "选择要在 Firefox 主页看到的内容。"; +"Settings.Home.Option.Description.v101" = "选择要在 Ecosia 主页看到的内容。"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "回到先前页面"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "主页"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "选择您回到 Firefox 时要看到的内容。"; +"Settings.Home.Option.StartAtHome.Description" = "选择您回到 Ecosia 时要看到的内容。"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "最后打开的标签页"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "壁纸"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox 壁纸,紫晶图案。"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Ecosia 壁纸,紫晶图案。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox 壁纸,海滩山丘图案。"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Ecosia 壁纸,海滩山丘图案。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox 壁纸,蔚蓝图案。"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Ecosia 壁纸,蔚蓝图案。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ "Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "默认明亮壁纸。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox 壁纸,日出图案。"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Ecosia 壁纸,日出图案。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "主页壁纸切换开关"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox 壁纸,暮光山丘图案。"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Ecosia 壁纸,暮光山丘图案。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "%@ 经典"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "限定版"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "轻点 Firefox 主页徽标以更换壁纸"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "轻点 Ecosia 主页徽标以更换壁纸"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "壁纸"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "自定义"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox 主页"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia 主页"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "主页"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "无图模式"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "当打开 Firefox 时"; +"Settings.OfferClipboardBar.Status" = "当打开 Ecosia 时"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "打开 %@ 时询问"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "当打开 Firefox 时"; +"Settings.OfferClipboardBar.StatusV2" = "当打开 Ecosia 时"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "询问打开已复制的链接"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "详细了解。"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla 仅收集为大众提供和改进 Firefox 所必要的信息。"; +"Settings.SendUsage.Message" = "Ecosia 仅收集为大众提供和改进 Ecosia 所必要的信息。"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "发送使用数据"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "打开新标签页"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "使用 Siri 快捷指令快速开启 Firefox"; +"Settings.Siri.SectionDescription" = "使用 Siri 快捷指令快速开启 Ecosia"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri 快捷指令"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "详细了解。"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox 可能会不时地安装并运行一些研究项目。"; +"Settings.Studies.Toggle.Message" = "Ecosia 可能会不时地安装并运行一些研究项目。"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "研究"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "在后台载入"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "正在 Firefox 中载入"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "正在 Ecosia 中载入"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "在 Firefox 中打开"; +"ShareExtension.OpenInFirefoxAction.Title" = "在 Ecosia 中打开"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "在 Firefox 中搜索"; +"ShareExtension.SeachInFirefoxAction.Title" = "在 Ecosia 中搜索"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "复制"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "粘贴并前往"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "使用 Firefox 搜索"; +"UIMenuItem.SearchWithFirefox" = "使用 Ecosia 搜索"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "未读"; @@ -1793,7 +1793,7 @@ "Yesterday" = "昨天"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "您没有在其他设备的 Firefox 上已经打开的标签页。"; +"You don’t have any tabs open in Firefox on your other devices." = "您没有在其他设备的 Ecosia 上已经打开的标签页。"; /* Your Rights settings section title */ "Your Rights" = "您的权利"; diff --git a/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings index 189c82a5b33a1..faf3d19796996 100644 --- a/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "新建隐私标签页打开"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox 不会记住您的历史记录和 Cookie,但新的书签将被保留。"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia 不会记住您的历史记录和 Cookie,但新的书签将被保留。"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "详细了解"; diff --git a/firefox-ios/Shared/zh-CN.lproj/Today.strings b/firefox-ios/Shared/zh-CN.lproj/Today.strings index ee772f5d361ee..9334c7a93e205 100644 --- a/firefox-ios/Shared/zh-CN.lproj/Today.strings +++ b/firefox-ios/Shared/zh-CN.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "关闭隐私标签页"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "在主屏幕添加 Firefox 快捷方式。"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "在主屏幕添加 Ecosia 快捷方式。"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "打开已复制的链接"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "没有打开的标签页。"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "打开 Firefox"; +"TodayWidget.OpenFirefoxLabel" = "打开 Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "隐私搜索"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "将 Firefox 快捷方式添加至主屏幕。添加后按住小组件进行编辑,可选择其他快捷方式。"; +"TodayWidget.QuickActionGalleryDescription" = "将 Ecosia 快捷方式添加至主屏幕。添加后按住小组件进行编辑,可选择其他快捷方式。"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "快捷操作"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox 快捷方式"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia 快捷方式"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "添加“打开的标签页”快捷方式。"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "快速浏览"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "在 Firefox 中搜索"; +"TodayWidget.SearchInFirefoxV2" = "在 Ecosia 中搜索"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "在隐私标签页中搜索"; diff --git a/firefox-ios/Shared/zh-TW.lproj/Default Browser.strings b/firefox-ios/Shared/zh-TW.lproj/Default Browser.strings index db8d35d457be0..e288556688118 100644 --- a/firefox-ios/Shared/zh-TW.lproj/Default Browser.strings +++ b/firefox-ios/Shared/zh-TW.lproj/Default Browser.strings @@ -1,5 +1,5 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.BetterInternet.Description.v108" = "讓 Firefox 成為您的預設瀏覽器,就是對一個開放、所有人都可隨意使用的網路環境投下贊成票。"; +"DefaultBrowserCard.BetterInternet.Description.v108" = "讓 Ecosia 成為您的預設瀏覽器,就是對一個開放、所有人都可隨意使用的網路環境投下贊成票。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.BetterInternet.Title.v108" = "預設有一個更好的網路環境"; @@ -8,19 +8,19 @@ "DefaultBrowserCard.Button.v2" = "更多資訊"; /* Description for small home tab banner shown that allows user to switch their default browser to Firefox. */ -"DefaultBrowserCard.Description" = "設定使用 Firefox 自動開啟網站、郵件、簡訊當中的鏈結。"; +"DefaultBrowserCard.Description" = "設定使用 Ecosia 自動開啟網站、郵件、簡訊當中的鏈結。"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.NextLevel.Description.v108" = "讓 Firefox 成為您的預設瀏覽器,自動加速、保護安全,鞏固隱私。"; +"DefaultBrowserCard.NextLevel.Description.v108" = "讓 Ecosia 成為您的預設瀏覽器,自動加速、保護安全,鞏固隱私。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Title.v108" = "日常上網功能再升級"; /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox 平均每月能幫每位使用者封鎖超過 3,000 組追蹤器。將 Firefox 設為您的預設瀏覽器,對隱私權的保護更加放心。"; +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Ecosia 平均每月能幫每位使用者封鎖超過 3,000 組追蹤器。將 Ecosia 設為您的預設瀏覽器,對隱私權的保護更加放心。"; /* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ -"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox 保護您的隱私"; +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Ecosia 保護您的隱私"; /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "切換您的預設瀏覽器"; @@ -35,7 +35,7 @@ "DefaultBrowserOnboarding.Description2" = "2. 點擊「預設瀏覽器」"; /* Description for default browser onboarding card. */ -"DefaultBrowserOnboarding.Description3" = "3. 選擇 Firefox"; +"DefaultBrowserOnboarding.Description3" = "3. 選擇 Ecosia"; /* Text for the screenshot of the iOS system settings page for Firefox. */ "DefaultBrowserOnboarding.Screenshot" = "預設瀏覽器"; diff --git a/firefox-ios/Shared/zh-TW.lproj/Intro.strings b/firefox-ios/Shared/zh-TW.lproj/Intro.strings index 38c47c97ce54b..53dbccc514448 100644 --- a/firefox-ios/Shared/zh-TW.lproj/Intro.strings +++ b/firefox-ios/Shared/zh-TW.lproj/Intro.strings @@ -2,7 +2,7 @@ "Intro.Slides.Button.SignIn" = "登入"; /* Title for the first panel 'Welcome' in the First Run tour. */ -"Intro.Slides.Welcome.Title.v2" = "歡迎使用 Firefox"; +"Intro.Slides.Welcome.Title.v2" = "歡迎使用 Ecosia"; /* See http://mzl.la/1T8gxwo */ "Start Browsing" = "開始上網"; diff --git a/firefox-ios/Shared/zh-TW.lproj/Localizable.strings b/firefox-ios/Shared/zh-TW.lproj/Localizable.strings index decad1584fbd2..9a8c473db9b90 100644 --- a/firefox-ios/Shared/zh-TW.lproj/Localizable.strings +++ b/firefox-ios/Shared/zh-TW.lproj/Localizable.strings @@ -311,7 +311,7 @@ "ErrorPages.AdvancedWarning2.Text" = "可能是設定錯誤,或是攻擊者惡意修改連線,若要繼續連線的話請小心會有安全性風險。"; /* Warning text on the certificate error page */ -"ErrorPages.CertWarning.Description" = "%@ 的擁有者並未正確設定好網站。為了確保您的個資不被偷走,Firefox 並未連線至此網站。"; +"ErrorPages.CertWarning.Description" = "%@ 的擁有者並未正確設定好網站。為了確保您的個資不被偷走,Ecosia 並未連線至此網站。"; /* Title on the certificate error page */ "ErrorPages.CertWarning.Title" = "此連線不受信任"; @@ -332,7 +332,7 @@ "Facebook" = "Facebook"; /* Title for firefox about:home page in tab history list */ -"Firefox.HomePage.Title" = "Firefox 首頁"; +"Firefox.HomePage.Title" = "Ecosia 首頁"; /* A button at bottom of the Firefox homepage that, when clicked, takes users straight to the settings options, where they can customize the Firefox Home page */ "FirefoxHome.CustomizeHomeButton.Title" = "自訂首頁"; @@ -377,7 +377,7 @@ "Forward" = "下一頁"; /* Settings section title for Firefox Account */ -"FxA.FirefoxAccount" = "Firefox 帳號"; +"FxA.FirefoxAccount" = "Ecosia 帳號"; /* Button label to go to Firefox Account settings */ "FxA.ManageAccount" = "管理帳號與裝置"; @@ -389,7 +389,7 @@ "fxa.signin.camera-signin" = "使用相機登入"; /* FxA sign in view qr code instructions */ -"fxa.signin.qr-link-instruction" = "用您的電腦開啟 Firefox,前往 firefox.com/pair"; +"fxa.signin.qr-link-instruction" = "用您的電腦開啟 Ecosia,前往 ecosia.org/pair"; /* FxA sign in view qr code scan button */ "fxa.signin.ready-to-scan" = "準備好了,開始掃描!"; @@ -401,7 +401,7 @@ "FxA.SyncNow" = "立刻同步"; /* Title of a notification displayed when another device has connected to FxA. %@ refers to the name of the newly connected device. */ -"FxAPush_DeviceConnected_body" = "Firefox 同步已連結到 %@"; +"FxAPush_DeviceConnected_body" = "Ecosia 同步已連結到 %@"; /* Title of a notification displayed when another device has connected to FxA. */ "FxAPush_DeviceConnected_title" = "已連線至 Sync"; @@ -410,7 +410,7 @@ "FxAPush_DeviceDisconnected_body" = "已成功取消 %@ 的連線。"; /* Body of a notification displayed when this device has been disconnected from FxA by another device. */ -"FxAPush_DeviceDisconnected_ThisDevice_body" = "已成功自 Firefox Sync 取消鏈結此裝置。"; +"FxAPush_DeviceDisconnected_ThisDevice_body" = "已成功自 Ecosia Sync 取消鏈結此裝置。"; /* Title of a notification displayed when this device has been disconnected by another device. */ "FxAPush_DeviceDisconnected_ThisDevice_title" = "已與 Sync 取消連線"; @@ -419,10 +419,10 @@ "FxAPush_DeviceDisconnected_title" = "已與 Sync 取消連線"; /* Body of a notification displayed when unnamed device has been disconnected from FxA. */ -"FxAPush_DeviceDisconnected_UnknownDevice_body" = "有一台裝置已與 Firefox 取消連線"; +"FxAPush_DeviceDisconnected_UnknownDevice_body" = "有一台裝置已與 Ecosia 取消連線"; /* On the firefox homepage, the string read by the voice over prompt for accessibility, for the button which changes the wallpaper */ -"FxHomepage.Wallpaper.ButtonLabel.v99" = "Firefox 圖示,點擊此圖示變更背景圖。"; +"FxHomepage.Wallpaper.ButtonLabel.v99" = "Ecosia 圖示,點擊此圖示變更背景圖。"; /* Show the SUMO support page from the Support section in the settings. see http://mzl.la/1dmM8tZ */ "Help" = "說明"; @@ -455,7 +455,7 @@ "HistoryPanel.EmptySyncedTabsPanelNotSignedInState.Description" = "登入以檢視您其他裝置中的分頁清單。"; /* Title for the empty synced tabs state in the History Panel */ -"HistoryPanel.EmptySyncedTabsState.Title" = "Firefox Sync"; +"HistoryPanel.EmptySyncedTabsState.Title" = "Ecosia Sync"; /* Title for the Back to History button in the History Panel */ "HistoryPanel.HistoryBackButton.Title" = "瀏覽紀錄"; @@ -653,13 +653,13 @@ "Logins.Onboarding.LearnMoreButtonTitle" = "了解更多"; /* Warning message shown when you try to enable or use native AutoFill without a device passcode setup */ -"Logins.PasscodeRequirement.Warning" = "若要使用 Firefox 的「自動填寫」功能,必須先設定使用裝置解鎖碼。"; +"Logins.PasscodeRequirement.Warning" = "若要使用 Ecosia 的「自動填寫」功能,必須先設定使用裝置解鎖碼。"; /* Label displaying welcome view tagline under the title */ "Logins.WelcomeView.Tagline" = "密碼隨身帶著走"; /* Label displaying welcome view title */ -"Logins.WelcomeView.Title2" = "自動填寫 Firefox 密碼"; +"Logins.WelcomeView.Title2" = "自動填寫 Ecosia 密碼"; /* Title of the big blue button to enable AutoFill */ "Logins.WelcomeView.TurnOnAutoFill" = "開啟密碼「自動填寫」功能"; @@ -704,7 +704,7 @@ "LoginsList.Title" = "儲存的登入資訊"; /* Restore Tabs Prompt Description */ -"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "看來 Firefox 之前掛掉了,要還原您先前的分頁嗎?"; +"Looks like Firefox crashed previously. Would you like to restore your tabs?" = "看來 Ecosia 之前掛掉了,要還原您先前的分頁嗎?"; /* Title for the button that marks a reading list item as read */ "Mark as Read" = "標示為已讀"; @@ -893,7 +893,7 @@ "Onboarding.WhatsNew.Button.Title" = "開始上網"; /* Title for prompt displayed to user after the app crashes */ -"Oops! Firefox crashed" = "喔喔,Firefox 發生錯誤"; +"Oops! Firefox crashed" = "喔喔,Ecosia 發生錯誤"; /* See http://mzl.la/1LXbDOL */ "Open articles in Reader View by tapping the book icon when it appears in the title bar." = "點選標題列出現的書本圖示,即可將該頁面於閱讀畫面開啟。"; @@ -905,7 +905,7 @@ "Open Tabs" = "開啟分頁"; /* The message displayed to a user when they try to open a URL that cannot be handled by Firefox, or any external app. */ -"OpenURL.Error.Message" = "由於網址錯誤,Firefox 無法開啟頁面。"; +"OpenURL.Error.Message" = "由於網址錯誤,Ecosia 無法開啟頁面。"; /* Title of the message shown when the user attempts to navigate to an invalid link. */ "OpenURL.Error.Title" = "無法開啟頁面"; @@ -917,7 +917,7 @@ "PhotoLibrary.FirefoxWouldLikeAccessMessage" = "讓您可儲存照片到「照片串流」。"; /* See http://mzl.la/1G7uHo7 */ -"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Firefox 想要存取您的照片"; +"PhotoLibrary.FirefoxWouldLikeAccessTitle" = "Ecosia 想要存取您的照片"; /* Button for closing the menu action sheet */ "PhotonMenu.close" = "關閉"; @@ -1004,7 +1004,7 @@ "ScanQRCode.InvalidDataError.Message" = "資料無效"; /* Text of the prompt to setup the camera authorization for the Scan QR Code feature. */ -"ScanQRCode.PermissionError.Message.v100" = "請到裝置的「設定」>「Firefox」允許 Firefox 存取攝影機。"; +"ScanQRCode.PermissionError.Message.v100" = "請到裝置的「設定」>「Ecosia」允許 Ecosia 存取攝影機。"; /* Title for the QR code scanner view. */ "ScanQRCode.View.Title" = "掃描 QR Code"; @@ -1019,7 +1019,7 @@ "Search.Awesomebar.SwitchToTab" = "切換至該分頁"; /* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a header to separate Firefox suggestions from normal suggestions. */ -"Search.SuggestSectionTitle.v102" = "Firefox 建議"; +"Search.SuggestSectionTitle.v102" = "Ecosia 建議"; /* The message that asks the user to Add the search provider explaining where the search engine will appear */ "Search.ThirdPartyEngines.AddMessage" = "新的搜尋引擎將顯示於快速搜尋列。"; @@ -1055,7 +1055,7 @@ "Search.ThirdPartyEngines.OK" = "確定"; /* Message displayed in the crash dialog above the buttons used to select when sending reports */ -"Send a crash report so Mozilla can fix the problem?" = "要傳送錯誤報告,讓 Mozilla 改善這些問題嗎?"; +"Send a crash report so Mozilla can fix the problem?" = "要傳送錯誤報告,讓 Ecosia 改善這些問題嗎?"; /* Menu item in settings used to open input.mozilla.org where people can submit feedback */ "Send Feedback" = "傳送意見回饋"; @@ -1085,13 +1085,13 @@ "SendTo.NavBar.Title" = "傳送分頁"; /* Error message shown in the remote tabs panel */ -"SendTo.NoDevicesFound.Message" = "您沒有其他連結到此 Firefox 帳號的裝置可進行同步。"; +"SendTo.NoDevicesFound.Message" = "您沒有其他連結到此 Ecosia 帳號的裝置可進行同步。"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Message" = "請開啟 Firefox,到「設定」登入後繼續。"; +"SendTo.NotSignedIn.Message" = "請開啟 Ecosia,到「設定」登入後繼續。"; /* See http://mzl.la/1ISlXnU */ -"SendTo.NotSignedIn.Title" = "您尚未登入 Firefox 帳號。"; +"SendTo.NotSignedIn.Title" = "您尚未登入 Ecosia 帳號。"; /* Navigation bar button to Send the current page to a device */ "SendTo.SendAction.Text" = "傳送"; @@ -1112,7 +1112,7 @@ "SentTab.NoTabArrivingNotification.body" = "點擊以開始"; /* Title of notification received after a spurious message from FxA has been received. */ -"SentTab.NoTabArrivingNotification.title" = "Firefox Sync"; +"SentTab.NoTabArrivingNotification.title" = "Ecosia Sync"; /* Label for an action used to view one or more tabs from a notification. */ "SentTab.ViewAction.title" = "檢視"; @@ -1175,7 +1175,7 @@ "Settings.DataManagement.Title" = "資料管理"; /* Body of the alert when prompting the user asking to disconnect. */ -"Settings.Disconnect.Body" = "Firefox 將會停止與您帳號間的同步,但不會刪除此裝置上的任何瀏覽資料。"; +"Settings.Disconnect.Body" = "Ecosia 將會停止與您帳號間的同步,但不會刪除此裝置上的任何瀏覽資料。"; /* Button displayed at the bottom of settings page allowing users to Disconnect from FxA */ "Settings.Disconnect.Button" = "取消連線至 Sync"; @@ -1241,7 +1241,7 @@ "Settings.FxA.Sync.SectionName" = "同步設定"; /* Title displayed in header of the FxA settings panel. */ -"Settings.FxA.Title" = "Firefox 帳號"; +"Settings.FxA.Title" = "Ecosia 帳號"; /* General settings section title */ "Settings.General.SectionName" = "一般"; @@ -1250,7 +1250,7 @@ "Settings.Home.Current.Description.v101" = "選擇要在首頁顯示哪些內容。"; /* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ -"Settings.Home.Option.Description.v101" = "選擇要在 Firefox 首頁看到哪些東西。"; +"Settings.Home.Option.Description.v101" = "選擇要在 Ecosia 首頁看到哪些東西。"; /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "回到先前開啟頁面"; @@ -1274,7 +1274,7 @@ "Settings.Home.Option.StartAtHome.Always" = "首頁"; /* In the settings menu, in the Start at Home customization options, this is text that appears below the section, describing what the section settings do. */ -"Settings.Home.Option.StartAtHome.Description" = "選擇回到 Firefox 時想看到什麼。"; +"Settings.Home.Option.StartAtHome.Description" = "選擇回到 Ecosia 時想看到什麼。"; /* In the settings menu, on the Start at Home homepage customization option, this allows users to set this setting to return to the last tab they were on, every time they open up Firefox */ "Settings.Home.Option.StartAtHome.Never" = "最後開啟的分頁"; @@ -1289,25 +1289,25 @@ "Settings.Home.Option.Wallpaper" = "背景圖"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "紫水晶圖案的 Firefox 背景圖。"; +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "紫水晶圖案的 Ecosia 背景圖。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "海灘山丘圖案的 Firefox 背景圖。"; +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "海灘山丘圖案的 Ecosia 背景圖。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "蔚藍圖案的 Firefox 背景圖。"; +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "蔚藍圖案的 Ecosia 背景圖。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "預設的明亮 Firefox 背景圖"; +"Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "預設的明亮 Ecosia 背景圖"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "日出圖案的 Firefox 背景圖。"; +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "日出圖案的 Ecosia 背景圖。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ "Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "首頁背景圖切換開關"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ -"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "暮光山丘圖案的 Firefox 背景圖。"; +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "暮光山丘圖案的 Ecosia 背景圖。"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "經典 %@"; @@ -1328,7 +1328,7 @@ "Settings.Home.Option.Wallpaper.LimitedEdition.Title.v106" = "限量版"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the string titling the switch button's function, which allows a user to toggle wallpaper switching from the homepage logo on or off. */ -"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "點擊 Firefox 首頁的圖示即可更改背景圖案"; +"Settings.Home.Option.Wallpaper.SwitchTitle.v99" = "點擊 Ecosia 首頁的圖示即可更改背景圖案"; /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of that screen, which allows users to change the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper.Title" = "背景圖"; @@ -1382,7 +1382,7 @@ "Settings.NewTab.Option.Custom" = "自訂"; /* Option in settings to show Firefox Home when you open a new tab */ -"Settings.NewTab.Option.FirefoxHome" = "Firefox 首頁"; +"Settings.NewTab.Option.FirefoxHome" = "Ecosia 首頁"; /* Option in settings to show your homepage when you open a new tab */ "Settings.NewTab.Option.HomePage" = "首頁"; @@ -1403,13 +1403,13 @@ "Settings.NoImageModeBlockImages.Label.v99" = "封鎖圖片"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.Status" = "開啟 Firefox 時"; +"Settings.OfferClipboardBar.Status" = "開啟 Ecosia 時"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ "Settings.OfferClipboardBar.Status.v128" = "開啟 %@ 時"; /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ -"Settings.OfferClipboardBar.StatusV2" = "開啟 Firefox 時"; +"Settings.OfferClipboardBar.StatusV2" = "開啟 Ecosia 時"; /* Title of setting to enable the Go to Copied URL feature. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Title" = "詢問是否直接開啟複製的鏈結"; @@ -1445,7 +1445,7 @@ "Settings.SendUsage.Link" = "了解更多。"; /* A short description that explains why mozilla collects usage data. */ -"Settings.SendUsage.Message" = "Mozilla 致力於只收集用來為眾人改善 Firefox 所需的必要資料。"; +"Settings.SendUsage.Message" = "Ecosia 致力於只收集用來為眾人改善 Ecosia 所需的必要資料。"; /* The title for the setting to send usage data. */ "Settings.SendUsage.Title" = "傳送使用資料"; @@ -1466,7 +1466,7 @@ "Settings.Siri.OpenTabShortcut" = "開啟新分頁"; /* The description that describes what siri shortcuts are */ -"Settings.Siri.SectionDescription" = "透過 Siri 使用捷徑快速開啟 Firefox"; +"Settings.Siri.SectionDescription" = "透過 Siri 使用捷徑快速開啟 Ecosia"; /* The option that takes you to the siri shortcuts settings page */ "Settings.Siri.SectionName" = "Siri 捷徑"; @@ -1475,7 +1475,7 @@ "Settings.Studies.Toggle.Link" = "了解更多。"; /* A short description that explains that Mozilla is running studies */ -"Settings.Studies.Toggle.Message" = "Firefox 會不定時安裝並執行使用者研究。"; +"Settings.Studies.Toggle.Message" = "Ecosia 會不定時安裝並執行使用者研究。"; /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "使用者研究"; @@ -1592,13 +1592,13 @@ "ShareExtension.LoadInBackgroundAction.Title" = "在背景載入"; /* Share extension label shown after user has performed 'Load in Background' action. */ -"ShareExtension.LoadInBackgroundActionDone.Title" = "正在 Firefox 載入"; +"ShareExtension.LoadInBackgroundActionDone.Title" = "正在 Ecosia 載入"; /* Action label on share extension to immediately open page in Firefox. */ -"ShareExtension.OpenInFirefoxAction.Title" = "於 Firefox 開啟"; +"ShareExtension.OpenInFirefoxAction.Title" = "於 Ecosia 開啟"; /* Action label on share extension to search for the selected text in Firefox. */ -"ShareExtension.SeachInFirefoxAction.Title" = "在 Firefox 中搜尋"; +"ShareExtension.SeachInFirefoxAction.Title" = "在 Ecosia 中搜尋"; /* Button in share sheet to copy the url of the current tab. */ "ShareSheet.Copy.Title.v108" = "複製"; @@ -1745,7 +1745,7 @@ "UIMenuItem.PasteGo" = "貼上並瀏覽"; /* Search in New Tab Text selection menu item */ -"UIMenuItem.SearchWithFirefox" = "使用 Firefox 搜尋"; +"UIMenuItem.SearchWithFirefox" = "使用 Ecosia 搜尋"; /* Accessibility label for unread article in reading list. It's a past participle - functions as an adjective. */ "unread" = "未讀"; @@ -1793,7 +1793,7 @@ "Yesterday" = "昨天"; /* Error message in the remote tabs panel */ -"You don’t have any tabs open in Firefox on your other devices." = "您其他裝置上的 Firefox 沒有任何開啟的分頁。"; +"You don’t have any tabs open in Firefox on your other devices." = "您其他裝置上的 Ecosia 沒有任何開啟的分頁。"; /* Your Rights settings section title */ "Your Rights" = "您的權利"; diff --git a/firefox-ios/Shared/zh-TW.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/zh-TW.lproj/PrivateBrowsing.strings index 4e5700656cd72..08d69f5837b61 100644 --- a/firefox-ios/Shared/zh-TW.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/zh-TW.lproj/PrivateBrowsing.strings @@ -5,7 +5,7 @@ "ContextMenu.OpenInNewPrivateTabButtonTitle" = "用新隱私分頁開啟"; /* Description text displayed when there are no open tabs while in private mode */ -"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox 不會留下瀏覽紀錄或 cookie,但會保留建立的書籤。"; +"Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Ecosia 不會留下瀏覽紀錄或 cookie,但會保留建立的書籤。"; /* Text button displayed when there are no tabs open while in private mode */ "Learn More" = "瞭解更多"; diff --git a/firefox-ios/Shared/zh-TW.lproj/Today.strings b/firefox-ios/Shared/zh-TW.lproj/Today.strings index 3f3f2e8bd8067..85ac7f8023bd8 100644 --- a/firefox-ios/Shared/zh-TW.lproj/Today.strings +++ b/firefox-ios/Shared/zh-TW.lproj/Today.strings @@ -5,7 +5,7 @@ "TodayWidget.ClosePrivateTabsLabelV2" = "關閉隱私分頁"; /* Description for medium size widget to add Firefox Shortcut to home screen */ -"TodayWidget.FirefoxShortcutGalleryDescription" = "將 Firefox 捷徑加入主畫面。"; +"TodayWidget.FirefoxShortcutGalleryDescription" = "將 Ecosia 捷徑加入主畫面。"; /* Go to link pasted on the clipboard */ "TodayWidget.GoToCopiedLinkLabelV1" = "開啟複製的鏈結"; @@ -23,19 +23,19 @@ "TodayWidget.NoOpenTabsLabel" = "沒有開啟的分頁。"; /* Open Firefox when there are no tabs opened in tab tray i.e. Empty State */ -"TodayWidget.OpenFirefoxLabel" = "開啟 Firefox"; +"TodayWidget.OpenFirefoxLabel" = "開啟 Ecosia"; /* Open New Private Tab button label */ "TodayWidget.PrivateTabButtonLabelV1" = "隱私搜尋"; /* Description for small size widget to add it to home screen */ -"TodayWidget.QuickActionGalleryDescription" = "把 Firefox 捷徑加到主畫面。新增小工具之後,請長按小工具選擇不同的捷徑。"; +"TodayWidget.QuickActionGalleryDescription" = "把 Ecosia 捷徑加到主畫面。新增小工具之後,請長按小工具選擇不同的捷徑。"; /* Quick Actions title when widget enters edit mode */ "TodayWidget.QuickActionsGalleryTitle" = "快速操作"; /* Firefox shortcuts title when widget enters edit mode. Do not translate the word Firefox. */ -"TodayWidget.QuickActionsGalleryTitleV2" = "Firefox 捷徑"; +"TodayWidget.QuickActionsGalleryTitleV2" = "Ecosia 捷徑"; /* Description for Quick View widget in Gallery View where user can add it to home screen */ "TodayWidget.QuickViewGalleryDescriptionV2" = "加入已開啟分頁的捷徑。"; @@ -44,7 +44,7 @@ "TodayWidget.QuickViewGalleryTitle" = "快速檢視"; /* Search in Firefox. Do not translate the word Firefox */ -"TodayWidget.SearchInFirefoxV2" = "用 Firefox 搜尋"; +"TodayWidget.SearchInFirefoxV2" = "用 Ecosia 搜尋"; /* Search in private tab */ "TodayWidget.SearchInPrivateTabLabelV2" = "用隱私分頁搜尋"; diff --git a/firefox-ios/Storage/DefaultSuggestedSites.swift b/firefox-ios/Storage/DefaultSuggestedSites.swift index db3bcb759ace7..44b6912b3fb15 100644 --- a/firefox-ios/Storage/DefaultSuggestedSites.swift +++ b/firefox-ios/Storage/DefaultSuggestedSites.swift @@ -3,8 +3,11 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +import Ecosia +import SiteImageView open class DefaultSuggestedSites { + /* Ecosia: Remove Default Suggested Sites private static let urlMap = [ "https://www.amazon.com/": [ "as": "https://www.amazon.in", @@ -129,8 +132,10 @@ open class DefaultSuggestedSites { ) ] ] + */ public static func defaultSites() -> [Site] { + /* Ecosia: Remove default sites let locale = Locale.current let defaultSites = sites[locale.identifier] ?? sites["default"] return defaultSites?.map { data in @@ -142,5 +147,7 @@ open class DefaultSuggestedSites { } return data } ?? [] + */ + [] } } diff --git a/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift b/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift index 931998177e50a..af7f3cbb21592 100644 --- a/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift +++ b/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift @@ -4,7 +4,7 @@ import Foundation -import class MozillaAppServices.SuggestStore +@preconcurrency import class MozillaAppServices.SuggestStore import class MozillaAppServices.SuggestStoreBuilder import class MozillaAppServices.Viaduct import enum MozillaAppServices.SuggestionProvider @@ -12,6 +12,7 @@ import enum MozillaAppServices.RemoteSettingsServer import struct MozillaAppServices.SuggestIngestionConstraints import struct MozillaAppServices.SuggestionQuery +@preconcurrency public protocol RustFirefoxSuggestProtocol { /// Downloads and stores new Firefox Suggest suggestions. func ingest() async throws @@ -32,6 +33,7 @@ public protocol RustFirefoxSuggestProtocol { /// Wraps the synchronous Rust `SuggestStore` binding to execute /// blocking operations on a dispatch queue. +@preconcurrency public class RustFirefoxSuggest: RustFirefoxSuggestProtocol { private let store: SuggestStore diff --git a/firefox-ios/Storage/Rust/RustLogins.swift b/firefox-ios/Storage/Rust/RustLogins.swift index 8262410037b00..f816a847f9a84 100644 --- a/firefox-ios/Storage/Rust/RustLogins.swift +++ b/firefox-ios/Storage/Rust/RustLogins.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Foundation +/* Ecosia: remove Glean dependency import Glean +*/ import Shared import Common @@ -1080,7 +1082,9 @@ public class RustLogins: LoginsProtocol { self.logger.log("Logins key was corrupted, new one generated", level: .warning, category: .storage) + /* Ecosia: Remove Glean GleanMetrics.LoginsStoreKeyRegeneration.corrupt.record() + */ self.resetLoginsAndKey(rustKeys: rustKeys, completion: completion) } } catch let error as NSError { @@ -1096,7 +1100,9 @@ public class RustLogins: LoginsProtocol { self.logger.log("Logins key lost due to storage malfunction, new one generated", level: .warning, category: .storage) + /* Ecosia: Remove Glean GleanMetrics.LoginsStoreKeyRegeneration.other.record() + */ self.resetLoginsAndKey(rustKeys: rustKeys, completion: completion) case (.none, .some(encryptedCanaryPhrase)): // We expected the key to be present, but it's gone missing on us. @@ -1104,7 +1110,9 @@ public class RustLogins: LoginsProtocol { self.logger.log("Logins key lost, new one generated", level: .warning, category: .storage) + /* Ecosia: Remove Glean GleanMetrics.LoginsStoreKeyRegeneration.lost.record() + */ self.resetLoginsAndKey(rustKeys: rustKeys, completion: completion) case (.none, .none): // We didn't expect the key to be present, which either means this is a first-time @@ -1125,7 +1133,9 @@ public class RustLogins: LoginsProtocol { if hasLogins { // Since the key data isn't present and we have login records in // the database, we both clear the database and reset the key. + /* Ecosia: Remove Glean GleanMetrics.LoginsStoreKeyRegeneration.keychainDataLost.record() + */ self.resetLoginsAndKey(rustKeys: rustKeys, completion: completion) } else { // There are no records in the database so we don't need to wipe any diff --git a/firefox-ios/Storage/Rust/RustPlaces.swift b/firefox-ios/Storage/Rust/RustPlaces.swift index 01c87c62d57a6..72e7b5b739b9a 100644 --- a/firefox-ios/Storage/Rust/RustPlaces.swift +++ b/firefox-ios/Storage/Rust/RustPlaces.swift @@ -647,7 +647,9 @@ extension RustPlaces { // as the title title = info.url } - let site = Site(url: info.url, title: title) + // Note: FXIOS-10740 Necessary to have unique Site ID iOS 18 HistoryPanel crash with diffable data sources + let hashValue = "\(info.url)_\(info.timestamp)".hashValue + let site = Site(id: hashValue, url: info.url, title: title) site.latestVisit = Visit(date: UInt64(info.timestamp) * 1000, type: info.visitType) return site }.uniqued() diff --git a/firefox-ios/Storage/Site.swift b/firefox-ios/Storage/Site.swift index fb4e1418ea08c..4edd6433b8303 100644 --- a/firefox-ios/Storage/Site.swift +++ b/firefox-ios/Storage/Site.swift @@ -55,6 +55,11 @@ open class Site: Identifiable { self.init(url: url, title: title, bookmarked: false, guid: nil, faviconResource: nil) } + public convenience init(id: Int, url: String, title: String) { + self.init(url: url, title: title, bookmarked: false, guid: nil, faviconResource: nil) + self.id = id + } + public init(url: String, title: String, bookmarked: Bool?, @@ -91,10 +96,20 @@ open class Site: Identifiable { // MARK: - Hashable extension Site: Hashable { public func hash(into hasher: inout Hasher) { + // The == operator below must match the same requirements as this method hasher.combine(id) + hasher.combine(guid) + hasher.combine(title) + hasher.combine(url) + hasher.combine(faviconResource) } public static func == (lhs: Site, rhs: Site) -> Bool { - lhs.url == rhs.url + // The hash method above must match the same requirements as this operator + return lhs.id == rhs.id + && lhs.guid == rhs.guid + && lhs.title == rhs.title + && lhs.url == rhs.url + && lhs.faviconResource == rhs.faviconResource } } diff --git a/firefox-ios/WidgetKit/Base.lproj/WidgetIntents.intentdefinition b/firefox-ios/WidgetKit/Base.lproj/WidgetIntents.intentdefinition index 160911300b968..048942125412d 100644 --- a/firefox-ios/WidgetKit/Base.lproj/WidgetIntents.intentdefinition +++ b/firefox-ios/WidgetKit/Base.lproj/WidgetIntents.intentdefinition @@ -73,18 +73,18 @@ INIntentDefinitionNamespace 5W2MbK INIntentDefinitionSystemVersion - 20E5196f + 23C71 INIntentDefinitionToolsBuildVersion - 12D4e + 15C500b INIntentDefinitionToolsVersion - 12.4 + 15.2 INIntents INIntentCategory information INIntentDescription - Quick access to various Firefox actions + Quick access to various Ecosia actions INIntentDescriptionID ctDNmu INIntentEligibleForWidgets diff --git a/firefox-ios/WidgetKit/ImageButtonWithLabel.swift b/firefox-ios/WidgetKit/ImageButtonWithLabel.swift index 8906e09683d8c..64cd6cacdb487 100644 --- a/firefox-ios/WidgetKit/ImageButtonWithLabel.swift +++ b/firefox-ios/WidgetKit/ImageButtonWithLabel.swift @@ -5,6 +5,7 @@ #if canImport(WidgetKit) import SwiftUI import Common +import Ecosia // View for Quick Action Widget Buttons (Small & Medium) // +-------------------------------------------------------+ @@ -82,14 +83,19 @@ struct ImageButtonWithLabel: View { .font(.headline) .minimumScaleFactor(0.75) .layoutPriority(1000) + // Ecosia: add color + .foregroundColor(link.textColor) } else { Text(link.label) .font(.footnote) .minimumScaleFactor(0.75) .layoutPriority(1000) + // Ecosia: add color + .foregroundColor(link.textColor) } } Spacer() + /* Ecosia: Update image if link == .search && isSmall { Image(decorative: StandardImageIdentifiers.Large.search) .scaledToFit() @@ -99,17 +105,31 @@ struct ImageButtonWithLabel: View { .scaledToFit() .frame(height: 24.0) } + */ + Image(decorative: link.imageName, bundle: .ecosia) + .scaledToFit() + .frame(height: 24.0) + .foregroundColor(link.iconColor) } if isSmall { HStack(alignment: .bottom) { Spacer() + /* Ecosia: Update image Image(decorative: "faviconFox") + .scaledToFit() + .frame(height: 24.0) + */ + Image(decorative: "iconLogo", bundle: .ecosia) .scaledToFit() .frame(height: 24.0) + .foregroundColor(link.iconColor) } } } + /* Ecosia: Update color .foregroundColor(Color("widgetLabelColors")) + */ + .foregroundColor(Color.ecosiaBundledColorWithName("widgetLabelColors")) .padding([.horizontal, .vertical], paddingValue) } } diff --git a/firefox-ios/WidgetKit/Info.plist b/firefox-ios/WidgetKit/Info.plist index 10088bb1014c0..59546e4ae097c 100644 --- a/firefox-ios/WidgetKit/Info.plist +++ b/firefox-ios/WidgetKit/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 133.0 + $(MARKETING_VERSION) CFBundleVersion 1 MozDevelopmentTeam diff --git a/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift b/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift index 0ba727176fcaa..5238a20d85475 100644 --- a/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift +++ b/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift @@ -7,6 +7,9 @@ import WidgetKit import UIKit import Combine import Common +// Ecosia: Additional imports for Ecosia framework and suggested sites updates +import Ecosia +import Storage struct OpenTabsWidget: Widget { private let kind: String = "Quick View" @@ -25,7 +28,10 @@ struct OpenTabsWidget: Widget { struct OpenTabsView: View { let entry: OpenTabsEntry + /* Ecosia: Update Environment state definition @Environment(\.widgetFamily) + */ + @SwiftUI.Environment(\.widgetFamily) var widgetFamily @ViewBuilder @@ -43,7 +49,10 @@ struct OpenTabsView: View { } Text(tab.title!) + /* Ecosia: update color .foregroundColor(Color.white) + */ + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")) .multilineTextAlignment(.leading) .lineLimit(1) .font(.system(size: 15, weight: .regular, design: .default)) @@ -51,7 +60,10 @@ struct OpenTabsView: View { } Rectangle() + /* Ecosia: update color .fill(Color(UIColor(red: 0.85, green: 0.85, blue: 0.85, alpha: 0.3))) + */ + .fill(Color.ecosiaBundledColorWithName("Border")) .frame(height: 0.5) .padding(.leading, 45) } @@ -59,9 +71,16 @@ struct OpenTabsView: View { var openFirefoxButton: some View { HStack(alignment: .center, spacing: 15) { + /* Ecosia: Update image and color Image(decorative: StandardImageIdentifiers.Small.externalLink).foregroundColor(Color.white) - Text("Open Firefox") + */ + Image(decorative: "openEcosia", bundle: .ecosia) + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")) + Text("Open Firefox") // Ecosia: Leave "Open Firefox" as within the WidgetKit Strings the key matches. + /* Ecosia: update color .foregroundColor(Color.white).lineLimit(1) + */ + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")).lineLimit(1) .font(.system(size: 13, weight: .semibold, design: .default)) Spacer() }.padding([.horizontal]) @@ -82,13 +101,22 @@ struct OpenTabsView: View { Text(String.NoOpenTabsLabel) HStack { Spacer() + /* Ecosia: Update image Image(decorative: StandardImageIdentifiers.Small.externalLink) + */ + Image(decorative: "openEcosia", bundle: .ecosia) Text(String.OpenFirefoxLabel) + /* Ecosia: Update color .foregroundColor(Color.white).lineLimit(1) + */ + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")).lineLimit(1) .font(.system(size: 13, weight: .semibold, design: .default)) Spacer() }.padding(10) - }.foregroundColor(Color.white) + } + // Ecosia: update color + // .foregroundColor(Color.white) + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")) } else { VStack(spacing: 8) { ForEach(entry.tabs.suffix(numberOfTabsToDisplay), id: \.self) { tab in @@ -97,8 +125,12 @@ struct OpenTabsView: View { if entry.tabs.count > numberOfTabsToDisplay { HStack(alignment: .center, spacing: 15) { + /* Update image and color Image(decorative: StandardImageIdentifiers.Small.externalLink) .foregroundColor(Color.white) + */ + Image(decorative: "openEcosia", bundle: .ecosia) + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")) .frame(width: 16, height: 16) Text( String.localizedStringWithFormat( @@ -106,7 +138,10 @@ struct OpenTabsView: View { (entry.tabs.count - numberOfTabsToDisplay) ) ) + /* Ecosia: Update color .foregroundColor(Color.white) + */ + .foregroundColor(.ecosiaBundledColorWithName("PrimaryText")) .lineLimit(1) .font(.system(size: 13, weight: .semibold, design: .default)) Spacer() @@ -120,7 +155,10 @@ struct OpenTabsView: View { } } .frame(maxWidth: .infinity, maxHeight: .infinity) + /* Ecosia: update color .widgetBackground(Color(UIColor(red: 0.11, green: 0.11, blue: 0.13, alpha: 1.00))) + */ + .background((Color.ecosiaBundledColorWithName("PrimaryBackground"))) } private func linkToContainingApp(_ urlSuffix: String = "", query: String) -> URL { diff --git a/firefox-ios/WidgetKit/QuickLink.swift b/firefox-ios/WidgetKit/QuickLink.swift index c513707c545ff..c10216f2e0352 100644 --- a/firefox-ios/WidgetKit/QuickLink.swift +++ b/firefox-ios/WidgetKit/QuickLink.swift @@ -4,6 +4,7 @@ import SwiftUI import Common +import Ecosia /// Enum file that holds the different cases for the Quick Actions small widget with their /// configurations (string, backgrounds, images) as selected by the user in edit mode. @@ -16,13 +17,21 @@ enum QuickLink: Int { public var imageName: String { switch self { case .search: - return "faviconFox" + // Ecosia: update image + // return "faviconFox" + return "openEcosia" case .privateSearch: + /* Ecosia: update image return StandardImageIdentifiers.Large.privateMode + */ + return "ecosiaSmallPrivateMask" case .copiedLink: return StandardImageIdentifiers.Large.tabTray case .closePrivateTabs: - return StandardImageIdentifiers.Large.delete + /* Ecosia: update image + StandardImageIdentifiers.Large.delete + */ + return "ecosiaDelete" } } @@ -65,6 +74,7 @@ enum QuickLink: Int { } } + /* Ecosia: Update colors public var backgroundColors: [Color] { switch self { case .search: @@ -77,4 +87,45 @@ enum QuickLink: Int { return [Color("privateGradientThree"), Color("privateGradientTwo"), Color("privateGradientOne")] } } + */ + + public var backgroundColors: [Color] { + /* Ecosia: Update colors + switch self { + case .search: + return [Color("PrimaryBrand")] + case .privateSearch: + return [Color("TertiaryBackground")] + case .copiedLink: + return [Color("TertiaryBackground")] + case .closePrivateTabs: + return [Color("TertiaryBackground")] + } + */ + return [.ecosiaBundledColorWithName("TertiaryBackground")] + } + + public var textColor: Color { + /* Ecosia: Update colors + switch self { + case .search: + return .init("PrimaryBackground") + default: + return .init("PrimaryText") + } + */ + return .ecosiaBundledColorWithName("PrimaryText") + } + + public var iconColor: Color { + /* Ecosia: Update colors + switch self { + case .search: + return .init("PrimaryBackground") + default: + return .init("SecondaryIcon") + } + */ + return .ecosiaBundledColorWithName("PrimaryText") + } } diff --git a/firefox-ios/WidgetKit/SearchQuickLinksMedium/SearchQuickLinks.swift b/firefox-ios/WidgetKit/SearchQuickLinksMedium/SearchQuickLinks.swift index edd356be3db14..d06163ffff581 100644 --- a/firefox-ios/WidgetKit/SearchQuickLinksMedium/SearchQuickLinks.swift +++ b/firefox-ios/WidgetKit/SearchQuickLinksMedium/SearchQuickLinks.swift @@ -42,7 +42,10 @@ struct SearchQuickLinksEntryView: View { } } .padding(10.0) + /* Ecosia: Update color .widgetBackground(Color("backgroundColor")) + */ + .widgetBackground(Color.ecosiaBundledColorWithName("PrimaryBackground")) } } diff --git a/firefox-ios/WidgetKit/TopSites/TopSitesWidget.swift b/firefox-ios/WidgetKit/TopSites/TopSitesWidget.swift index a8036c60eee2a..ae3a9e0d6710d 100644 --- a/firefox-ios/WidgetKit/TopSites/TopSitesWidget.swift +++ b/firefox-ios/WidgetKit/TopSites/TopSitesWidget.swift @@ -5,6 +5,9 @@ import SwiftUI import WidgetKit import Combine +// Ecosia: Additional imports for Ecosia framework and suggested sites updates +import Ecosia +import Storage struct TopSitesWidget: Widget { private let kind: String = "Top Sites" @@ -47,8 +50,14 @@ struct TopSitesView: View { topSitesItem(site, iconSize: itemSize) .frame(height: rowSize) } else { + /* Ecosia: Update rectangle Rectangle() + */ + RoundedRectangle(cornerRadius: UX.itemCornerRadius) + /* Ecosia: Update color .fill(Color.clear) + */ + .fill(Color.ecosiaBundledColorWithName("TertiaryBackground")) .frame(height: rowSize) .overlay { RoundedRectangle(cornerRadius: UX.itemCornerRadius) @@ -62,7 +71,10 @@ struct TopSitesView: View { .padding(.all) } .frame(maxWidth: .infinity, maxHeight: .infinity) + /* Ecosia: Update color .widgetBackground(UX.widgetBackgroundColor) + */ + .widgetBackground(Color.ecosiaBundledColorWithName("PrimaryBackground")) } @ViewBuilder diff --git a/firefox-ios/WidgetKit/ab.lproj/Localizable.strings b/firefox-ios/WidgetKit/ab.lproj/Localizable.strings new file mode 100644 index 0000000000000..d4be6de02bd26 --- /dev/null +++ b/firefox-ios/WidgetKit/ab.lproj/Localizable.strings @@ -0,0 +1,3 @@ +/* No comment provided by engineer. */ +"Open Firefox" = "Иаарттәуп Ecosia"; + diff --git a/firefox-ios/WidgetKit/ab.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ab.lproj/WidgetIntents.strings new file mode 100644 index 0000000000000..cda5a89b7aef9 --- /dev/null +++ b/firefox-ios/WidgetKit/ab.lproj/WidgetIntents.strings @@ -0,0 +1,48 @@ +/* (No Comment) */ +"2GqvPe" = "Иқәҭыху азхьарԥш шәацныҟәа"; + +/* (No Comment) */ +"ctDNmu" = "Еиуеиԥшым Ecosia аусқәа рахь ирласны анеира"; + +/* (No Comment) */ +"eHmH1H" = "Ахатә абларҭақәа арктәуп"; + +/* (No Comment) */ +"eqyNJg" = "Ирццаку аусқәа"; + +/* (No Comment) */ +"eV8mOT" = "Ирццаку аус ахкы"; + +/* (No Comment) */ +"fi3W24-2GqvPe" = "Иҟоуп ${count} амҩа(қәа) “Иқәҭыху азхьарԥш шәацныҟәа”."; + +/* (No Comment) */ +"fi3W24-eHmH1H" = "Иҟоуп ${count} амҩа(қәа) “Хаҭалатәи аҭаӡҩырақәа рыркразы”."; + +/* (No Comment) */ +"fi3W24-scEmjs" = "Иҟоуп ${count} аметод(қәа) \"Хаҭалатәи аԥшаара ҿыц\" азы."; + +/* (No Comment) */ +"fi3W24-xRJbBP" = "Иҟоуп ${count} аметод(қәа) \"Аԥшаара ҿыц\" азы."; + +/* (No Comment) */ +"PzSrmZ-2GqvPe" = "Уара иуҭахума «Иқәҭыху алинк уацныҟәа»?"; + +/* (No Comment) */ +"PzSrmZ-eHmH1H" = "Уара “Закрыть частные вкладки” уҭахума?"; + +/* (No Comment) */ +"PzSrmZ-scEmjs" = "\"Хаҭалатәи аԥшаара ҿыц\" анагӡара уҭахума?"; + +/* (No Comment) */ +"PzSrmZ-xRJbBP" = "\"Аԥшаара ҿыц\" анагӡара уҭахума?"; + +/* (No Comment) */ +"scEmjs" = "Ахатә ԥшаара ҿыц"; + +/* (No Comment) */ +"w9jdPK" = "Ирццаку аусқәа"; + +/* (No Comment) */ +"xRJbBP" = "Аԥшаара ҿыц"; + diff --git a/firefox-ios/WidgetKit/af.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/af.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/af.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/af.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/an.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/an.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/an.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/an.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/anp.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/anp.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/anp.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/anp.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/ar.lproj/Localizable.strings b/firefox-ios/WidgetKit/ar.lproj/Localizable.strings index fab84c9afe84c..32ef00ae1116c 100644 --- a/firefox-ios/WidgetKit/ar.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ar.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "افتح Firefox"; +"Open Firefox" = "افتح Ecosia"; diff --git a/firefox-ios/WidgetKit/ar.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ar.lproj/WidgetIntents.strings index 411ecc80f297a..bd436454bab47 100644 --- a/firefox-ios/WidgetKit/ar.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ar.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "انتقل إلى الرابط المنسوخ"; /* (No Comment) */ -"ctDNmu" = "وصول سريع إلى مختلف إجراءات Firefox"; +"ctDNmu" = "وصول سريع إلى مختلف إجراءات Ecosia"; /* (No Comment) */ "eHmH1H" = "امسح الألسنة الخاصة"; diff --git a/firefox-ios/WidgetKit/ast.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ast.lproj/WidgetIntents.strings index 0ee34c842dbcb..08527b0506645 100644 --- a/firefox-ios/WidgetKit/ast.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ast.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/az.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/az.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/az.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/az.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/be.lproj/Localizable.strings b/firefox-ios/WidgetKit/be.lproj/Localizable.strings index a01f142dc1753..2cf27f1e9428c 100644 --- a/firefox-ios/WidgetKit/be.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/be.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Адкрыць Firefox"; +"Open Firefox" = "Адкрыць Ecosia"; diff --git a/firefox-ios/WidgetKit/be.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/be.lproj/WidgetIntents.strings index 765900ee367e8..c5ab2c9b04c2a 100644 --- a/firefox-ios/WidgetKit/be.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/be.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Перайсці па скапіяванай спасылцы"; /* (No Comment) */ -"ctDNmu" = "Хуткі доступ да розных дзеянняў Firefox"; +"ctDNmu" = "Хуткі доступ да розных дзеянняў Ecosia"; /* (No Comment) */ "eHmH1H" = "Закрыць прыватныя карткі"; diff --git a/firefox-ios/WidgetKit/bg.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/bg.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/bg.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/bg.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/bn.lproj/Localizable.strings b/firefox-ios/WidgetKit/bn.lproj/Localizable.strings index ce0ca2fed823a..80e3125ea3048 100644 --- a/firefox-ios/WidgetKit/bn.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/bn.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox খুলুন"; +"Open Firefox" = "Ecosia খুলুন"; diff --git a/firefox-ios/WidgetKit/bn.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/bn.lproj/WidgetIntents.strings index d7bc205f2b6cb..a7699885a3647 100644 --- a/firefox-ios/WidgetKit/bn.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/bn.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "কপি করা লিঙ্কে যান"; /* (No Comment) */ -"ctDNmu" = "Firefox এর বিভিন্ন পদক্ষেপের দ্রুত অ্যাক্সেস"; +"ctDNmu" = "Ecosia এর বিভিন্ন পদক্ষেপের দ্রুত অ্যাক্সেস"; /* (No Comment) */ "eHmH1H" = "ব্যক্তিগত ট্যাবগুলি বন্ধ করুন"; diff --git a/firefox-ios/WidgetKit/bo.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/bo.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/bo.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/bo.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/br.lproj/Localizable.strings b/firefox-ios/WidgetKit/br.lproj/Localizable.strings index f16f3c0b3f38a..91730fa5ceda9 100644 --- a/firefox-ios/WidgetKit/br.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/br.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Digeriñ Firefox"; +"Open Firefox" = "Digeriñ Ecosia"; diff --git a/firefox-ios/WidgetKit/br.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/br.lproj/WidgetIntents.strings index d6b56338ee465..10e708f8c7399 100644 --- a/firefox-ios/WidgetKit/br.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/br.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Mont d’an ere eilet"; /* (No Comment) */ -"ctDNmu" = "Haeziñ prim da weredoù Firefox liesseurt"; +"ctDNmu" = "Haeziñ prim da weredoù Ecosia liesseurt"; /* (No Comment) */ "eHmH1H" = "Skarzhañ an ivinelloù prevez"; diff --git a/firefox-ios/WidgetKit/bs.lproj/Localizable.strings b/firefox-ios/WidgetKit/bs.lproj/Localizable.strings index fa6be45e030ec..daba181c2bd3f 100644 --- a/firefox-ios/WidgetKit/bs.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/bs.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Otvori Firefox"; +"Open Firefox" = "Otvori Ecosia"; diff --git a/firefox-ios/WidgetKit/bs.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/bs.lproj/WidgetIntents.strings index 655589c0deac3..6f44f13952c61 100644 --- a/firefox-ios/WidgetKit/bs.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/bs.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Idi na kopirani link"; /* (No Comment) */ -"ctDNmu" = "Brzi pristup raznim Firefoxovim radnjama"; +"ctDNmu" = "Brzi pristup raznim Ecosiaovim radnjama"; /* (No Comment) */ "eHmH1H" = "Očisti privatne tabove"; diff --git a/firefox-ios/WidgetKit/ca.lproj/Localizable.strings b/firefox-ios/WidgetKit/ca.lproj/Localizable.strings index 523ab5476897b..0ad1178745724 100644 --- a/firefox-ios/WidgetKit/ca.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ca.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Obre el Firefox"; +"Open Firefox" = "Obre el Ecosia"; diff --git a/firefox-ios/WidgetKit/ca.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ca.lproj/WidgetIntents.strings index c4c5b649a05a9..dde30b38a8976 100644 --- a/firefox-ios/WidgetKit/ca.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ca.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Vés a l'enllaç copiat"; /* (No Comment) */ -"ctDNmu" = "Accés ràpid a diverses accions del Firefox"; +"ctDNmu" = "Accés ràpid a diverses accions del Ecosia"; /* (No Comment) */ "eHmH1H" = "Esborra les pestanyes privades"; diff --git a/firefox-ios/WidgetKit/co.lproj/Localizable.strings b/firefox-ios/WidgetKit/co.lproj/Localizable.strings index 752fa975e76e0..d45e0d6a13b81 100644 --- a/firefox-ios/WidgetKit/co.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/co.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Apre Firefox"; +"Open Firefox" = "Apre Ecosia"; diff --git a/firefox-ios/WidgetKit/co.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/co.lproj/WidgetIntents.strings index 993fa2bb936b1..10cf5a0477593 100644 --- a/firefox-ios/WidgetKit/co.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/co.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Apre u liame cupiatu"; /* (No Comment) */ -"ctDNmu" = "Accessu rapidu à parechje azzioni Firefox"; +"ctDNmu" = "Accessu rapidu à parechje azzioni Ecosia"; /* (No Comment) */ "eHmH1H" = "Squassà l’unghjette private"; diff --git a/firefox-ios/WidgetKit/cs.lproj/Localizable.strings b/firefox-ios/WidgetKit/cs.lproj/Localizable.strings index 2c0b805b5d4eb..c758037bccd3c 100644 --- a/firefox-ios/WidgetKit/cs.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/cs.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Otevřít Firefox"; +"Open Firefox" = "Otevřít Ecosia"; diff --git a/firefox-ios/WidgetKit/cs.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/cs.lproj/WidgetIntents.strings index 8919b7eec09f2..f5241eeb5663e 100644 --- a/firefox-ios/WidgetKit/cs.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/cs.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Přejít na zkopírovaný odkaz"; /* (No Comment) */ -"ctDNmu" = "Rychlý přístup k různým akcím ve Firefoxu"; +"ctDNmu" = "Rychlý přístup k různým akcím ve Ecosiau"; /* (No Comment) */ "eHmH1H" = "Smazat anonymní panely"; diff --git a/firefox-ios/WidgetKit/cy.lproj/Localizable.strings b/firefox-ios/WidgetKit/cy.lproj/Localizable.strings index bf23b7f326066..d2cdce69d6f68 100644 --- a/firefox-ios/WidgetKit/cy.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/cy.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Agor Firefox"; +"Open Firefox" = "Agor Ecosia"; diff --git a/firefox-ios/WidgetKit/cy.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/cy.lproj/WidgetIntents.strings index 3fc1ec5e5ffcc..c0eeb8a48f922 100644 --- a/firefox-ios/WidgetKit/cy.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/cy.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Mynd i'r Ddolen Gopïwyd"; /* (No Comment) */ -"ctDNmu" = "Mynediad cyflym i amrywiol weithredoedd Firefox"; +"ctDNmu" = "Mynediad cyflym i amrywiol weithredoedd Ecosia"; /* (No Comment) */ "eHmH1H" = "Clirio'r Tabiau Preifat"; diff --git a/firefox-ios/WidgetKit/da.lproj/Localizable.strings b/firefox-ios/WidgetKit/da.lproj/Localizable.strings index 9385bc10f5f47..de564f89791c3 100644 --- a/firefox-ios/WidgetKit/da.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/da.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Åbn Firefox"; +"Open Firefox" = "Åbn Ecosia"; diff --git a/firefox-ios/WidgetKit/da.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/da.lproj/WidgetIntents.strings index 3724ab70c2456..163ed11d902c5 100644 --- a/firefox-ios/WidgetKit/da.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/da.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Gå til kopieret link"; /* (No Comment) */ -"ctDNmu" = "Hurtig adgang til forskellige Firefox-handlinger"; +"ctDNmu" = "Hurtig adgang til forskellige Ecosia-handlinger"; /* (No Comment) */ "eHmH1H" = "Ryd private faneblade"; diff --git a/firefox-ios/WidgetKit/de.lproj/Localizable.strings b/firefox-ios/WidgetKit/de.lproj/Localizable.strings index 009dc25ebf54e..acc28f499b51b 100644 --- a/firefox-ios/WidgetKit/de.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/de.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox öffnen"; +"Open Firefox" = "Ecosia öffnen"; diff --git a/firefox-ios/WidgetKit/de.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/de.lproj/WidgetIntents.strings index d1cfb8020fa21..4a81536c77ecb 100644 --- a/firefox-ios/WidgetKit/de.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/de.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Link in der Zwischenablage aufrufen"; /* (No Comment) */ -"ctDNmu" = "Schneller Zugriff auf verschiedene Aktionen von Firefox"; +"ctDNmu" = "Schneller Zugriff auf verschiedene Aktionen von Ecosia"; /* (No Comment) */ "eHmH1H" = "Private Tabs löschen"; diff --git a/firefox-ios/WidgetKit/dsb.lproj/Localizable.strings b/firefox-ios/WidgetKit/dsb.lproj/Localizable.strings index a64ee41377239..c803ff155231e 100644 --- a/firefox-ios/WidgetKit/dsb.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/dsb.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox wócyniś"; +"Open Firefox" = "Ecosia wócyniś"; diff --git a/firefox-ios/WidgetKit/dsb.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/dsb.lproj/WidgetIntents.strings index 7505589e70ee2..68c4cf9255966 100644 --- a/firefox-ios/WidgetKit/dsb.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/dsb.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ku kopěrowanemu wótkazoju"; /* (No Comment) */ -"ctDNmu" = "Malsny pśistup k wšakim akcijam Firefox"; +"ctDNmu" = "Malsny pśistup k wšakim akcijam Ecosia"; /* (No Comment) */ "eHmH1H" = "Priwatne rejtariki wulašowaś"; diff --git a/firefox-ios/WidgetKit/el.lproj/Localizable.strings b/firefox-ios/WidgetKit/el.lproj/Localizable.strings index 25968baa1591a..1275eab3dc6b4 100644 --- a/firefox-ios/WidgetKit/el.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/el.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Άνοιγμα Firefox"; +"Open Firefox" = "Άνοιγμα Ecosia"; diff --git a/firefox-ios/WidgetKit/el.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/el.lproj/WidgetIntents.strings index 281e8dad2c11f..ef9054a116014 100644 --- a/firefox-ios/WidgetKit/el.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/el.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Μετάβαση στον αντιγραμμένο σύνδεσμο"; /* (No Comment) */ -"ctDNmu" = "Γρήγορη πρόσβαση σε διάφορες ενέργειες του Firefox"; +"ctDNmu" = "Γρήγορη πρόσβαση σε διάφορες ενέργειες του Ecosia"; /* (No Comment) */ "eHmH1H" = "Απαλοιφή ιδιωτικών καρτελών"; diff --git a/firefox-ios/WidgetKit/en-CA.lproj/Localizable.strings b/firefox-ios/WidgetKit/en-CA.lproj/Localizable.strings index 2ccef406edadc..7d47acef42fb9 100644 --- a/firefox-ios/WidgetKit/en-CA.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/en-CA.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Open Firefox"; +"Open Firefox" = "Open Ecosia"; diff --git a/firefox-ios/WidgetKit/en-CA.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/en-CA.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/en-CA.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/en-CA.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/en-GB.lproj/Localizable.strings b/firefox-ios/WidgetKit/en-GB.lproj/Localizable.strings index 2ccef406edadc..7d47acef42fb9 100644 --- a/firefox-ios/WidgetKit/en-GB.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/en-GB.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Open Firefox"; +"Open Firefox" = "Open Ecosia"; diff --git a/firefox-ios/WidgetKit/en-GB.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/en-GB.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/en-GB.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/en-GB.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/en-US.lproj/Localizable.strings b/firefox-ios/WidgetKit/en-US.lproj/Localizable.strings index 2ccef406edadc..7d47acef42fb9 100644 --- a/firefox-ios/WidgetKit/en-US.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/en-US.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Open Firefox"; +"Open Firefox" = "Open Ecosia"; diff --git a/firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/en-US.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/eo.lproj/Localizable.strings b/firefox-ios/WidgetKit/eo.lproj/Localizable.strings index e62abc37ba721..50ea75f7d9711 100644 --- a/firefox-ios/WidgetKit/eo.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/eo.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Malfermi Firefox"; +"Open Firefox" = "Malfermi Ecosia"; diff --git a/firefox-ios/WidgetKit/eo.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/eo.lproj/WidgetIntents.strings index 3abad89380e94..59c6cff7f0689 100644 --- a/firefox-ios/WidgetKit/eo.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/eo.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Iri al kopiita ligilo"; /* (No Comment) */ -"ctDNmu" = "Rapida aliro al pluraj agoj de Firefox"; +"ctDNmu" = "Rapida aliro al pluraj agoj de Ecosia"; /* (No Comment) */ "eHmH1H" = "Forigi privatajn langetojn"; diff --git a/firefox-ios/WidgetKit/es-AR.lproj/Localizable.strings b/firefox-ios/WidgetKit/es-AR.lproj/Localizable.strings index 5a6e47b996b0f..35ed572400059 100644 --- a/firefox-ios/WidgetKit/es-AR.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/es-AR.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir Firefox"; +"Open Firefox" = "Abrir Ecosia"; diff --git a/firefox-ios/WidgetKit/es-AR.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/es-AR.lproj/WidgetIntents.strings index 8cb961b379c00..5fa217d0fc550 100644 --- a/firefox-ios/WidgetKit/es-AR.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/es-AR.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir al enlace copiado"; /* (No Comment) */ -"ctDNmu" = "Acceso rápido a varias acciones de Firefox"; +"ctDNmu" = "Acceso rápido a varias acciones de Ecosia"; /* (No Comment) */ "eHmH1H" = "Eliminar pestañas privadas"; diff --git a/firefox-ios/WidgetKit/es-CL.lproj/Localizable.strings b/firefox-ios/WidgetKit/es-CL.lproj/Localizable.strings index 5a6e47b996b0f..35ed572400059 100644 --- a/firefox-ios/WidgetKit/es-CL.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/es-CL.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir Firefox"; +"Open Firefox" = "Abrir Ecosia"; diff --git a/firefox-ios/WidgetKit/es-CL.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/es-CL.lproj/WidgetIntents.strings index ec4298aa33f64..5495b3ce7c020 100644 --- a/firefox-ios/WidgetKit/es-CL.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/es-CL.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir al enlace copiado"; /* (No Comment) */ -"ctDNmu" = "Acceso rápido a varias acciones de Firefox"; +"ctDNmu" = "Acceso rápido a varias acciones de Ecosia"; /* (No Comment) */ "eHmH1H" = "Cerrar pestañas privadas"; diff --git a/firefox-ios/WidgetKit/es-MX.lproj/Localizable.strings b/firefox-ios/WidgetKit/es-MX.lproj/Localizable.strings index 5a6e47b996b0f..35ed572400059 100644 --- a/firefox-ios/WidgetKit/es-MX.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/es-MX.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir Firefox"; +"Open Firefox" = "Abrir Ecosia"; diff --git a/firefox-ios/WidgetKit/es-MX.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/es-MX.lproj/WidgetIntents.strings index 2fbe5cb5eab89..9622cf5715dd8 100644 --- a/firefox-ios/WidgetKit/es-MX.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/es-MX.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir al Enlace Copiado"; /* (No Comment) */ -"ctDNmu" = "Atajos a varias acciones de Firefox"; +"ctDNmu" = "Atajos a varias acciones de Ecosia"; /* (No Comment) */ "eHmH1H" = "Limpiar Pestañas Privadas"; diff --git a/firefox-ios/WidgetKit/es.lproj/Localizable.strings b/firefox-ios/WidgetKit/es.lproj/Localizable.strings index 5a6e47b996b0f..35ed572400059 100644 --- a/firefox-ios/WidgetKit/es.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/es.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir Firefox"; +"Open Firefox" = "Abrir Ecosia"; diff --git a/firefox-ios/WidgetKit/es.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/es.lproj/WidgetIntents.strings index 6e9acdacb26ff..0c43f1ea2f0cf 100644 --- a/firefox-ios/WidgetKit/es.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/es.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir al enlace copiado"; /* (No Comment) */ -"ctDNmu" = "Acceso rápido a varias acciones de Firefox"; +"ctDNmu" = "Acceso rápido a varias acciones de Ecosia"; /* (No Comment) */ "eHmH1H" = "Eliminar pestañas privadas"; diff --git a/firefox-ios/WidgetKit/et.lproj/Localizable.strings b/firefox-ios/WidgetKit/et.lproj/Localizable.strings index 4c4eac98988d6..4391c8c62fac3 100644 --- a/firefox-ios/WidgetKit/et.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/et.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Ava Firefox"; +"Open Firefox" = "Ava Ecosia"; diff --git a/firefox-ios/WidgetKit/et.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/et.lproj/WidgetIntents.strings index a71376abd7317..fa3994415cf76 100644 --- a/firefox-ios/WidgetKit/et.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/et.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Mine kopeeritud lingile"; /* (No Comment) */ -"ctDNmu" = "Kiire ligipääs erinevatele Firefoxi tegevustele"; +"ctDNmu" = "Kiire ligipääs erinevatele Ecosiai tegevustele"; /* (No Comment) */ "eHmH1H" = "Tühjenda privaatsed kaardid"; diff --git a/firefox-ios/WidgetKit/eu.lproj/Localizable.strings b/firefox-ios/WidgetKit/eu.lproj/Localizable.strings index e0e94495a2775..c96eb960aa12d 100644 --- a/firefox-ios/WidgetKit/eu.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/eu.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Ireki Firefox"; +"Open Firefox" = "Ireki Ecosia"; diff --git a/firefox-ios/WidgetKit/eu.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/eu.lproj/WidgetIntents.strings index 7ada350376de6..29ab47d2cece5 100644 --- a/firefox-ios/WidgetKit/eu.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/eu.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Joan kopiatutako loturara"; /* (No Comment) */ -"ctDNmu" = "Firefoxen hainbat ekintzetarako sarbide bizkorra"; +"ctDNmu" = "Ecosiaen hainbat ekintzetarako sarbide bizkorra"; /* (No Comment) */ "eHmH1H" = "Garbitu fitxa pribatuak"; diff --git a/firefox-ios/WidgetKit/fa.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/fa.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/fa.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/fa.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/fi.lproj/Localizable.strings b/firefox-ios/WidgetKit/fi.lproj/Localizable.strings index 505c0cf8e6e6d..30441e64139e8 100644 --- a/firefox-ios/WidgetKit/fi.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/fi.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Avaa Firefox"; +"Open Firefox" = "Avaa Ecosia"; diff --git a/firefox-ios/WidgetKit/fi.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/fi.lproj/WidgetIntents.strings index 351412ec051f7..7245937364c20 100644 --- a/firefox-ios/WidgetKit/fi.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/fi.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Siirry kopioituun linkkiin"; /* (No Comment) */ -"ctDNmu" = "Nopea pääsy useisiin Firefoxin toimintoihin"; +"ctDNmu" = "Nopea pääsy useisiin Ecosiain toimintoihin"; /* (No Comment) */ "eHmH1H" = "Tyhjennä yksityiset välilehdet"; diff --git a/firefox-ios/WidgetKit/fil.lproj/Localizable.strings b/firefox-ios/WidgetKit/fil.lproj/Localizable.strings index e35b6208defc7..96c0790b07421 100644 --- a/firefox-ios/WidgetKit/fil.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/fil.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Buksan ang Firefox"; +"Open Firefox" = "Buksan ang Ecosia"; diff --git a/firefox-ios/WidgetKit/fil.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/fil.lproj/WidgetIntents.strings index a612afb0460cc..4fc27d18959f7 100644 --- a/firefox-ios/WidgetKit/fil.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/fil.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/fr.lproj/Localizable.strings b/firefox-ios/WidgetKit/fr.lproj/Localizable.strings index f2ac2eb7874dc..74511467bee3b 100644 --- a/firefox-ios/WidgetKit/fr.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/fr.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Ouvrir Firefox"; +"Open Firefox" = "Ouvrir Ecosia"; diff --git a/firefox-ios/WidgetKit/fr.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/fr.lproj/WidgetIntents.strings index ba242f9ef7e35..07e7277eb37fd 100644 --- a/firefox-ios/WidgetKit/fr.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/fr.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ouvrir le lien copié"; /* (No Comment) */ -"ctDNmu" = "Accès rapide à diverses actions de Firefox"; +"ctDNmu" = "Accès rapide à diverses actions de Ecosia"; /* (No Comment) */ "eHmH1H" = "Effacer les onglets privés"; diff --git a/firefox-ios/WidgetKit/ga.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ga.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/ga.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ga.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/gd.lproj/Localizable.strings b/firefox-ios/WidgetKit/gd.lproj/Localizable.strings index 37b5c45add64a..082a9b9d85b2d 100644 --- a/firefox-ios/WidgetKit/gd.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/gd.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Fosgail Firefox"; +"Open Firefox" = "Fosgail Ecosia"; diff --git a/firefox-ios/WidgetKit/gd.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/gd.lproj/WidgetIntents.strings index 74f04edcf695f..6d7f2c5ad4c94 100644 --- a/firefox-ios/WidgetKit/gd.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/gd.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Tadhail air a’ cheangal a rinn thu lethbhreac dheth"; /* (No Comment) */ -"ctDNmu" = "Grad-inntrigeadh do ghnìomhan Firefox"; +"ctDNmu" = "Grad-inntrigeadh do ghnìomhan Ecosia"; /* (No Comment) */ "eHmH1H" = "Falamhaich na tabaichean prìobhaideach"; diff --git a/firefox-ios/WidgetKit/gl.lproj/Localizable.strings b/firefox-ios/WidgetKit/gl.lproj/Localizable.strings index 5a6e47b996b0f..35ed572400059 100644 --- a/firefox-ios/WidgetKit/gl.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/gl.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir Firefox"; +"Open Firefox" = "Abrir Ecosia"; diff --git a/firefox-ios/WidgetKit/gl.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/gl.lproj/WidgetIntents.strings index 9ab507308b5d3..55202f5757483 100644 --- a/firefox-ios/WidgetKit/gl.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/gl.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir á ligazón copiada"; /* (No Comment) */ -"ctDNmu" = "Acceso rápido a varias accións de Firefox"; +"ctDNmu" = "Acceso rápido a varias accións de Ecosia"; /* (No Comment) */ "eHmH1H" = "Borrar as lapelas privadas"; diff --git a/firefox-ios/WidgetKit/gu-IN.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/gu-IN.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/gu-IN.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/gu-IN.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/he.lproj/Localizable.strings b/firefox-ios/WidgetKit/he.lproj/Localizable.strings index 15fdb27feeef2..404fdc263990d 100644 --- a/firefox-ios/WidgetKit/he.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/he.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "פתיחת Firefox"; +"Open Firefox" = "פתיחת Ecosia"; diff --git a/firefox-ios/WidgetKit/he.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/he.lproj/WidgetIntents.strings index 778d0002a81b1..64bce1ba44e93 100644 --- a/firefox-ios/WidgetKit/he.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/he.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "מעבר לקישור שהועתק"; /* (No Comment) */ -"ctDNmu" = "גישה מהירה לפעולות שונות ב־Firefox"; +"ctDNmu" = "גישה מהירה לפעולות שונות ב־Ecosia"; /* (No Comment) */ "eHmH1H" = "ניקוי לשוניות פרטיות"; diff --git a/firefox-ios/WidgetKit/hi-IN.lproj/Localizable.strings b/firefox-ios/WidgetKit/hi-IN.lproj/Localizable.strings index a09a0e9eace81..d532291e9f1bf 100644 --- a/firefox-ios/WidgetKit/hi-IN.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/hi-IN.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox खोलें"; +"Open Firefox" = "Ecosia खोलें"; diff --git a/firefox-ios/WidgetKit/hi-IN.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/hi-IN.lproj/WidgetIntents.strings index 335feb9970c1b..2db30d1899d14 100644 --- a/firefox-ios/WidgetKit/hi-IN.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/hi-IN.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "कॉपी की गई लिंक पर जाएं"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "निजी टैब बंद करें"; diff --git a/firefox-ios/WidgetKit/hr.lproj/Localizable.strings b/firefox-ios/WidgetKit/hr.lproj/Localizable.strings index fa6be45e030ec..daba181c2bd3f 100644 --- a/firefox-ios/WidgetKit/hr.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/hr.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Otvori Firefox"; +"Open Firefox" = "Otvori Ecosia"; diff --git a/firefox-ios/WidgetKit/hr.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/hr.lproj/WidgetIntents.strings index cda4024ebc0ea..f9abbb9818917 100644 --- a/firefox-ios/WidgetKit/hr.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/hr.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Idi na kopiranu poveznicu"; /* (No Comment) */ -"ctDNmu" = "Brzi pristup raznim Firefoxovim radnjama"; +"ctDNmu" = "Brzi pristup raznim Ecosiaovim radnjama"; /* (No Comment) */ "eHmH1H" = "Očisti privatne kartice"; diff --git a/firefox-ios/WidgetKit/hsb.lproj/Localizable.strings b/firefox-ios/WidgetKit/hsb.lproj/Localizable.strings index 78dec15f4948e..284575edf9ad0 100644 --- a/firefox-ios/WidgetKit/hsb.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/hsb.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox wočinić"; +"Open Firefox" = "Ecosia wočinić"; diff --git a/firefox-ios/WidgetKit/hsb.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/hsb.lproj/WidgetIntents.strings index 7b7370f37ad02..6c94f321a99cc 100644 --- a/firefox-ios/WidgetKit/hsb.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/hsb.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ke kopěrowanemu wotkazej"; /* (No Comment) */ -"ctDNmu" = "Spěšny přistup k wšelakim akcijam Firefox"; +"ctDNmu" = "Spěšny přistup k wšelakim akcijam Ecosia"; /* (No Comment) */ "eHmH1H" = "Priwatne rajtarki zhašeć"; diff --git a/firefox-ios/WidgetKit/hu.lproj/Localizable.strings b/firefox-ios/WidgetKit/hu.lproj/Localizable.strings index 6e7eea2661225..3d5d3beb9f5b5 100644 --- a/firefox-ios/WidgetKit/hu.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/hu.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox megnyitása"; +"Open Firefox" = "Ecosia megnyitása"; diff --git a/firefox-ios/WidgetKit/hu.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/hu.lproj/WidgetIntents.strings index a095a4f0a4ebf..cc0586de5467f 100644 --- a/firefox-ios/WidgetKit/hu.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/hu.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ugrás a másolt hivatkozáshoz"; /* (No Comment) */ -"ctDNmu" = "Gyors hozzáférés a Firefox különböző műveleteihez"; +"ctDNmu" = "Gyors hozzáférés a Ecosia különböző műveleteihez"; /* (No Comment) */ "eHmH1H" = "Privát lapok törlése"; diff --git a/firefox-ios/WidgetKit/hy-AM.lproj/Localizable.strings b/firefox-ios/WidgetKit/hy-AM.lproj/Localizable.strings index 6037fe98922ab..997ea0a3b8741 100644 --- a/firefox-ios/WidgetKit/hy-AM.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/hy-AM.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Բացել Firefox-ը"; +"Open Firefox" = "Բացել Ecosia-ը"; diff --git a/firefox-ios/WidgetKit/hy-AM.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/hy-AM.lproj/WidgetIntents.strings index e711fafb2e0f1..7eeacbd87e884 100644 --- a/firefox-ios/WidgetKit/hy-AM.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/hy-AM.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Անցնել պատճենված հղմանը"; /* (No Comment) */ -"ctDNmu" = "Արագ մուտք դեպի Firefox-ի տարբեր գործողություններ"; +"ctDNmu" = "Արագ մուտք դեպի Ecosia-ի տարբեր գործողություններ"; /* (No Comment) */ "eHmH1H" = "Մաքրել գաղտնի ներդիրները"; diff --git a/firefox-ios/WidgetKit/ia.lproj/Localizable.strings b/firefox-ios/WidgetKit/ia.lproj/Localizable.strings index f1174e531459a..aafa56d75bc09 100644 --- a/firefox-ios/WidgetKit/ia.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ia.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Aperir Firefox"; +"Open Firefox" = "Aperir Ecosia"; diff --git a/firefox-ios/WidgetKit/ia.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ia.lproj/WidgetIntents.strings index 1cf9e3d13427f..a13769158cc49 100644 --- a/firefox-ios/WidgetKit/ia.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ia.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir al ligamine copiate"; /* (No Comment) */ -"ctDNmu" = "Accesso rapide a varie actiones de Firefox"; +"ctDNmu" = "Accesso rapide a varie actiones de Ecosia"; /* (No Comment) */ "eHmH1H" = "Eliminar schedas private"; diff --git a/firefox-ios/WidgetKit/id.lproj/Localizable.strings b/firefox-ios/WidgetKit/id.lproj/Localizable.strings index f0fc44bd94056..4430a196870d0 100644 --- a/firefox-ios/WidgetKit/id.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/id.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Buka Firefox"; +"Open Firefox" = "Buka Ecosia"; diff --git a/firefox-ios/WidgetKit/id.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/id.lproj/WidgetIntents.strings index e23f517f6c534..41cf3e3379d87 100644 --- a/firefox-ios/WidgetKit/id.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/id.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Buka Tautan yang Disalin"; /* (No Comment) */ -"ctDNmu" = "Akses cepat ke berbagai aksi Firefox"; +"ctDNmu" = "Akses cepat ke berbagai aksi Ecosia"; /* (No Comment) */ "eHmH1H" = "Bersihkan Tab Pribadi"; diff --git a/firefox-ios/WidgetKit/is.lproj/Localizable.strings b/firefox-ios/WidgetKit/is.lproj/Localizable.strings index 8d7dea3dc47cf..0f7650a8449a6 100644 --- a/firefox-ios/WidgetKit/is.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/is.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Opna Firefox"; +"Open Firefox" = "Opna Ecosia"; diff --git a/firefox-ios/WidgetKit/is.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/is.lproj/WidgetIntents.strings index bdb905f6e0295..3fd44a547eec7 100644 --- a/firefox-ios/WidgetKit/is.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/is.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Fara á afritaða tengilinn"; /* (No Comment) */ -"ctDNmu" = "Flýtiaðgangur að ýmsum Firefox-aðgerðum"; +"ctDNmu" = "Flýtiaðgangur að ýmsum Ecosia-aðgerðum"; /* (No Comment) */ "eHmH1H" = "Hreinsa huliðsflipa"; diff --git a/firefox-ios/WidgetKit/it.lproj/Localizable.strings b/firefox-ios/WidgetKit/it.lproj/Localizable.strings index 3122225114f06..e56b040eecfea 100644 --- a/firefox-ios/WidgetKit/it.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/it.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Apri Firefox"; +"Open Firefox" = "Apri Ecosia"; diff --git a/firefox-ios/WidgetKit/it.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/it.lproj/WidgetIntents.strings index 3f966f24e078f..bc20008efd17f 100644 --- a/firefox-ios/WidgetKit/it.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/it.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Apri il link copiato"; /* (No Comment) */ -"ctDNmu" = "Accesso rapido a varie azioni di Firefox"; +"ctDNmu" = "Accesso rapido a varie azioni di Ecosia"; /* (No Comment) */ "eHmH1H" = "Elimina schede anonime"; diff --git a/firefox-ios/WidgetKit/ja.lproj/Localizable.strings b/firefox-ios/WidgetKit/ja.lproj/Localizable.strings index 0b252fb3c4af5..b581c0cd1618e 100644 --- a/firefox-ios/WidgetKit/ja.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ja.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox を開く"; +"Open Firefox" = "Ecosia を開く"; diff --git a/firefox-ios/WidgetKit/ja.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ja.lproj/WidgetIntents.strings index 1f35e0a1ca520..cd90bea7e0ff2 100644 --- a/firefox-ios/WidgetKit/ja.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ja.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "コピーしたリンク先へ移動する"; /* (No Comment) */ -"ctDNmu" = "様々な Firefox 操作へのクイックアクセス"; +"ctDNmu" = "様々な Ecosia 操作へのクイックアクセス"; /* (No Comment) */ "eHmH1H" = "プライベートタブを消去"; diff --git a/firefox-ios/WidgetKit/jv.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/jv.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/jv.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/jv.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/ka.lproj/Localizable.strings b/firefox-ios/WidgetKit/ka.lproj/Localizable.strings index 9c3c71e170a94..d5aa7c1e7d128 100644 --- a/firefox-ios/WidgetKit/ka.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ka.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "გახსენით Firefox"; +"Open Firefox" = "გახსენით Ecosia"; diff --git a/firefox-ios/WidgetKit/ka.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ka.lproj/WidgetIntents.strings index 72e47c250d6dc..8b560806d0843 100644 --- a/firefox-ios/WidgetKit/ka.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ka.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "აღებულ ბმულზე გადასვლა"; /* (No Comment) */ -"ctDNmu" = "სწრაფი წვდომა Firefox-ის სხვადასხვა მოქმედებაზე"; +"ctDNmu" = "სწრაფი წვდომა Ecosia-ის სხვადასხვა მოქმედებაზე"; /* (No Comment) */ "eHmH1H" = "პირადი ჩანართების გასუფთავება"; diff --git a/firefox-ios/WidgetKit/kab.lproj/Localizable.strings b/firefox-ios/WidgetKit/kab.lproj/Localizable.strings index 0cfd78582245a..0fc0867173e33 100644 --- a/firefox-ios/WidgetKit/kab.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/kab.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Ldi Firefox"; +"Open Firefox" = "Ldi Ecosia"; diff --git a/firefox-ios/WidgetKit/kab.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/kab.lproj/WidgetIntents.strings index a8f37750d071d..2252067ab859f 100644 --- a/firefox-ios/WidgetKit/kab.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/kab.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ddu ɣer useɣwen yettwaneɣlen"; /* (No Comment) */ -"ctDNmu" = "Anekcum arurad ɣer tigawin yemgaraden n Firefox"; +"ctDNmu" = "Anekcum arurad ɣer tigawin yemgaraden n Ecosia"; /* (No Comment) */ "eHmH1H" = "Sfeḍ iccaren usligen"; diff --git a/firefox-ios/WidgetKit/kk.lproj/Localizable.strings b/firefox-ios/WidgetKit/kk.lproj/Localizable.strings index 965c269f9915e..f4a08712a9b26 100644 --- a/firefox-ios/WidgetKit/kk.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/kk.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox-ты ашу"; +"Open Firefox" = "Ecosia-ты ашу"; diff --git a/firefox-ios/WidgetKit/kk.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/kk.lproj/WidgetIntents.strings index 47af86e563aff..147aa070b27af 100644 --- a/firefox-ios/WidgetKit/kk.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/kk.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Көшірілген сілтемеге өту"; /* (No Comment) */ -"ctDNmu" = "Әр түрлі Firefox әрекеттеріне жылдам қатынау"; +"ctDNmu" = "Әр түрлі Ecosia әрекеттеріне жылдам қатынау"; /* (No Comment) */ "eHmH1H" = "Жекелік беттерді тазарту"; diff --git a/firefox-ios/WidgetKit/km.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/km.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/km.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/km.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/kn.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/kn.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/kn.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/kn.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/ko.lproj/Localizable.strings b/firefox-ios/WidgetKit/ko.lproj/Localizable.strings index 95aac9f903bff..039fd9c8cdbf9 100644 --- a/firefox-ios/WidgetKit/ko.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ko.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox 열기"; +"Open Firefox" = "Ecosia 열기"; diff --git a/firefox-ios/WidgetKit/ko.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ko.lproj/WidgetIntents.strings index 85a7bfa856d37..dd6217a3602a2 100644 --- a/firefox-ios/WidgetKit/ko.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ko.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "복사한 링크로 이동"; /* (No Comment) */ -"ctDNmu" = "다양한 Firefox 액션으로의 빠른 접근"; +"ctDNmu" = "다양한 Ecosia 액션으로의 빠른 접근"; /* (No Comment) */ "eHmH1H" = "사생활 보호 탭 지우기"; diff --git a/firefox-ios/WidgetKit/lo.lproj/Localizable.strings b/firefox-ios/WidgetKit/lo.lproj/Localizable.strings index fd1f7f0b00136..a6759fda6ee45 100644 --- a/firefox-ios/WidgetKit/lo.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/lo.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "ເປີດ Firefox"; +"Open Firefox" = "ເປີດ Ecosia"; diff --git a/firefox-ios/WidgetKit/lo.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/lo.lproj/WidgetIntents.strings index 4acce606c334e..caad8ce4de86c 100644 --- a/firefox-ios/WidgetKit/lo.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/lo.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "ໄປທີ່ລີ້ງໄດ້ກັອບປີ້ໄວ້"; /* (No Comment) */ -"ctDNmu" = "ເຂົ້າໄປຫາການກະທຳຂອງ Firefox ໄດ້ຢ່າງວ່ອງໄວ"; +"ctDNmu" = "ເຂົ້າໄປຫາການກະທຳຂອງ Ecosia ໄດ້ຢ່າງວ່ອງໄວ"; /* (No Comment) */ "eHmH1H" = "ລົບລ້າງແທັບສ່ວນຕົວ"; diff --git a/firefox-ios/WidgetKit/lt.lproj/Localizable.strings b/firefox-ios/WidgetKit/lt.lproj/Localizable.strings index bf7ab056bc4db..c9b7ea226e834 100644 --- a/firefox-ios/WidgetKit/lt.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/lt.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Atverti „Firefox“"; +"Open Firefox" = "Atverti „Ecosia“"; diff --git a/firefox-ios/WidgetKit/lt.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/lt.lproj/WidgetIntents.strings index 24e37e974734a..a0c908105c3f6 100644 --- a/firefox-ios/WidgetKit/lt.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/lt.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Atverti nukopijuotą saitą"; /* (No Comment) */ -"ctDNmu" = "Greitas priėjimas prie įvairių „Firefox“ veiksmų"; +"ctDNmu" = "Greitas priėjimas prie įvairių „Ecosia“ veiksmų"; /* (No Comment) */ "eHmH1H" = "Išvalyti privačias korteles"; diff --git a/firefox-ios/WidgetKit/lv.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/lv.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/lv.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/lv.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/ml.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ml.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/ml.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ml.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/mr.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/mr.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/mr.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/mr.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/ms.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ms.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/ms.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ms.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/my.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/my.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/my.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/my.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/nb.lproj/Localizable.strings b/firefox-ios/WidgetKit/nb.lproj/Localizable.strings index e6b79fe48a105..df77e7c57cc25 100644 --- a/firefox-ios/WidgetKit/nb.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/nb.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Åpne Firefox"; +"Open Firefox" = "Åpne Ecosia"; diff --git a/firefox-ios/WidgetKit/nb.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/nb.lproj/WidgetIntents.strings index 217ee58ce8643..70b4dfb674e95 100644 --- a/firefox-ios/WidgetKit/nb.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/nb.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Gå til kopiert lenke"; /* (No Comment) */ -"ctDNmu" = "Rask tilgang til forskjellige Firefox-aktiviteter"; +"ctDNmu" = "Rask tilgang til forskjellige Ecosia-aktiviteter"; /* (No Comment) */ "eHmH1H" = "Tøm private faner"; diff --git a/firefox-ios/WidgetKit/ne-NP.lproj/Localizable.strings b/firefox-ios/WidgetKit/ne-NP.lproj/Localizable.strings index e965bf98857d4..5213cb060f856 100644 --- a/firefox-ios/WidgetKit/ne-NP.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ne-NP.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox खोल्नुहोस्"; +"Open Firefox" = "Ecosia खोल्नुहोस्"; diff --git a/firefox-ios/WidgetKit/ne-NP.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ne-NP.lproj/WidgetIntents.strings index 2283ccc574b16..0e227a5867b91 100644 --- a/firefox-ios/WidgetKit/ne-NP.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ne-NP.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "कपि गरिएको लिङ्कमा जानुुहोस्"; /* (No Comment) */ -"ctDNmu" = "विभिन्न Firefox कार्यहरूमा द्रुत पहुँच"; +"ctDNmu" = "विभिन्न Ecosia कार्यहरूमा द्रुत पहुँच"; /* (No Comment) */ "eHmH1H" = "निजी ट्याबहरु खाली गर्नुहोस्"; diff --git a/firefox-ios/WidgetKit/nl.lproj/Localizable.strings b/firefox-ios/WidgetKit/nl.lproj/Localizable.strings index 356e2db0d8e86..05d39fb1daf0d 100644 --- a/firefox-ios/WidgetKit/nl.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/nl.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox openen"; +"Open Firefox" = "Ecosia openen"; diff --git a/firefox-ios/WidgetKit/nl.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/nl.lproj/WidgetIntents.strings index 4fd9a993ad412..98ac2ab72641c 100644 --- a/firefox-ios/WidgetKit/nl.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/nl.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Naar gekopieerde koppeling"; /* (No Comment) */ -"ctDNmu" = "Snelle toegang tot diverse Firefox-acties"; +"ctDNmu" = "Snelle toegang tot diverse Ecosia-acties"; /* (No Comment) */ "eHmH1H" = "Privétabbladen wissen"; diff --git a/firefox-ios/WidgetKit/nn.lproj/Localizable.strings b/firefox-ios/WidgetKit/nn.lproj/Localizable.strings index 8029751b565d9..db9565615a860 100644 --- a/firefox-ios/WidgetKit/nn.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/nn.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Opne Firefox"; +"Open Firefox" = "Opne Ecosia"; diff --git a/firefox-ios/WidgetKit/nn.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/nn.lproj/WidgetIntents.strings index 0ed6511fd0b2e..358a6a4736f2f 100644 --- a/firefox-ios/WidgetKit/nn.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/nn.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Gå til kopiert lenke"; /* (No Comment) */ -"ctDNmu" = "Rask tilgang til forskjellige Firefox-aktivitetar"; +"ctDNmu" = "Rask tilgang til forskjellige Ecosia-aktivitetar"; /* (No Comment) */ "eHmH1H" = "Tøm private faner"; diff --git a/firefox-ios/WidgetKit/oc.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/oc.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/oc.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/oc.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/or.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/or.lproj/WidgetIntents.strings index 24ae60e53ec98..46616d11f6cfe 100644 --- a/firefox-ios/WidgetKit/or.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/or.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/pa-IN.lproj/Localizable.strings b/firefox-ios/WidgetKit/pa-IN.lproj/Localizable.strings index 5ac664629f11b..86af033e9ef4f 100644 --- a/firefox-ios/WidgetKit/pa-IN.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/pa-IN.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox ਖੋਲ੍ਹੋ"; +"Open Firefox" = "Ecosia ਖੋਲ੍ਹੋ"; diff --git a/firefox-ios/WidgetKit/pa-IN.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/pa-IN.lproj/WidgetIntents.strings index f0f783ed4e59e..9980e8e76c111 100644 --- a/firefox-ios/WidgetKit/pa-IN.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/pa-IN.lproj/WidgetIntents.strings @@ -2,10 +2,10 @@ "2GqvPe" = "ਕਾਪੀ ਕੀਤੇ ਲਿੰਕ ਉੱਤੇ ਜਾਓ"; /* (No Comment) */ -"ctDNmu" = "ਕਈ Firefox ਕਾਰਵਾਈਆਂ ਲਈ ਫ਼ੌਰੀ ਪਹੁੰਚ"; +"ctDNmu" = "ਕਈ Ecosia ਕਾਰਵਾਈਆਂ ਲਈ ਫ਼ੌਰੀ ਪਹੁੰਚ"; /* (No Comment) */ -"eHmH1H" = "ਨਿੱਜੀ ਟੈਬਾਂ ਨੂੰ ਮਿਟਾਓ"; +"eHmH1H" = "ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਨੂੰ ਮਿਟਾਓ"; /* (No Comment) */ "eqyNJg" = "ਫ਼ੌਰੀ ਕਾਰਵਾਈ"; @@ -17,10 +17,10 @@ "fi3W24-2GqvPe" = "‘ਕਾਪੀ ਕੀਤੇ ਲਿੰਕ ਉੱਤੇ ਜਾਓ’ ਨਾਲ ${count} ਚੋਣਾਂ ਮਿਲਦੀਆਂ ਹਨ।"; /* (No Comment) */ -"fi3W24-eHmH1H" = "‘ਨਿੱਜੀ ਟੈਬਾਂ ਨੂੰ ਮਿਟਾਓ’ ਨਾਲ ${count} ਚੋਣਾਂ ਮਿਲਦੀਆਂ ਹਨ।"; +"fi3W24-eHmH1H" = "‘ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਨੂੰ ਮਿਟਾਓ’ ਨਾਲ ${count} ਚੋਣਾਂ ਮਿਲਦੀਆਂ ਹਨ।"; /* (No Comment) */ -"fi3W24-scEmjs" = "‘ਨਵੀਂ ਨਿੱਜੀ ਖੋਜ’ ਨਾਲ ${count} ਚੋਣਾਂ ਮਿਲਦੀਆਂ ਹਨ।"; +"fi3W24-scEmjs" = "‘ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਖੋਜ’ ਨਾਲ ${count} ਚੋਣਾਂ ਮਿਲਦੀਆਂ ਹਨ।"; /* (No Comment) */ "fi3W24-xRJbBP" = "‘ਨਵੀਂ ਖੋਜ’ ਨਾਲ ${count} ਚੋਣਾਂ ਮਿਲਦੀਆਂ ਹਨ।"; @@ -29,16 +29,16 @@ "PzSrmZ-2GqvPe" = "ਤਸਦੀਕ ਕਰਨ ਲਈ, ਕੀ ਤੁਸੀਂ ‘ਕਾਪੀ ਕੀਤੇ ਲਿੰਕ ਉੱਤੇ ਜਾਓ’ ਚਾਹੁੰਦੇ ਹੋ?"; /* (No Comment) */ -"PzSrmZ-eHmH1H" = "ਤਸਦੀਕ ਕਰਨ ਲਈ, ਕੀ ਤੁਸੀਂ ‘ਨਿੱਜੀ ਟੈਬਾਂ ਨੂੰ ਮਿਟਾਓ’ ਚਾਹੁੰਦੇ ਹੋ?"; +"PzSrmZ-eHmH1H" = "ਤਸਦੀਕ ਕਰਨ ਲਈ, ਕੀ ਤੁਸੀਂ ‘ਪ੍ਰਾਈਵੇਟ ਟੈਬਾਂ ਨੂੰ ਮਿਟਾਓ’ ਚਾਹੁੰਦੇ ਹੋ?"; /* (No Comment) */ -"PzSrmZ-scEmjs" = "ਤਸਦੀਕ ਕਰਨ ਲਈ, ਕੀ ਤੁਸੀਂ ‘ਨਵੀਂ ਨਿੱਜੀ ਖੋਜ’ ਚਾਹੁੰਦੇ ਹੋ?"; +"PzSrmZ-scEmjs" = "ਤਸਦੀਕ ਕਰਨ ਲਈ, ਕੀ ਤੁਸੀਂ ‘ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਖੋਜ’ ਚਾਹੁੰਦੇ ਹੋ?"; /* (No Comment) */ "PzSrmZ-xRJbBP" = "ਤਸਦੀਕ ਕਰਨ ਲਈ, ਕੀ ਤੁਸੀਂ ‘ਨਵੀਂ ਖੋਜ’ ਚਾਹੁੰਦੇ ਹੋ?"; /* (No Comment) */ -"scEmjs" = "ਨਵੀਂ ਨਿੱਜੀ ਖੋਜ"; +"scEmjs" = "ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਖੋਜ"; /* (No Comment) */ "w9jdPK" = "ਫ਼ੌਰੀ ਕਾਰਵਾਈ"; diff --git a/firefox-ios/WidgetKit/pl.lproj/Localizable.strings b/firefox-ios/WidgetKit/pl.lproj/Localizable.strings index 0133b742efbf6..8611bdf8c6722 100644 --- a/firefox-ios/WidgetKit/pl.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/pl.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Otwórz Firefoksa"; +"Open Firefox" = "Otwórz Ecosia"; diff --git a/firefox-ios/WidgetKit/pl.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/pl.lproj/WidgetIntents.strings index 6079d0067ba84..3656c096f0ae0 100644 --- a/firefox-ios/WidgetKit/pl.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/pl.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Otwórz skopiowany odnośnik"; /* (No Comment) */ -"ctDNmu" = "Szybki dostęp do różnych działań Firefoksa"; +"ctDNmu" = "Szybki dostęp do różnych działań Ecosia"; /* (No Comment) */ "eHmH1H" = "Wyczyść prywatne karty"; diff --git a/firefox-ios/WidgetKit/pt-BR.lproj/Localizable.strings b/firefox-ios/WidgetKit/pt-BR.lproj/Localizable.strings index 5a6e47b996b0f..35ed572400059 100644 --- a/firefox-ios/WidgetKit/pt-BR.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/pt-BR.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir Firefox"; +"Open Firefox" = "Abrir Ecosia"; diff --git a/firefox-ios/WidgetKit/pt-BR.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/pt-BR.lproj/WidgetIntents.strings index 297e9cbe1f81e..ea86d757ce275 100644 --- a/firefox-ios/WidgetKit/pt-BR.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/pt-BR.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Usar link copiado"; /* (No Comment) */ -"ctDNmu" = "Acesso rápido a várias ações do Firefox"; +"ctDNmu" = "Acesso rápido a várias ações do Ecosia"; /* (No Comment) */ "eHmH1H" = "Limpar abas privativas"; diff --git a/firefox-ios/WidgetKit/pt-PT.lproj/Localizable.strings b/firefox-ios/WidgetKit/pt-PT.lproj/Localizable.strings index ed2a0bd635fb1..00f9d046985a6 100644 --- a/firefox-ios/WidgetKit/pt-PT.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/pt-PT.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Abrir o Firefox"; +"Open Firefox" = "Abrir o Ecosia"; diff --git a/firefox-ios/WidgetKit/pt-PT.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/pt-PT.lproj/WidgetIntents.strings index 40affb1ba399a..f53897a647cc7 100644 --- a/firefox-ios/WidgetKit/pt-PT.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/pt-PT.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Ir para a ligação copiada"; /* (No Comment) */ -"ctDNmu" = "Acesso rápido a várias ações do Firefox"; +"ctDNmu" = "Acesso rápido a várias ações do Ecosia"; /* (No Comment) */ "eHmH1H" = "Limpar separadores privados"; diff --git a/firefox-ios/WidgetKit/rm.lproj/Localizable.strings b/firefox-ios/WidgetKit/rm.lproj/Localizable.strings index 60fab0272b1db..60a9f0216102e 100644 --- a/firefox-ios/WidgetKit/rm.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/rm.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Avrir Firefox"; +"Open Firefox" = "Avrir Ecosia"; diff --git a/firefox-ios/WidgetKit/rm.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/rm.lproj/WidgetIntents.strings index 51f424c1f12a9..3d61c4bfd0a64 100644 --- a/firefox-ios/WidgetKit/rm.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/rm.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Avrir l'adressa copiada"; /* (No Comment) */ -"ctDNmu" = "Access direct a differentas acziuns da Firefox"; +"ctDNmu" = "Access direct a differentas acziuns da Ecosia"; /* (No Comment) */ "eHmH1H" = "Stizzar ils tabs privats"; diff --git a/firefox-ios/WidgetKit/ro.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ro.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/ro.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ro.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/ru.lproj/Localizable.strings b/firefox-ios/WidgetKit/ru.lproj/Localizable.strings index 09c4adc0652ba..36da996a57ca7 100644 --- a/firefox-ios/WidgetKit/ru.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ru.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Открыть Firefox"; +"Open Firefox" = "Открыть Ecosia"; diff --git a/firefox-ios/WidgetKit/ru.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ru.lproj/WidgetIntents.strings index 35bdd9ab8c866..448503ce26503 100644 --- a/firefox-ios/WidgetKit/ru.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ru.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Перейти по скопированной ссылке"; /* (No Comment) */ -"ctDNmu" = "Быстрый доступ к различным действиям Firefox"; +"ctDNmu" = "Быстрый доступ к различным действиям Ecosia"; /* (No Comment) */ "eHmH1H" = "Закрыть приватные вкладки"; diff --git a/firefox-ios/WidgetKit/sat-Olck.lproj/Localizable.strings b/firefox-ios/WidgetKit/sat-Olck.lproj/Localizable.strings index e2476403ba3dc..643cc28cf207f 100644 --- a/firefox-ios/WidgetKit/sat-Olck.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/sat-Olck.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox ᱡᱷᱤᱡᱽ ᱢᱮ"; +"Open Firefox" = "Ecosia ᱡᱷᱤᱡᱽ ᱢᱮ"; diff --git a/firefox-ios/WidgetKit/sat-Olck.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/sat-Olck.lproj/WidgetIntents.strings index e0f87e208be25..10fdef4f0d3d4 100644 --- a/firefox-ios/WidgetKit/sat-Olck.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/sat-Olck.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "ᱱᱚᱠᱚᱞ ᱟᱠᱟᱱ ᱞᱮᱸᱠ ᱛᱮ ᱪᱟᱞᱟᱜᱢᱮ"; /* (No Comment) */ -"ctDNmu" = "ᱟᱭᱢᱟ Firefox ᱠᱟᱹᱢᱤ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱞᱚᱜᱚᱱ ᱟᱫᱮᱨ"; +"ctDNmu" = "ᱟᱭᱢᱟ Ecosia ᱠᱟᱹᱢᱤ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱞᱚᱜᱚᱱ ᱟᱫᱮᱨ"; /* (No Comment) */ "eHmH1H" = "ᱱᱤᱡᱮᱨᱟᱠ ᱴᱮᱵᱠᱚ ᱢᱮᱴᱟᱣ ᱢᱮ"; diff --git a/firefox-ios/WidgetKit/ses.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ses.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/ses.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ses.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/sk.lproj/Localizable.strings b/firefox-ios/WidgetKit/sk.lproj/Localizable.strings index e57f6b0db4a16..1e07ebbde4d0a 100644 --- a/firefox-ios/WidgetKit/sk.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/sk.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Otvoriť Firefox"; +"Open Firefox" = "Otvoriť Ecosia"; diff --git a/firefox-ios/WidgetKit/sk.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/sk.lproj/WidgetIntents.strings index e34088185c6e3..26c350962a9ba 100644 --- a/firefox-ios/WidgetKit/sk.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/sk.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Prejsť na skopírovaný odkaz"; /* (No Comment) */ -"ctDNmu" = "Rýchly prístup k rôznym akciám prehliadača Firefox"; +"ctDNmu" = "Rýchly prístup k rôznym akciám prehliadača Ecosia"; /* (No Comment) */ "eHmH1H" = "Vymazať súkromné karty"; diff --git a/firefox-ios/WidgetKit/sl.lproj/Localizable.strings b/firefox-ios/WidgetKit/sl.lproj/Localizable.strings index 69e37d957c41f..4cda84c78834f 100644 --- a/firefox-ios/WidgetKit/sl.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/sl.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Odpri Firefox"; +"Open Firefox" = "Odpri Ecosia"; diff --git a/firefox-ios/WidgetKit/sl.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/sl.lproj/WidgetIntents.strings index 319f8c0953a2c..48d26d73e1f05 100644 --- a/firefox-ios/WidgetKit/sl.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/sl.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Pojdi na kopirano povezavo"; /* (No Comment) */ -"ctDNmu" = "Hiter dostop do različnih dejanj Firefoxa"; +"ctDNmu" = "Hiter dostop do različnih dejanj Ecosiaa"; /* (No Comment) */ "eHmH1H" = "Počisti zasebne zavihke"; diff --git a/firefox-ios/WidgetKit/sq.lproj/Localizable.strings b/firefox-ios/WidgetKit/sq.lproj/Localizable.strings index b33d22ce06226..cf89329a9f023 100644 --- a/firefox-ios/WidgetKit/sq.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/sq.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Hap Firefox-in"; +"Open Firefox" = "Hap Ecosia-in"; diff --git a/firefox-ios/WidgetKit/sq.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/sq.lproj/WidgetIntents.strings index 9648236697a3f..d8b71f384df00 100644 --- a/firefox-ios/WidgetKit/sq.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/sq.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Shko te Lidhja e Kopjuar"; /* (No Comment) */ -"ctDNmu" = "Hyrje e shpejtë te veprime të ndryshme Firefox-i"; +"ctDNmu" = "Hyrje e shpejtë te veprime të ndryshme Ecosia-i"; /* (No Comment) */ "eHmH1H" = "Spastro Skedat Private"; diff --git a/firefox-ios/WidgetKit/su.lproj/Localizable.strings b/firefox-ios/WidgetKit/su.lproj/Localizable.strings index f0fc44bd94056..4430a196870d0 100644 --- a/firefox-ios/WidgetKit/su.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/su.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Buka Firefox"; +"Open Firefox" = "Buka Ecosia"; diff --git a/firefox-ios/WidgetKit/su.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/su.lproj/WidgetIntents.strings index 79beada527753..0f332d35dd23d 100644 --- a/firefox-ios/WidgetKit/su.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/su.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Indit ka Tutumbu Tironan"; /* (No Comment) */ -"ctDNmu" = "Aksés téréh kana sagala rupa peta Firefox"; +"ctDNmu" = "Aksés téréh kana sagala rupa peta Ecosia"; /* (No Comment) */ "eHmH1H" = "Beresihan Tab Nyamuni"; diff --git a/firefox-ios/WidgetKit/sv.lproj/Localizable.strings b/firefox-ios/WidgetKit/sv.lproj/Localizable.strings index 91b1cdc471fe3..f70c980c53496 100644 --- a/firefox-ios/WidgetKit/sv.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/sv.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Öppna Firefox"; +"Open Firefox" = "Öppna Ecosia"; diff --git a/firefox-ios/WidgetKit/sv.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/sv.lproj/WidgetIntents.strings index 3f250d2b17a51..1973cac85b416 100644 --- a/firefox-ios/WidgetKit/sv.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/sv.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Gå till kopierad länk"; /* (No Comment) */ -"ctDNmu" = "Snabb åtkomst till olika Firefox-åtgärder"; +"ctDNmu" = "Snabb åtkomst till olika Ecosia-åtgärder"; /* (No Comment) */ "eHmH1H" = "Rensa privata flikar"; diff --git a/firefox-ios/WidgetKit/ta.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ta.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/ta.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ta.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/te.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/te.lproj/WidgetIntents.strings index f087ae0c60656..63fa057294aee 100644 --- a/firefox-ios/WidgetKit/te.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/te.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "కాపీచేసిన లంకెకు వెళ్ళు"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "అంతరంగిక ట్యాబులను తుడిచివేయి"; diff --git a/firefox-ios/WidgetKit/th.lproj/Localizable.strings b/firefox-ios/WidgetKit/th.lproj/Localizable.strings index c08153e6791fc..05f55d76e5d7b 100644 --- a/firefox-ios/WidgetKit/th.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/th.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "เปิด Firefox"; +"Open Firefox" = "เปิด Ecosia"; diff --git a/firefox-ios/WidgetKit/th.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/th.lproj/WidgetIntents.strings index 66b46648b5eb9..dacd2f2563a0e 100644 --- a/firefox-ios/WidgetKit/th.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/th.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "ไปยังลิงก์ที่คัดลอก"; /* (No Comment) */ -"ctDNmu" = "เข้าถึงการกระทำต่าง ๆ ของ Firefox ได้อย่างรวดเร็ว"; +"ctDNmu" = "เข้าถึงการกระทำต่าง ๆ ของ Ecosia ได้อย่างรวดเร็ว"; /* (No Comment) */ "eHmH1H" = "ล้างแท็บส่วนตัว"; diff --git a/firefox-ios/WidgetKit/tr.lproj/Localizable.strings b/firefox-ios/WidgetKit/tr.lproj/Localizable.strings index 86f1cf59c7cbc..7c87aff6cf14a 100644 --- a/firefox-ios/WidgetKit/tr.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/tr.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox’u aç"; +"Open Firefox" = "Ecosia’u aç"; diff --git a/firefox-ios/WidgetKit/tr.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/tr.lproj/WidgetIntents.strings index 39874b9c557b3..f5e14b5f95e74 100644 --- a/firefox-ios/WidgetKit/tr.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/tr.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Kopyalanan linke git"; /* (No Comment) */ -"ctDNmu" = "Çeşitli Firefox eylemlerine hızlı erişim"; +"ctDNmu" = "Çeşitli Ecosia eylemlerine hızlı erişim"; /* (No Comment) */ "eHmH1H" = "Gizli sekmeleri temizle"; diff --git a/firefox-ios/WidgetKit/tt.lproj/Localizable.strings b/firefox-ios/WidgetKit/tt.lproj/Localizable.strings index 8cecf51dee5fc..db6c5e5d8eccb 100644 --- a/firefox-ios/WidgetKit/tt.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/tt.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox-ны ачу"; +"Open Firefox" = "Ecosia-ны ачу"; diff --git a/firefox-ios/WidgetKit/tt.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/tt.lproj/WidgetIntents.strings index 866f33eb58e87..f5162acb434ed 100644 --- a/firefox-ios/WidgetKit/tt.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/tt.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Копияләнгән сылтамага күчү"; /* (No Comment) */ -"ctDNmu" = "Төрле Firefox гамәлләренә тиз ирешү"; +"ctDNmu" = "Төрле Ecosia гамәлләренә тиз ирешү"; /* (No Comment) */ "eHmH1H" = "Хосусый табларны ябу"; diff --git a/firefox-ios/WidgetKit/ug.lproj/Localizable.strings b/firefox-ios/WidgetKit/ug.lproj/Localizable.strings index c968b0e3f1b09..b47fb37d110ca 100644 --- a/firefox-ios/WidgetKit/ug.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ug.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox نى ئاچ"; +"Open Firefox" = "Ecosia نى ئاچ"; diff --git a/firefox-ios/WidgetKit/ug.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ug.lproj/WidgetIntents.strings index e90f8eb1331f5..77001de48f048 100644 --- a/firefox-ios/WidgetKit/ug.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ug.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "كۆچۈرۈلگەن ئۇلانمىغا يۆتكەل"; /* (No Comment) */ -"ctDNmu" = "نۇرغۇن Firefox مەشغۇلاتلىرىنى تېز زىيارەت قىلالايسىز"; +"ctDNmu" = "نۇرغۇن Ecosia مەشغۇلاتلىرىنى تېز زىيارەت قىلالايسىز"; /* (No Comment) */ "eHmH1H" = "شەخسىي بەتكۈچنى تازىلا"; diff --git a/firefox-ios/WidgetKit/uk.lproj/Localizable.strings b/firefox-ios/WidgetKit/uk.lproj/Localizable.strings index c4dab186dccd1..a687cf7e30a6e 100644 --- a/firefox-ios/WidgetKit/uk.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/uk.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Відкрити Firefox"; +"Open Firefox" = "Відкрити Ecosia"; diff --git a/firefox-ios/WidgetKit/uk.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/uk.lproj/WidgetIntents.strings index b82e6f35e5f5b..d1e40525d4b26 100644 --- a/firefox-ios/WidgetKit/uk.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/uk.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Відкрити скопійоване посилання"; /* (No Comment) */ -"ctDNmu" = "Швидкий доступ до різних дій Firefox"; +"ctDNmu" = "Швидкий доступ до різних дій Ecosia"; /* (No Comment) */ "eHmH1H" = "Очистити приватні вкладки"; diff --git a/firefox-ios/WidgetKit/ur.lproj/Localizable.strings b/firefox-ios/WidgetKit/ur.lproj/Localizable.strings index 0cc33a6ff99a8..add5907a28f80 100644 --- a/firefox-ios/WidgetKit/ur.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/ur.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Firefox کھولیں"; +"Open Firefox" = "Ecosia کھولیں"; diff --git a/firefox-ios/WidgetKit/ur.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/ur.lproj/WidgetIntents.strings index 545d4003373b6..8312013959a51 100644 --- a/firefox-ios/WidgetKit/ur.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/ur.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "نقل شدہ لنک پر جائیں\n "; /* (No Comment) */ -"ctDNmu" = "Firefox کی مختلف کارروائیوں تک فوری رسائی"; +"ctDNmu" = "Ecosia کی مختلف کارروائیوں تک فوری رسائی"; /* (No Comment) */ "eHmH1H" = "نجی ٹیبز کو صاف کریں"; diff --git a/firefox-ios/WidgetKit/uz.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/uz.lproj/WidgetIntents.strings index ec03d9a17edf6..218417e2752ef 100644 --- a/firefox-ios/WidgetKit/uz.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/uz.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Go to Copied Link"; /* (No Comment) */ -"ctDNmu" = "Quick access to various Firefox actions"; +"ctDNmu" = "Quick access to various Ecosia actions"; /* (No Comment) */ "eHmH1H" = "Clear Private Tabs"; diff --git a/firefox-ios/WidgetKit/vi.lproj/Localizable.strings b/firefox-ios/WidgetKit/vi.lproj/Localizable.strings index eda55843ab06d..04c3ce7f13fbe 100644 --- a/firefox-ios/WidgetKit/vi.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/vi.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "Mở Firefox"; +"Open Firefox" = "Mở Ecosia"; diff --git a/firefox-ios/WidgetKit/vi.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/vi.lproj/WidgetIntents.strings index d52119c20b873..46a6bdda6ddea 100644 --- a/firefox-ios/WidgetKit/vi.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/vi.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "Đi đến liên kết đã sao chép"; /* (No Comment) */ -"ctDNmu" = "Truy cập nhanh vào các hành động khác nhau của Firefox"; +"ctDNmu" = "Truy cập nhanh vào các hành động khác nhau của Ecosia"; /* (No Comment) */ "eHmH1H" = "Xóa các thẻ riêng tư"; diff --git a/firefox-ios/WidgetKit/zh-CN.lproj/Localizable.strings b/firefox-ios/WidgetKit/zh-CN.lproj/Localizable.strings index 1def3611ae32a..6d509df336d8e 100644 --- a/firefox-ios/WidgetKit/zh-CN.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/zh-CN.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "打开 Firefox"; +"Open Firefox" = "打开 Ecosia"; diff --git a/firefox-ios/WidgetKit/zh-CN.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/zh-CN.lproj/WidgetIntents.strings index b2d6094b547a1..108f02c25778e 100644 --- a/firefox-ios/WidgetKit/zh-CN.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/zh-CN.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "打开复制的链接"; /* (No Comment) */ -"ctDNmu" = "快速进行众多 Firefox 操作"; +"ctDNmu" = "快速进行众多 Ecosia 操作"; /* (No Comment) */ "eHmH1H" = "清除隐私标签页"; diff --git a/firefox-ios/WidgetKit/zh-TW.lproj/Localizable.strings b/firefox-ios/WidgetKit/zh-TW.lproj/Localizable.strings index cd2e0a33be27b..589abb81db106 100644 --- a/firefox-ios/WidgetKit/zh-TW.lproj/Localizable.strings +++ b/firefox-ios/WidgetKit/zh-TW.lproj/Localizable.strings @@ -1,3 +1,3 @@ /* No comment provided by engineer. */ -"Open Firefox" = "開啟 Firefox"; +"Open Firefox" = "開啟 Ecosia"; diff --git a/firefox-ios/WidgetKit/zh-TW.lproj/WidgetIntents.strings b/firefox-ios/WidgetKit/zh-TW.lproj/WidgetIntents.strings index 578da20425ccf..965812186d34d 100644 --- a/firefox-ios/WidgetKit/zh-TW.lproj/WidgetIntents.strings +++ b/firefox-ios/WidgetKit/zh-TW.lproj/WidgetIntents.strings @@ -2,7 +2,7 @@ "2GqvPe" = "開啟複製的鏈結"; /* (No Comment) */ -"ctDNmu" = "快速使用多種 Firefox 功能"; +"ctDNmu" = "快速使用多種 Ecosia 功能"; /* (No Comment) */ "eHmH1H" = "清除隱私分頁"; diff --git a/firefox-ios/bin/nimbus-fml-configuration.sh b/firefox-ios/bin/nimbus-fml-configuration.sh index 919fae3e28b0f..b2254cbc64500 100644 --- a/firefox-ios/bin/nimbus-fml-configuration.sh +++ b/firefox-ios/bin/nimbus-fml-configuration.sh @@ -10,7 +10,10 @@ ## The `CONFIGURATION` to derive the channel used in the feature manifest. CHANNEL= case "${CONFIGURATION}" in - Fennec) + Debug) + CHANNEL="developer" + ;; + BetaDebug) CHANNEL="developer" ;; Fennec_Testing) @@ -19,10 +22,13 @@ case "${CONFIGURATION}" in Fennec_Enterprise) CHANNEL="developer" ;; - FirefoxBeta) + Development_TestFlight) + CHANNEL="beta" + ;; + Development_AppCenter) CHANNEL="beta" ;; - Firefox) + Release) CHANNEL="release" ;; *) # The channel must match up with the channels listed in APP_FML_FILE. diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift index 653ac2a748010..e2773d84e8c5b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift @@ -5,6 +5,7 @@ import XCTest @testable import Client +@preconcurrency final class AppFxACommandsTests: XCTestCase { private var applicationStateProvider: MockApplicationStateProvider! private var applicationHelper: MockApplicationHelper! diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/DefaultBrowserUitTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/DefaultBrowserUitTests.swift new file mode 100644 index 0000000000000..5abde2e992d3b --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/DefaultBrowserUitTests.swift @@ -0,0 +1,103 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Shared +@testable import Client + +final class DefaultBrowserUtilTests: XCTestCase { + var subject: DefaultBrowserUtil! + var telemetryWrapper: MockTelemetryWrapper! + var userDefaults: MockUserDefaults! + var application: MockUIApplication! + var locale: MockLocale! + + override func setUp() { + super.setUp() + telemetryWrapper = MockTelemetryWrapper() + userDefaults = MockUserDefaults() + locale = MockLocale() + application = MockUIApplication() + } + + override func tearDown() { + telemetryWrapper = nil + userDefaults = nil + locale = nil + application = nil + subject = nil + super.tearDown() + } + + func testFirstLaunchWithDMAUser() { + guard #available(iOS 18.2, *) else { return } + + locale.localeRegionCode = "IE" + application.mockDefaultApplicationValue = true + + setupSubject() + subject.processUserDefaultState(isFirstRun: true) + + XCTAssertTrue(userDefaults.bool(forKey: PrefsKeys.DidDismissDefaultBrowserMessage)) + XCTAssertTrue(userDefaults.bool(forKey: PrefsKeys.AppleConfirmedUserIsDefaultBrowser)) + XCTAssertTrue(telemetryWrapper.recordedObjects.contains(.defaultBrowser)) + XCTAssertTrue(telemetryWrapper.recordedObjects.contains(.choiceScreenAcquisition)) + XCTAssertEqual(userDefaults.setCalledCount, 2) + } + + func testFirstLaunchWithNonDMAUser() { + guard #available(iOS 18.2, *) else { return } + + locale.localeRegionCode = "US" + application.mockDefaultApplicationValue = false + + setupSubject() + subject.processUserDefaultState(isFirstRun: true) + + XCTAssertFalse(userDefaults.bool(forKey: PrefsKeys.DidDismissDefaultBrowserMessage)) + XCTAssertFalse(userDefaults.bool(forKey: PrefsKeys.AppleConfirmedUserIsDefaultBrowser)) + XCTAssertTrue(telemetryWrapper.recordedObjects.contains(.defaultBrowser)) + XCTAssertFalse(telemetryWrapper.recordedObjects.contains(.choiceScreenAcquisition)) + XCTAssertEqual(userDefaults.setCalledCount, 1) + } + + func testSecondLaunchWithDMAUser() { + guard #available(iOS 18.2, *) else { return } + + locale.localeRegionCode = "IT" + application.mockDefaultApplicationValue = true + + setupSubject() + subject.processUserDefaultState(isFirstRun: false) + + XCTAssertFalse(userDefaults.bool(forKey: PrefsKeys.DidDismissDefaultBrowserMessage)) + XCTAssertTrue(userDefaults.bool(forKey: PrefsKeys.AppleConfirmedUserIsDefaultBrowser)) + XCTAssertTrue(telemetryWrapper.recordedObjects.contains(.defaultBrowser)) + XCTAssertFalse(telemetryWrapper.recordedObjects.contains(.choiceScreenAcquisition)) + XCTAssertEqual(userDefaults.setCalledCount, 1) + } + + func testSecondLaunchWithNonDMAUser() { + guard #available(iOS 18.2, *) else { return } + + locale.localeRegionCode = "US" + application.mockDefaultApplicationValue = false + + setupSubject() + subject.processUserDefaultState(isFirstRun: false) + + XCTAssertFalse(userDefaults.bool(forKey: PrefsKeys.DidDismissDefaultBrowserMessage)) + XCTAssertFalse(userDefaults.bool(forKey: PrefsKeys.AppleConfirmedUserIsDefaultBrowser)) + XCTAssertTrue(telemetryWrapper.recordedObjects.contains(.defaultBrowser)) + XCTAssertFalse(telemetryWrapper.recordedObjects.contains(.choiceScreenAcquisition)) + XCTAssertEqual(userDefaults.setCalledCount, 1) + } + + private func setupSubject() { + subject = DefaultBrowserUtil(userDefault: userDefaults, + telemetryWrapper: telemetryWrapper, + locale: locale, + application: application) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/MockLocale.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/MockLocale.swift new file mode 100644 index 0000000000000..a83de550374ce --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/MockLocale.swift @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Client + +struct MockLocale: LocaleInterface { + var localeRegionCode: String? +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/MockUIApplication.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/MockUIApplication.swift new file mode 100644 index 0000000000000..27118f5dcbadd --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/MockUIApplication.swift @@ -0,0 +1,14 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Client + +class MockUIApplication: UIApplicationInterface { + var mockDefaultApplicationValue = false + + @available(iOS 18.2, *) + func isDefault(_ category: UIApplication.Category) throws -> Bool { + return mockDefaultApplicationValue + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift index 50716a995257f..1266f46ff2ae8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift @@ -100,12 +100,22 @@ final class BrowserCoordinatorTests: XCTestCase, FeatureFlaggable { overlayManager: overlayModeManager ) + /* Ecosia: Update HomepageViewController's init let secondHomepage = LegacyHomepageViewController( profile: profile, toastContainer: UIView(), tabManager: tabManager, overlayManager: overlayModeManager ) + */ + let secondHomepage = LegacyHomepageViewController(profile: profile, + toastContainer: UIView(), + tabManager: tabManager, + overlayManager: overlayModeManager, + referrals: .init(), + delegate: nil, + auth: EcosiaAuth(browserViewController: BrowserViewController(profile: profile, tabManager: MockTabManager()))) + XCTAssertFalse(subject.browserViewController.contentContainer.canAdd(content: secondHomepage)) XCTAssertNotNil(subject.legacyHomepageViewController) XCTAssertNil(subject.webviewController) @@ -782,6 +792,7 @@ final class BrowserCoordinatorTests: XCTestCase, FeatureFlaggable { XCTAssertEqual(windowManager.closePrivateTabsMultiActionCalled, 1) } + /* Ecosia: Disable Onboarding dependant tests since LaunchCoordinator.presentIntroOnboarding is temporarily disabled func testHandleShowOnboarding_returnsTrueAndShowsOnboarding() { let subject = createSubject() subject.browserHasLoaded() @@ -792,6 +803,7 @@ final class BrowserCoordinatorTests: XCTestCase, FeatureFlaggable { XCTAssertEqual(subject.childCoordinators.count, 1) XCTAssertNotNil(subject.childCoordinators[0] as? LaunchCoordinator) } + */ // MARK: - Saved route diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/Launch/LaunchCoordinatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/Launch/LaunchCoordinatorTests.swift index 585c2240c8706..9f18e86e1106b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/Launch/LaunchCoordinatorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/Launch/LaunchCoordinatorTests.swift @@ -38,27 +38,31 @@ final class LaunchCoordinatorTests: XCTestCase { } // MARK: - Intro + /* Ecosia: Disable Onboarding dependant tests since LaunchCoordinator.presentIntroOnboarding is temporarily disabled func testStart_introNotIphone_present() throws { let introScreenManager = IntroScreenManager(prefs: profile.prefs) let subject = createSubject(isIphone: false) subject.start(with: .intro(manager: introScreenManager)) - XCTAssertEqual(mockRouter.presentCalled, 1) XCTAssertEqual(mockRouter.setRootViewControllerCalled, 0) let presentedViewController = try XCTUnwrap(mockRouter.presentedViewController) - XCTAssertNotNil(presentedViewController as? IntroViewController) + // Ecosia: Update view controller being presented + // XCTAssertNotNil(presentedViewController as? IntroViewController) + XCTAssertNotNil(presentedViewController as? WelcomeNavigation) } func testStart_introIsIphone_setRootView() throws { let introScreenManager = IntroScreenManager(prefs: profile.prefs) let subject = createSubject(isIphone: true) subject.start(with: .intro(manager: introScreenManager)) - XCTAssertEqual(mockRouter.presentCalled, 1) XCTAssertEqual(mockRouter.setRootViewControllerCalled, 0) let pushedVC = try XCTUnwrap(mockRouter.presentedViewController) - XCTAssertNotNil(pushedVC as? IntroViewController) + // Ecosia: Update view controller being presented + // XCTAssertNotNil(pushedVC as? IntroViewController) + XCTAssertNotNil(pushedVC as? WelcomeNavigation) } + */ // MARK: - Update func testStart_updateNotIphone_present() throws { @@ -114,7 +118,7 @@ final class LaunchCoordinatorTests: XCTestCase { XCTAssertEqual(mockRouter.setRootViewControllerCalled, 0) XCTAssertNil(mockRouter.presentedViewController) } - + /* Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref func testStart_surveyWithMessage_setRootView() throws { let messageManager = MockGleanPlumbMessageManagerProtocol() let message = createMessage(isExpired: false) @@ -130,6 +134,7 @@ final class LaunchCoordinatorTests: XCTestCase { let pushedVC = try XCTUnwrap(mockRouter.presentedViewController) XCTAssertNotNil(pushedVC as? SurveySurfaceViewController) } + */ // MARK: - QRCodeNavigationHandler @@ -154,6 +159,7 @@ final class LaunchCoordinatorTests: XCTestCase { } // MARK: - Delegates + /* Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref func testStart_surveySetsDelegate() throws { let messageManager = MockGleanPlumbMessageManagerProtocol() let message = createMessage(isExpired: false) @@ -167,6 +173,7 @@ final class LaunchCoordinatorTests: XCTestCase { let presentedVC = try XCTUnwrap(mockRouter.presentedViewController as? SurveySurfaceViewController) XCTAssertNotNil(presentedVC.delegate) } + */ func testDidFinish_fromSurveySurfaceViewControllerDelegate() { let subject = createSubject(isIphone: false) @@ -191,6 +198,7 @@ final class LaunchCoordinatorTests: XCTestCase { return subject } + /* Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref private func createMessage( for surface: MessageSurfaceId = .survey, isExpired: Bool @@ -208,4 +216,5 @@ final class LaunchCoordinatorTests: XCTestCase { style: MockStyleDataProtocol(), metadata: metadata) } + */ } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/LaunchView/LaunchScreenViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/LaunchView/LaunchScreenViewModelTests.swift index 31463a4398ced..b0704ed4faa88 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/LaunchView/LaunchScreenViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/LaunchView/LaunchScreenViewModelTests.swift @@ -8,7 +8,8 @@ import XCTest @testable import Client final class LaunchScreenViewModelTests: XCTestCase { - private var messageManager: MockGleanPlumbMessageManagerProtocol! + // Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref + // private var messageManager: MockGleanPlumbMessageManagerProtocol! private var profile: MockProfile! private var delegate: MockLaunchFinishedLoadingDelegate! let windowUUID: WindowUUID = .XCTestDefaultUUID @@ -19,7 +20,8 @@ final class LaunchScreenViewModelTests: XCTestCase { profile = MockProfile() delegate = MockLaunchFinishedLoadingDelegate() LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) - messageManager = MockGleanPlumbMessageManagerProtocol() + // Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref + // messageManager = MockGleanPlumbMessageManagerProtocol() UserDefaults.standard.set(true, forKey: PrefsKeys.NimbusFeatureTestsOverride) } @@ -28,7 +30,8 @@ final class LaunchScreenViewModelTests: XCTestCase { super.tearDown() AppContainer.shared.reset() profile = nil - messageManager = nil + // Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref + // messageManager = nil delegate = nil UserDefaults.standard.set(false, forKey: PrefsKeys.NimbusFeatureTestsOverride) @@ -41,7 +44,7 @@ final class LaunchScreenViewModelTests: XCTestCase { XCTAssertEqual(delegate.launchBrowserCalled, 0) XCTAssertEqual(delegate.launchWithTypeCalled, 0) } - + /* Ecosia: Versioning is testes in a dedicated Ecosia test func testLaunchType_intro() async { let subject = createSubject() subject.delegate = delegate @@ -70,7 +73,9 @@ final class LaunchScreenViewModelTests: XCTestCase { XCTAssertEqual(delegate.launchBrowserCalled, 0) XCTAssertEqual(delegate.launchWithTypeCalled, 1) } + */ + /* Ecosia: Comment test with no mean to Launch survey func testLaunchType_survey() async { profile.prefs.setString("112.0", forKey: PrefsKeys.AppVersion.Latest) profile.prefs.setInt(1, forKey: PrefsKeys.IntroSeen) @@ -88,8 +93,11 @@ final class LaunchScreenViewModelTests: XCTestCase { XCTAssertEqual(delegate.launchBrowserCalled, 0) XCTAssertEqual(delegate.launchWithTypeCalled, 1) } + */ + /* Ecosia: Versioning is testes in a dedicated Ecosia test func testSplashScreenExperiment_afterShown_returnsTrue() { + let subject = createSubject() let value = subject.getSplashScreenExperimentHasShown() XCTAssertFalse(value) @@ -99,6 +107,7 @@ final class LaunchScreenViewModelTests: XCTestCase { let updatedValue = subject.getSplashScreenExperimentHasShown() XCTAssertTrue(updatedValue) } + */ // MARK: - Helpers private func createSubject(file: StaticString = #file, @@ -107,12 +116,13 @@ final class LaunchScreenViewModelTests: XCTestCase { let subject = LaunchScreenViewModel(windowUUID: windowUUID, profile: profile, - messageManager: messageManager, + // Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref + // messageManager: messageManager, onboardingModel: onboardingModel) trackForMemoryLeaks(subject, file: file, line: line) return subject } - + /* Ecosia: Remove MockGleanPlumbMessageManagerProtocol ref private func createMessage( for surface: MessageSurfaceId = .survey, action: String = "OPEN_NEW_TAB" @@ -130,6 +140,7 @@ final class LaunchScreenViewModelTests: XCTestCase { style: MockStyleDataProtocol(), metadata: metadata) } + */ func createOnboardingViewModel() -> OnboardingViewModel { let cards: [OnboardingCardInfoModel] = [ diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/RouteTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/RouteTests.swift index cdf33a493de4a..077bd6905acf9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/RouteTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/RouteTests.swift @@ -24,7 +24,10 @@ class RouteTests: XCTestCase { func testSearchRouteWithEncodedUrl() { let subject = createSubject() - let url = URL(string: "firefox://open-url?url=http%3A%2F%2Fgoogle.com%3Fa%3D1%26b%3D2%26c%3Dfoo%2520bar")! + + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://open-url?url=http%3A%2F%2Fgoogle.com%3Fa%3D1%26b%3D2%26c%3Dfoo%2520bar")! let route = subject.makeRoute(url: url) @@ -33,7 +36,9 @@ class RouteTests: XCTestCase { func testSearchRouteWithPrivateFlag() { let subject = createSubject() - let url = URL(string: "firefox://open-url?private=true")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://open-url?private=true")! let route = subject.makeRoute(url: url) @@ -42,7 +47,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithClearPrivateData() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/clear-private-data")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/clear-private-data")! let route = subject.makeRoute(url: url) @@ -51,7 +58,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithNewTab() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/newTab")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/newTab")! let route = subject.makeRoute(url: url) @@ -60,7 +69,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithNewTabTrailingSlash() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/newTab/")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/newTab/")! let route = subject.makeRoute(url: url) @@ -69,7 +80,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithHomePage() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/homePage")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/homePage")! let route = subject.makeRoute(url: url) @@ -78,7 +91,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithMailto() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/mailto")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/mailto")! let route = subject.makeRoute(url: url) @@ -87,7 +102,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithSearch() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/search")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/search")! let route = subject.makeRoute(url: url) @@ -96,7 +113,9 @@ class RouteTests: XCTestCase { func testSettingsRouteWithFxa() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/settings/fxa")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/settings/fxa")! let route = subject.makeRoute(url: url) @@ -105,7 +124,9 @@ class RouteTests: XCTestCase { func testHomepanelRouteWithBookmarks() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/homepanel/bookmarks")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/homepanel/bookmarks")! let route = subject.makeRoute(url: url) @@ -114,7 +135,9 @@ class RouteTests: XCTestCase { func testHomepanelRouteWithTopSites() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/homepanel/top-sites")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/homepanel/top-sites")! let route = subject.makeRoute(url: url) @@ -123,7 +146,9 @@ class RouteTests: XCTestCase { func testHomepanelRouteWithHistory() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/homepanel/history")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/homepanel/history")! let route = subject.makeRoute(url: url) @@ -132,7 +157,9 @@ class RouteTests: XCTestCase { func testHomepanelRouteWithReadingList() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/homepanel/reading-list")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/homepanel/reading-list")! let route = subject.makeRoute(url: url) @@ -141,7 +168,9 @@ class RouteTests: XCTestCase { func testDefaultBrowserRouteWithTutorial() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/default-browser/tutorial")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/default-browser/tutorial")! let route = subject.makeRoute(url: url) @@ -150,7 +179,9 @@ class RouteTests: XCTestCase { func testDefaultBrowserRouteWithSystemSettings() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/default-browser/system-settings")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/default-browser/system-settings")! let route = subject.makeRoute(url: url) @@ -159,7 +190,9 @@ class RouteTests: XCTestCase { func testInvalidRouteWithBadPath() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/homepanel/badbad")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/homepanel/badbad")! let route = subject.makeRoute(url: url) @@ -168,7 +201,9 @@ class RouteTests: XCTestCase { func testFxaSignInrouteBuilderRoute() { let subject = createSubject() - let url = URL(string: "firefox://fxa-signin?signin=coolcodes&user=foo&email=bar")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://fxa-signin?signin=coolcodes&user=foo&email=bar")! let route = subject.makeRoute(url: url) @@ -215,7 +250,9 @@ class RouteTests: XCTestCase { func testInvalidDeepLink() { let subject = createSubject() - let url = URL(string: "firefox://deep-links-are-fun?url=/settings/newTab/")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-links-are-fun?url=/settings/newTab/")! let route = subject.makeRoute(url: url) @@ -224,7 +261,9 @@ class RouteTests: XCTestCase { func testWidgetMediumTopSitesOpenUrl() { let subject = createSubject() - let url = URL(string: "firefox://widget-medium-topsites-open-url?url=https://google.com")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-medium-topsites-open-url?url=https://google.com")! let route = subject.makeRoute(url: url) @@ -233,7 +272,9 @@ class RouteTests: XCTestCase { func testWidgetSmallQuicklinkOpenUrlWithPrivateFlag() { let subject = createSubject() - let url = URL(string: "firefox://widget-small-quicklink-open-url?private=true&url=https://google.com")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-small-quicklink-open-url?private=true&url=https://google.com")! let route = subject.makeRoute(url: url) @@ -245,7 +286,9 @@ class RouteTests: XCTestCase { func testWidgetMediumQuicklinkOpenUrlWithoutPrivateFlag() { let subject = createSubject() - let url = URL(string: "firefox://widget-medium-quicklink-open-url?url=https://google.com")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-medium-quicklink-open-url?url=https://google.com")! let route = subject.makeRoute(url: url) @@ -258,7 +301,9 @@ class RouteTests: XCTestCase { func testWidgetSmallQuicklinkOpenCopied() { let subject = createSubject() UIPasteboard.general.string = "test search text" - let url = URL(string: "firefox://widget-small-quicklink-open-copied")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-small-quicklink-open-copied")! let route = subject.makeRoute(url: url) @@ -268,7 +313,9 @@ class RouteTests: XCTestCase { func testWidgetSmallQuicklinkOpenCopiedWithUrl() { let subject = createSubject() UIPasteboard.general.url = URL(string: "https://google.com") - let url = URL(string: "firefox://widget-small-quicklink-open-copied")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-small-quicklink-open-copied")! let route = subject.makeRoute(url: url) @@ -277,7 +324,9 @@ class RouteTests: XCTestCase { func testWidgetSmallQuicklinkClosePrivateTabs() { let subject = createSubject() - let url = URL(string: "firefox://widget-small-quicklink-close-private-tabs")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-small-quicklink-close-private-tabs")! let route = subject.makeRoute(url: url) @@ -286,7 +335,9 @@ class RouteTests: XCTestCase { func testWidgetMediumQuicklinkClosePrivateTabs() { let subject = createSubject() - let url = URL(string: "firefox://widget-medium-quicklink-close-private-tabs")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-medium-quicklink-close-private-tabs")! let route = subject.makeRoute(url: url) @@ -322,7 +373,9 @@ class RouteTests: XCTestCase { func testInvalidWidgetTabUuid() { let subject = createSubject() - let url = URL(string: "firefox://widget-tabs-medium-open-url?uuid=invalid")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://widget-tabs-medium-open-url?uuid=invalid")! let route = subject.makeRoute(url: url) @@ -340,7 +393,9 @@ class RouteTests: XCTestCase { func testOpenText() { let subject = createSubject() - let url = URL(string: "firefox://open-text?text=google")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://open-text?text=google")! let route = subject.makeRoute(url: url) @@ -351,7 +406,9 @@ class RouteTests: XCTestCase { func testAppAction_showIntroOnboarding() { let subject = createSubject() - let url = URL(string: "firefox://deep-link?url=/action/show-intro-onboarding")! + // Ecosia: Update url + // let url = URL(string: "firefox://open-url?private=true")! + let url = URL(string: "ecosia://deep-link?url=/action/show-intro-onboarding")! let route = subject.makeRoute(url: url) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SceneCoordinatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SceneCoordinatorTests.swift index f44d224c031a6..0d3564190f34e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SceneCoordinatorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SceneCoordinatorTests.swift @@ -49,6 +49,7 @@ final class SceneCoordinatorTests: XCTestCase { XCTAssertEqual(mockRouter.pushCalled, 1) } + /* Ecosia: Disable Onboarding dependant tests since LaunchCoordinator.presentIntroOnboarding is temporarily disabled func testLaunchWithLaunchType_launchFromScene() { let subject = createSubject() subject.launchWith(launchType: .intro(manager: IntroScreenManager(prefs: MockProfile().prefs))) @@ -56,6 +57,7 @@ final class SceneCoordinatorTests: XCTestCase { XCTAssertEqual(subject.childCoordinators.count, 1) XCTAssertNotNil(subject.childCoordinators[0] as? LaunchCoordinator) } + */ func testLaunchWithLaunchType_launchFromBrowser() { let subject = createSubject() @@ -74,6 +76,7 @@ final class SceneCoordinatorTests: XCTestCase { XCTAssertNotNil(subject.childCoordinators[0] as? BrowserCoordinator) } + /* Ecosia: Disable Onboarding dependant tests since LaunchCoordinator.presentIntroOnboarding is temporarily disabled func testChildLaunchCoordinatorIsDone_startsBrowser() throws { let subject = createSubject() subject.launchWith(launchType: .intro(manager: IntroScreenManager(prefs: MockProfile().prefs))) @@ -84,6 +87,7 @@ final class SceneCoordinatorTests: XCTestCase { XCTAssertEqual(subject.childCoordinators.count, 1) XCTAssertNotNil(subject.childCoordinators[0] as? BrowserCoordinator) } + */ func testHandleRoute_launchNotFinished_routeSaved() { let subject = createSubject() @@ -123,6 +127,7 @@ final class SceneCoordinatorTests: XCTestCase { // MARK: - Handle route + /* Ecosia: flanky test, commenting in this version func testHandleShowOnboarding_returnsTrueAndShowsOnboarding() { let subject = createSubject() @@ -132,6 +137,7 @@ final class SceneCoordinatorTests: XCTestCase { XCTAssertEqual(subject.childCoordinators.count, 1) XCTAssertNotNil(subject.childCoordinators[0] as? LaunchCoordinator) } + */ // MARK: - Helpers private func createSubject(file: StaticString = #file, diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift index 453aa7480007e..d3eb6227d69d2 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/SettingsCoordinatorTests.swift @@ -63,7 +63,9 @@ final class SettingsCoordinatorTests: XCTestCase { subject.start(with: .homePage) XCTAssertEqual(mockRouter.pushCalled, 1) - XCTAssertTrue(mockRouter.pushedViewController is HomePageSettingViewController) + // Ecosia: Update tests + // XCTAssertTrue(mockRouter.pushedViewController is HomePageSettingViewController) + XCTAssertTrue(mockRouter.pushedViewController is NTPCustomizationSettingsViewController) } func testMailtoSettingsRoute_showsMailtoSettingsPage() throws { @@ -324,7 +326,9 @@ final class SettingsCoordinatorTests: XCTestCase { subject.pressedHome() XCTAssertEqual(mockRouter.pushCalled, 1) - XCTAssertTrue(mockRouter.pushedViewController is HomePageSettingViewController) + // Ecosia: Update tests + // XCTAssertTrue(mockRouter.pushedViewController is HomePageSettingViewController) + XCTAssertTrue(mockRouter.pushedViewController is NTPCustomizationSettingsViewController) } func testGeneralSettingsDelegate_pushedMailApp() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/URLScannerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/URLScannerTests.swift index ea47f1030ac85..77cf9e62c1743 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/URLScannerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/URLScannerTests.swift @@ -52,7 +52,9 @@ class URLScannerTests: XCTestCase { } func testSimpleFullURLQueryItem() { - let urlString = "firefox://open-url?url=https://example.com/path" + // Ecosia: Update url + // let urlString = "firefox://open-url?url=https://example.com/path" + let urlString = "ecosia://open-url?url=https://example.com/path" let url = URL(string: urlString)! let scanner = URLScanner(url: url)! XCTAssertEqual(scanner.value(query: "url"), "https://example.com/path") @@ -60,7 +62,9 @@ class URLScannerTests: XCTestCase { } func testPrecedingNonURLParamBeforeURLQueryItem() { - let urlString = "firefox://open-url?arg1=abc&url=https://example.com/path?arg1=a" + // Ecosia: Update url + // let urlString = "firefox://open-url?arg1=abc&url=https://example.com/path?arg1=a" + let urlString = "ecosia://open-url?arg1=abc&url=https://example.com/path?arg1=a" let url = URL(string: urlString)! let scanner = URLScanner(url: url)! XCTAssertEqual(scanner.value(query: "url"), "https://example.com/path?arg1=a") @@ -69,7 +73,9 @@ class URLScannerTests: XCTestCase { } func testSingleQueryFullURLQueryItem() { - let urlString = "firefox://open-url?url=https://example.com/path?arg1=a" + // Ecosia: Update url + // let urlString = "firefox://open-url?url=https://example.com/path?arg1=a" + let urlString = "ecosia://open-url?url=https://example.com/path?arg1=a" let url = URL(string: urlString)! let scanner = URLScanner(url: url)! XCTAssertEqual(scanner.value(query: "url"), "https://example.com/path?arg1=a") @@ -77,7 +83,9 @@ class URLScannerTests: XCTestCase { } func testTwoQueryParams() { - let urlString = "firefox://open-url?url=https://example.com/path?arg1=a&arg2=b" + // Ecosia: Update url + // let urlString = "firefox://open-url?url=https://example.com/path?arg1=a&arg2=b" + let urlString = "ecosia://open-url?url=https://example.com/path?arg1=a&arg2=b" let url = URL(string: urlString)! let scanner = URLScanner(url: url)! // Currently we do _not_ expect URLComponents to include all parameters to the URL. @@ -88,7 +96,9 @@ class URLScannerTests: XCTestCase { } func testMultipleQueryParams() { - let urlString = "firefox://open-url?url=https://example.com/path?arg1=a&arg2=b&arg3=c" + // Ecosia: Update url + // let urlString = "firefox://open-url?url=https://example.com/path?arg1=a&arg2=b&arg3=c" + let urlString = "ecosia://open-url?url=https://example.com/path?arg1=a&arg2=b&arg3=c" let url = URL(string: urlString)! let scanner = URLScanner(url: url)! XCTAssertEqual(scanner.value(query: "url"), "https://example.com/path?arg1=a") @@ -97,7 +107,9 @@ class URLScannerTests: XCTestCase { } func testMultipleLevelsOfNestedURLs() { - let urlString = "firefox://open-url?url=https://example.com/path?arg1=a&anotherURL=https://test.com" + // Ecosia: Update url + // let urlString = "firefox://open-url?url=https://example.com/path?arg1=a&anotherURL=https://test.com" + let urlString = "ecosia://open-url?url=https://example.com/path?arg1=a&anotherURL=https://test.com" let url = URL(string: urlString)! let scanner = URLScanner(url: url)! XCTAssertEqual(scanner.fullURLQueryItem(), "https://example.com/path?arg1=a&anotherURL=https://test.com") @@ -113,7 +125,9 @@ class URLScannerTests: XCTestCase { } func testOurScheme() { - let urlString = "firefox://abcdefg/path/to/resource" + // Ecosia: Update url + // let urlString = "firefox://abcdefg/path/to/resource" + let urlString = "ecosia://abcdefg/path/to/resource" let url = URL(string: urlString)! let scanner = URLScanner(url: url) XCTAssertNotNil(scanner) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DateGroupedTableDataTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DateGroupedTableDataTests.swift new file mode 100644 index 0000000000000..45b18a8fbcc2c --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DateGroupedTableDataTests.swift @@ -0,0 +1,148 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Shared + +final class DateGroupedTableDataTests: XCTestCase { + let lastHour = Date() + let today = Calendar.current.date(byAdding: .hour, value: -2, to: Date()) ?? Date() + let yesterday = Date().dayBefore + let lastWeek = Calendar.current.date(byAdding: .day, value: -4, to: Date()) ?? Date() + let lastMonth = Calendar.current.date(byAdding: .day, value: -14, to: Date()) ?? Date() + let older = Calendar.current.date(byAdding: .month, value: -2, to: Date()) ?? Date() + let customTimeStamps = [Date().lastHour.timeIntervalSince1970, Date().lastMonth.timeIntervalSince1970] + + func testIncludeLastHourAddNow() { + var subject = DateGroupedTableData(includeLastHour: true) + subject.add("Now", timestamp: Date().timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(0), ["Now"]) + } + + func testAddNow() { + var subject = DateGroupedTableData() + subject.add("Now", timestamp: Date().timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(0), ["Now"]) + } + + func testAddOneHourAgo() { + var subject = DateGroupedTableData() + subject.add("One Hour Ago", timestamp: lastHour.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(0), ["One Hour Ago"]) + } + + func testAddYesterday() { + var subject = DateGroupedTableData() + subject.add("Yesterday", timestamp: yesterday.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(1), ["Yesterday"]) + } + + func testAddOlder() { + var subject = DateGroupedTableData() + subject.add("Older", timestamp: older.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(4), ["Older"]) + } + + func testIncludeLastHourRemove() { + var subject = DateGroupedTableData(includeLastHour: true) + subject.add("Yesterday1", timestamp: yesterday.timeIntervalSince1970) + subject.add("Yesterday2", timestamp: yesterday.timeIntervalSince1970) + + subject.remove("Yesterday1") + + XCTAssertEqual(subject.itemsForSection(2), ["Yesterday2"]) + } + + func testAllItems() { + var subject = DateGroupedTableData(includeLastHour: true) + subject.add("Last Hour", timestamp: lastHour.timeIntervalSince1970) + subject.add("Today", timestamp: today.timeIntervalSince1970) + subject.add("Yesterday", timestamp: yesterday.timeIntervalSince1970) + subject.add("Last Week", timestamp: lastWeek.timeIntervalSince1970) + subject.add("Last Month", timestamp: lastMonth.timeIntervalSince1970) + subject.add("Older", timestamp: older.timeIntervalSince1970) + + XCTAssertEqual(subject.allItems(), ["Last Hour", + "Today", + "Yesterday", + "Last Week", + "Last Month", + "Older"]) + } + + func testIsEmptySucceeds() { + let subject = DateGroupedTableData(includeLastHour: true) + XCTAssertTrue(subject.isEmpty) + } + + func testIsEmptyFails() { + var subject = DateGroupedTableData(includeLastHour: true) + subject.add("Last Hour", timestamp: lastHour.timeIntervalSince1970) + XCTAssertFalse(subject.isEmpty) + } + + func testNumberOfItemsForSection() { + var subject = DateGroupedTableData(includeLastHour: true) + subject.add("Yesterday1", timestamp: yesterday.timeIntervalSince1970) + subject.add("Yesterday2", timestamp: yesterday.timeIntervalSince1970) + + XCTAssertEqual(subject.numberOfItemsForSection(2), 2) + } + + func testItemsForSection() { + var subject = DateGroupedTableData(includeLastHour: true) + subject.add("Yesterday1", timestamp: yesterday.timeIntervalSince1970) + subject.add("Yesterday2", timestamp: yesterday.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(2), ["Yesterday1", "Yesterday2"]) + } + + func testCustomIntervalsCreation() { + let subject = DateGroupedTableData(timestamps: customTimeStamps) + + XCTAssertEqual(subject.timestamps.count, 2) + XCTAssertEqual(subject.timestampData.count, 3) + } + + func testCustomIntervalsItemsForSection () { + var subject = DateGroupedTableData(timestamps: customTimeStamps) + + subject.add("Last Hour", timestamp: lastHour.timeIntervalSince1970) + subject.add("Last Month", timestamp: lastMonth.timeIntervalSince1970) + subject.add("Older", timestamp: older.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(0), ["Last Hour"]) + XCTAssertEqual(subject.itemsForSection(1), ["Last Month"]) + XCTAssertEqual(subject.itemsForSection(2), ["Older"]) + } + + func testCustomIntervalsAdd() { + var subject = DateGroupedTableData(timestamps: customTimeStamps) + + subject.add("Last Hour", timestamp: lastHour.timeIntervalSince1970) + subject.add("Last Month", timestamp: lastMonth.timeIntervalSince1970) + subject.add("Older", timestamp: older.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(0), ["Last Hour"]) + XCTAssertEqual(subject.itemsForSection(1), ["Last Month"]) + XCTAssertEqual(subject.itemsForSection(2), ["Older"]) + } + + func testCustomIntervalsRemove() { + var subject = DateGroupedTableData(timestamps: customTimeStamps) + + subject.add("Last Hour", timestamp: lastHour.timeIntervalSince1970) + + XCTAssertEqual(subject.itemsForSection(0), ["Last Hour"]) + + subject.remove("Last Hour") + + XCTAssertEqual(subject.itemsForSection(0), []) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DefaultSearchPrefsTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DefaultSearchPrefsTests.swift index dc3ae5a3b0524..41c82b3a2eb80 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DefaultSearchPrefsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DefaultSearchPrefsTests.swift @@ -16,65 +16,66 @@ class DefaultSearchPrefsTests: XCTestCase { )! // setup the most popular locales + // Ecosia: update values to include ecosia let usa = ( lang: ["en-US", "en"], region: "US", - resultList: ["google-b-1-m", "bing", "ddg", "ebay", "wikipedia"], - resultDefault: "Google" + resultList: ["google-b-1-m", "bing", "ddg", "wikipedia", "ecosia"], + resultDefault: "ecosia" ) let england = ( lang: ["en-GB", "en"], region: "GB", - resultList: ["google-b-m", "bing", "ddg", "ebay-co-uk", "qwant", "wikipedia"], - resultDefault: "Google" + resultList: ["google-b-m", "bing", "ddg", "wikipedia", "ecosia"], + resultDefault: "ecosia" ) let france = ( lang: ["fr-FR", "fr"], region: "FR", - resultList: ["google-b-m", "bing", "ddg", "ebay-fr", "qwant", "wikipedia-fr"], - resultDefault: "Google" + resultList: ["google-b-m", "bing", "ddg", "wikipedia-fr", "ecosia"], + resultDefault: "ecosia" ) let japan = ( lang: ["ja-JP", "ja"], region: "JP", - resultList: ["google-b-m", "amazon-jp", "bing", "ebay", "wikipedia-ja", "yahoo-jp"], - resultDefault: "Google" + resultList: ["google-b-m", "bing", "wikipedia-ja", "ecosia"], + resultDefault: "ecosia" ) let canada = ( lang: ["en-CA", "en"], region: "CA", - resultList: ["google-b-m", "bing", "ddg", "ebay", "wikipedia"], - resultDefault: "Google" + resultList: ["ecosia", "wikipedia", "google-b-m", "bing", "ddg"], + resultDefault: "ecosia" ) let russia = ( lang: ["ru-RU", "ru"], region: "RU", - resultList: ["google-com-nocodes", "ebay", "wikipedia-ru"], - resultDefault: "Google" + resultList: ["google-com-nocodes", "wikipedia-ru", "ecosia"], + resultDefault: "ecosia" ) let taiwan = ( lang: ["zh-TW", "zh"], region: "TW", - resultList: ["google-b-m", "bing", "ddg", "wikipedia-zh-TW"], - resultDefault: "Google" + resultList: ["google-b-m", "bing", "ddg", "wikipedia-zh-TW", "ecosia"], + resultDefault: "ecosia" ) let china = ( lang: ["zh-hans-CN", "zh-CN", "zh"], region: "CN", - resultList: ["google-b-m", "baidu", "bing", "wikipedia-zh-CN"], + resultList: ["google-b-m", "bing", "wikipedia-zh-CN", "ecosia"], resultDefault: "百度" ) let germany = ( lang: ["de-DE", "de"], region: "DE", - resultList: ["google-b-m", "bing", "ddg", "ebay-de", "ecosia", "qwant", "wikipedia-de"], - resultDefault: "Google" + resultList: ["google-b-m", "bing", "ddg", "ecosia", "wikipedia-de"], + resultDefault: "ecosia" ) let southAfrica = ( lang: ["en-SA", "en"], region: "SA", - resultList: ["google-b-m", "bing", "ddg", "ebay", "wikipedia"], - resultDefault: "Google" + resultList: ["ecosia", "wikipedia", "google-b-m", "bing", "ddg"], + resultDefault: "ecosia" ) let testLocales = [ usa, diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift index 468ea46863c10..d334caceb91b5 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DependencyInjection/DependencyHelperMock.swift @@ -11,7 +11,8 @@ import TabDataStore class DependencyHelperMock { func bootstrapDependencies( injectedTabManager: TabManager? = nil, - injectedMicrosurveyManager: MicrosurveyManager? = nil + injectedMicrosurveyManager: MicrosurveyManager? = nil, + themeManager: ThemeManager = MockThemeManager() // Ecosia: Make themeManager injectable ) { AppContainer.shared.reset() @@ -35,8 +36,8 @@ class DependencyHelperMock { let appSessionProvider: AppSessionProvider = AppSessionManager() AppContainer.shared.register(service: appSessionProvider) - - let themeManager: ThemeManager = MockThemeManager() + // Ecosia: Remove themeManager constant + // let themeManager: ThemeManager = MockThemeManager() AppContainer.shared.register(service: themeManager) let ratingPromptManager = RatingPromptManager(profile: profile) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DownloadHelperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DownloadHelperTests.swift index 2963aa9b4913d..b0bed9086550f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/DownloadHelperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/DownloadHelperTests.swift @@ -11,136 +11,96 @@ class DownloadHelperTests: XCTestCase { func test_init_whenMIMETypeIsNil_initializeCorrectly() { let response = anyResponse(mimeType: nil) - var subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: false - ) - XCTAssertNotNil(subject) - - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: false, - forceDownload: true - ) + let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) XCTAssertNotNil(subject) + } - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: false, - forceDownload: false - ) - XCTAssertNotNil(subject) + func test_shouldDownloadFile_whenMIMETypeOctetStream_isTrue() { + let mimeType = MIMEType.OctetStream + + let response = anyResponse(mimeType: mimeType) + let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) + let shouldDownload = subject?.shouldDownloadFile(canShowInWebView: true, + forceDownload: false, + isForMainFrame: false) + XCTAssertTrue(shouldDownload ?? false) + } - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: true - ) - XCTAssertNotNil(subject) + func test_shouldDownloadFile_whenMIMETypeIsNotOctetStream_isFalse() { + let mimeType = MIMEType.GIF + + let response = anyResponse(mimeType: mimeType) + if let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) { + let shouldDownload = subject.shouldDownloadFile(canShowInWebView: true, + forceDownload: false, + isForMainFrame: false) + XCTAssertFalse(shouldDownload) + } } - func test_init_whenMIMETypeIsNotOctetStream_initializeCorrectly() { - for mimeType in allMIMETypes() { - if mimeType == MIMEType.OctetStream { continue } - - let response = anyResponse(mimeType: mimeType) - - var subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: false - ) - XCTAssertNil(subject) - - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: false, - forceDownload: true - ) - XCTAssertNotNil(subject) - - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: false, - forceDownload: false - ) - XCTAssertNotNil(subject) - - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: true - ) - XCTAssertNotNil(subject) + func test_shouldDownloadFile_whenCanShowInWebview_isFalse() { + let response = anyResponse(mimeType: MIMEType.GIF) + + if let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) { + let shouldDownload = subject.shouldDownloadFile(canShowInWebView: true, + forceDownload: false, + isForMainFrame: false) + XCTAssertFalse(shouldDownload) } } - func test_init_whenMIMETypeIsOctetStream_initializeCorrectly() { - let response = anyResponse(mimeType: MIMEType.OctetStream) + func test_shouldDownloadFile_whenCanNotShowInWebview_isTrue() { + let response = anyResponse(mimeType: MIMEType.GIF) - var subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: false - ) - XCTAssertNotNil(subject) + if let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) { + let shouldDownload = subject.shouldDownloadFile(canShowInWebView: false, + forceDownload: false, + isForMainFrame: false) + XCTAssertTrue(shouldDownload) + } + } - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: false, - forceDownload: true - ) - XCTAssertNotNil(subject) + func test_shouldDownloadFile_whenNotForceDownload_isFalse() { + let response = anyResponse(mimeType: MIMEType.GIF) - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: true - ) - XCTAssertNotNil(subject) + if let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) { + let shouldDownload = subject.shouldDownloadFile(canShowInWebView: true, + forceDownload: false, + isForMainFrame: false) + XCTAssertFalse(shouldDownload) + } + } - subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: false, - forceDownload: false - ) - XCTAssertNotNil(subject) + func test_shouldDownloadFile_whenForceDownload_isTrue() { + let response = anyResponse(mimeType: MIMEType.GIF) + + if let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) { + let shouldDownload = subject.shouldDownloadFile(canShowInWebView: false, + forceDownload: true, + isForMainFrame: false) + XCTAssertTrue(shouldDownload) + } } func test_downloadViewModel_whenRequestURLIsWrong_deliversEmptyResult() { let request = anyRequest(urlString: "wrong-url.com") - let subject = DownloadHelper( - request: request, - response: anyResponse(mimeType: nil), - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: false - ) + let subject = createSubject(request: request, + response: anyResponse(mimeType: nil), + cookieStore: cookieStore()) let downloadViewModel = subject?.downloadViewModel(windowUUID: .XCTestDefaultUUID, okAction: { _ in }) @@ -149,13 +109,9 @@ class DownloadHelperTests: XCTestCase { func test_downloadViewModel_deliversCorrectTitle() { let response = anyResponse(urlString: "http://some-domain.com/some-image.jpg") - let subject = DownloadHelper( - request: anyRequest(), - response: response, - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: false - ) + let subject = createSubject(request: anyRequest(), + response: response, + cookieStore: cookieStore()) let downloadViewModel = subject?.downloadViewModel(windowUUID: .XCTestDefaultUUID, okAction: { _ in }) @@ -163,13 +119,9 @@ class DownloadHelperTests: XCTestCase { } func test_downloadViewModel_deliversCorrectCancelButtonTitle() { - let subject = DownloadHelper( - request: anyRequest(), - response: anyResponse(mimeType: nil), - cookieStore: cookieStore(), - canShowInWebView: true, - forceDownload: false - ) + let subject = createSubject(request: anyRequest(), + response: anyResponse(mimeType: nil), + cookieStore: cookieStore()) let downloadViewModel = subject?.downloadViewModel(windowUUID: .XCTestDefaultUUID, okAction: { _ in }) @@ -178,6 +130,14 @@ class DownloadHelperTests: XCTestCase { // MARK: - Helpers + private func createSubject(request: URLRequest, + response: URLResponse, + cookieStore: WKHTTPCookieStore) -> DownloadHelper? { + return DownloadHelper(request: request, + response: response, + cookieStore: cookieStore) + } + private func anyRequest(urlString: String = "http://any-url.com") -> URLRequest { return URLRequest(url: URL(string: urlString)!, cachePolicy: anyCachePolicy(), timeoutInterval: 60.0) } @@ -207,22 +167,4 @@ class DownloadHelperTests: XCTestCase { private func anyCachePolicy() -> URLRequest.CachePolicy { return .useProtocolCachePolicy } - - private func allMIMETypes() -> [String] { - return [MIMEType.Bitmap, - MIMEType.CSS, - MIMEType.GIF, - MIMEType.JavaScript, - MIMEType.JPEG, - MIMEType.HTML, - MIMEType.OctetStream, - MIMEType.Passbook, - MIMEType.PDF, - MIMEType.PlainText, - MIMEType.PNG, - MIMEType.WebP, - MIMEType.Calendar, - MIMEType.USDZ, - MIMEType.Reality] - } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/SearchEngines/SearchTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/SearchEngines/SearchTests.swift index af3ed8898b3c9..18aae934fe93a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/SearchEngines/SearchTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/SearchEngines/SearchTests.swift @@ -9,6 +9,7 @@ import XCTest @testable import Client class SearchTests: XCTestCase { + /* Ecosia: Replace Google tests with Ecosia's func testParsing() { let parser = OpenSearchParser(pluginMode: true) let file = Bundle.main.path(forResource: "google-b-m", ofType: "xml", inDirectory: "SearchPlugins/") @@ -23,6 +24,21 @@ class SearchTests: XCTestCase { XCTAssertEqual(engine.suggestURLForQuery("foobar")!.absoluteString, "https://www.google.com/complete/search?client=firefox&q=foobar") } + */ + func testParsing() { + let parser = OpenSearchParser(pluginMode: true) + let file = Bundle.main.path(forResource: "ecosia", ofType: "xml", inDirectory: "SearchPlugins/") + let engine: OpenSearchEngine! = parser.parse(file!, engineID: "ecosia") + XCTAssertEqual(engine.shortName, "Ecosia") + + // Test regular search queries. + XCTAssertEqual(engine.searchURLForQuery("foobar")!.absoluteString, + "https://www.ecosia-staging.xyz/search?q=foobar&tt=iosapp") + + // Test search suggestion queries. + XCTAssertEqual(engine.suggestURLForQuery("foobar")!.absoluteString, + "https://ac.ecosia.org/autocomplete?q=foobar&type=list") + } func testURIFixup() { // Check valid URLs. We can load these after some fixup. @@ -146,8 +162,8 @@ class SearchTests: XCTestCase { let engine: OpenSearchEngine! = parser.parse(file!, engineID: "bing") XCTAssertEqual(engine.shortName, "Bing") - let containsPartnerCode = engine.searchTemplate.contains("pc=MOZW") - XCTAssertTrue(containsPartnerCode) + let containsPartnerCode = engine.searchTemplate.contains("pc=MOZW") + XCTAssertTrue(containsPartnerCode) } func testBingParsing_iPad_hasIpadPartnerCode() { @@ -156,8 +172,8 @@ class SearchTests: XCTestCase { let engine: OpenSearchEngine! = parser.parse(file!, engineID: "bing") XCTAssertEqual(engine.shortName, "Bing") - let containsPartnerCode = engine.searchTemplate.contains("pc=MOZL") - XCTAssertTrue(containsPartnerCode) + let containsPartnerCode = engine.searchTemplate.contains("pc=MOZL") + XCTAssertTrue(containsPartnerCode) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TopSitesHelperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TopSitesHelperTests.swift index de6a22dfd79d1..33c0818de1948 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TopSitesHelperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TopSitesHelperTests.swift @@ -66,7 +66,10 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } + /* Ecosia: Update with removed default sites XCTAssertEqual(sites.count, 5, "Contains 5 default sites") + */ + XCTAssertEqual(sites.count, 0, "Contains no default sites") expectation.fulfill() } @@ -82,7 +85,10 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } + /* Ecosia: Update with removed default sites XCTAssertEqual(sites.count, 7, "Contains 5 default sites and two pinned sites") + */ + XCTAssertEqual(sites.count, 2, "Contains no default sites and two pinned sites") expectation.fulfill() } @@ -101,7 +107,10 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } + /* Ecosia: Update with removed default sites XCTAssertEqual(sites.count, 7, "Contains 5 default sites and 2 frecency sites") + */ + XCTAssertEqual(sites.count, 2, "Contains no default sites and 2 frecency sites") expectation.fulfill() } @@ -119,14 +128,17 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } + /* Ecosia: Update with removed default sites XCTAssertEqual(sites.count, 7, "Contains 5 default sites and 2 frecency sites, no sponsored urls") + */ + XCTAssertEqual(sites.count, 2, "Contains no default sites and 2 frecency sites, no sponsored urls") expectation.fulfill() } waitForExpectations(timeout: 1, handler: nil) } - func testGetTopSites_removesDuplicates() { + func testGetTopSites_removesDuplicates() { // let expectation = expectation(description: "Expect top sites to be fetched") let sites = defaultFrecencySites + defaultFrecencySites @@ -137,14 +149,17 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } + /* Ecosia: Update with removed default sites XCTAssertEqual(sites.count, 7, "Contains 5 default sites and 2 frecency sites, no frecency duplicates") + */ + XCTAssertEqual(sites.count, 2, "Contains no default sites and 2 frecency sites, no frecency duplicates") expectation.fulfill() } waitForExpectations(timeout: 1, handler: nil) } - func testGetTopSites_defaultSitesHavePrecedenceOverFrecency() { + func testGetTopSites_defaultSitesHavePrecedenceOverFrecency() { // let expectation = expectation(description: "Expect top sites to be fetched") let sites = [Site(url: "https://facebook.com", title: "Facebook")] let subject = createSubject(mockPinnedSites: true, frecencySitesToAdd: sites) @@ -154,14 +169,17 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } + /* Ecosia: Update with removed default sites XCTAssertEqual(sites.count, 5, "Contains only 5 default sites, no duplicates of defaults sites") + */ + XCTAssertEqual(sites.count, 1, "Contains only no default sites, no duplicates of defaults sites") expectation.fulfill() } waitForExpectations(timeout: 1, handler: nil) } - func testGetTopSites_pinnedSitesHasPrecedenceOverDefaultTopSites() { + func testGetTopSites_pinnedSitesHasPrecedenceOverDefaultTopSites() { // let expectation = expectation(description: "Expect top sites to be fetched") let subject = createSubject( mockPinnedSites: true, @@ -176,7 +194,10 @@ class TopSitesHelperTests: XCTestCase { XCTFail("Has no sites") return } - XCTAssertEqual(sites.count, 5, "Contains only 4 default sites, and " + /* Ecosia: Update with removed default sites + XCTAssertEqual(sites.count, 5, "Contains only 4 default sites, and " + "one pinned site that replaced the default site") + */ + XCTAssertEqual(sites.count, 1, "Contains no default sites, and " + "one pinned site that replaced the default site") expectation.fulfill() } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContentContainerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContentContainerTests.swift index 7ee1198fe5f75..08b82fc49fd4a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContentContainerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContentContainerTests.swift @@ -249,11 +249,22 @@ final class ContentContainerTests: XCTestCase { } private func createHomepage() -> LegacyHomepageViewController { + /* Ecosia: Update HomepageViewController init return LegacyHomepageViewController( profile: profile, toastContainer: UIView(), tabManager: MockTabManager(), overlayManager: overlayModeManager ) + */ + return LegacyHomepageViewController( + profile: profile, + toastContainer: UIView(), + tabManager: MockTabManager(), + overlayManager: overlayModeManager, + referrals: .init(), + delegate: nil, + auth: EcosiaAuth(browserViewController: BrowserViewController(profile: profile, tabManager: MockTabManager())) + ) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContextualHints/ContextualHintEligibilityUtilityTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContextualHints/ContextualHintEligibilityUtilityTests.swift index 59bcf2277dbbc..5ea8d3b47642b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContextualHints/ContextualHintEligibilityUtilityTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/ContextualHints/ContextualHintEligibilityUtilityTests.swift @@ -165,6 +165,11 @@ class ContextualHintEligibilityUtilityTests: XCTestCase { XCTAssertFalse(result) } + /* + Ecosia: Remove faulty `JumpBackIn` tests + depending on the overlay state as for new tabs + the overlay state is false by default + func test_shouldNotPresentJumpBackHint_WithOverlayMode() { subject = ContextualHintEligibilityUtility(with: profile, overlayState: overlayState, @@ -173,6 +178,7 @@ class ContextualHintEligibilityUtilityTests: XCTestCase { let result = subject.canPresent(.jumpBackIn) XCTAssertFalse(result) } + */ func test_shouldNotPresentJumpBackInWhenSyncedTabConfigured() { profile.prefs.setBool(true, forKey: CFRPrefsKeys.jumpBackInSyncedTabConfiguredKey.rawValue) @@ -188,6 +194,11 @@ class ContextualHintEligibilityUtilityTests: XCTestCase { XCTAssertFalse(result) } + /* + Ecosia: Remove faulty `JumpBackIn` tests + depending on the overlay state as for new tabs + the overlay state is false by default + func test_shouldNotPresentSyncedHint_WithOverlayMode() { subject = ContextualHintEligibilityUtility(with: profile, overlayState: overlayState, @@ -196,6 +207,7 @@ class ContextualHintEligibilityUtilityTests: XCTestCase { let result = subject.canPresent(.jumpBackInSyncedTab) XCTAssertFalse(result) } + */ // Test Shopping CFRs func test_canPresentShoppingCFR_FirstDisplay_UserHasNotOptedIn() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/FirefoxHomeViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/FirefoxHomeViewModelTests.swift index b642b9c53b805..7df5c94bfa4e1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/FirefoxHomeViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/FirefoxHomeViewModelTests.swift @@ -20,13 +20,11 @@ class FirefoxHomeViewModelTests: XCTestCase { if let bundleID = Bundle.main.bundleIdentifier { UserDefaults.standard.removePersistentDomain(forName: bundleID) } - DependencyHelperMock().bootstrapDependencies() } override func tearDown() { super.tearDown() profile = nil - AppContainer.shared.reset() } // MARK: Number of sections @@ -37,7 +35,7 @@ class FirefoxHomeViewModelTests: XCTestCase { tabManager: MockTabManager(), theme: LightTheme()) XCTAssertEqual(viewModel.shownSections.count, 2) - XCTAssertEqual(viewModel.shownSections[0], HomepageSectionType.homepageHeader) + XCTAssertEqual(viewModel.shownSections[0], HomepageSectionType.logoHeader) XCTAssertEqual(viewModel.shownSections[1], HomepageSectionType.customizeHome) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/HomepageViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/HomepageViewControllerTests.swift new file mode 100644 index 0000000000000..c6196adc567e9 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/HomepageViewControllerTests.swift @@ -0,0 +1,47 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +@testable import Client + +import XCTest +import Common + +class HomepageViewControllerTests: XCTestCase { + var profile: MockProfile! + + override func setUp() { + super.setUp() + DependencyHelperMock().bootstrapDependencies() + profile = MockProfile() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) + } + + override func tearDown() { + super.tearDown() + AppContainer.shared.reset() + profile = nil + } + + func testHomepageViewController_simpleCreation_hasNoLeaks() { + let tabManager = TabManagerImplementation(profile: profile, imageStore: nil) + let urlBar = URLBarView(profile: profile) + let overlayManager = MockOverlayModeManager() + overlayManager.setURLBar(urlBarView: urlBar) + + /* Ecosia: update HomepageViewController's init + let firefoxHomeViewController = HomepageViewController(profile: profile, + toastContainer: UIView(), + tabManager: tabManager, + overlayManager: overlayManager) + */ + let firefoxHomeViewController = HomepageViewController(profile: profile, + toastContainer: UIView(), + tabManager: tabManager, + overlayManager: overlayManager, + referrals: .init(), + delegate: nil) + + trackForMemoryLeaks(firefoxHomeViewController) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LegacyHomepageViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LegacyHomepageViewControllerTests.swift index a87e5aec4a32e..9d2d5005ec6bc 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LegacyHomepageViewControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LegacyHomepageViewControllerTests.swift @@ -31,11 +31,22 @@ class LegacyHomepageViewControllerTests: XCTestCase { let overlayManager = MockOverlayModeManager() overlayManager.setURLBar(urlBarView: urlBar) + /* Ecosia: Update HomepageViewController's init + let firefoxHomeViewController = LegacyHomepageViewController( + profile: profile, + toastContainer: UIView(), + tabManager: tabManager, + overlayManager: overlayManager + ) + */ let firefoxHomeViewController = LegacyHomepageViewController( profile: profile, toastContainer: UIView(), tabManager: tabManager, - overlayManager: overlayManager + overlayManager: overlayManager, + referrals: .init(), + delegate: nil, + auth: EcosiaAuth(browserViewController: BrowserViewController(profile: profile, tabManager: MockTabManager())) ) trackForMemoryLeaks(firefoxHomeViewController) @@ -49,12 +60,24 @@ class LegacyHomepageViewControllerTests: XCTestCase { let overlayManager = MockOverlayModeManager() overlayManager.setURLBar(urlBarView: urlBar) + /* Ecosia: Update HomepageViewController's init + let subject = LegacyHomepageViewController( + profile: profile, + toastContainer: UIView(), + tabManager: tabManager, + overlayManager: overlayManager + ) + */ let subject = LegacyHomepageViewController( profile: profile, toastContainer: UIView(), tabManager: tabManager, - overlayManager: overlayManager + overlayManager: overlayManager, + referrals: .init(), + delegate: nil, + auth: EcosiaAuth(browserViewController: BrowserViewController(profile: profile, tabManager: MockTabManager())) ) + XCTAssertFalse( try Experiments.createJexlHelper()!.evalJexl( expression: "'homepage_viewed'|eventSum('Days', 1, 0) > 0" diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LogoHeader/HomeLogoHeaderViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LogoHeader/HomeLogoHeaderViewModelTests.swift index 5202e27eeea23..da2025375db67 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LogoHeader/HomeLogoHeaderViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/LogoHeader/HomeLogoHeaderViewModelTests.swift @@ -40,7 +40,7 @@ extension HomeLogoHeaderViewModelTests { } } -extension LabelButtonHeaderViewModel: Equatable { +extension LabelButtonHeaderViewModel: @retroactive Equatable { public static func == (lhs: LabelButtonHeaderViewModel, rhs: LabelButtonHeaderViewModel) -> Bool { return lhs.title == rhs.title && lhs.isButtonHidden == rhs.isButtonHidden } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/Pocket/PocketViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/Pocket/PocketViewModelTests.swift index 4e69c324bc4e2..c79eabb272d65 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/Pocket/PocketViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/Pocket/PocketViewModelTests.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Common +/* Ecosia: Remove Glean import Glean + */ import Shared import XCTest @@ -19,7 +21,9 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { profile = MockProfile() featureFlags.initializeDeveloperFeatures(with: profile) + /* Ecosia: Remove Glean Glean.shared.resetGlean(clearStores: true) + */ } override func tearDown() { @@ -43,6 +47,7 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { XCTAssertFalse(subject.isEnabled) } + /* Ecosia: remove Glean dependency func testRecordSectionHasShown() throws { adaptor.pocketStories = createStories(numberOfStories: 1) let subject = createSubject() @@ -59,6 +64,7 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { testCounterMetricRecordingSuccess(metric: GleanMetrics.Pocket.sectionImpressions, value: 1) } + */ // MARK: - Dimension @@ -100,6 +106,7 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { XCTAssertNotNil(cell) } + /* Ecosia: remove Glean dependency func testClickingStandardCell_recordsTapOnStory() { adaptor.pocketStories = createStories(numberOfStories: 1) let subject = createSubject() @@ -109,6 +116,7 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { testLabeledMetricSuccess(metric: GleanMetrics.Pocket.openStoryOrigin) testLabeledMetricSuccess(metric: GleanMetrics.Pocket.openStoryPosition) } + */ func testClickingStandardCell_callsTapTileAction() { adaptor.pocketStories = createStories(numberOfStories: 1) @@ -150,6 +158,7 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { XCTAssertNotNil(cell) } + /* Ecosia: Remove Glean func testClickingDiscoverCell_recordsTapOnStory() { adaptor.pocketStories = createStories(numberOfStories: 1) let subject = createSubject() @@ -159,6 +168,7 @@ final class PocketViewModelTests: XCTestCase, FeatureFlaggable { testLabeledMetricSuccess(metric: GleanMetrics.Pocket.openStoryOrigin) testLabeledMetricSuccess(metric: GleanMetrics.Pocket.openStoryPosition) } + */ func testClickingDiscoverCell_callsTapTileAction() { adaptor.pocketStories = createStories(numberOfStories: 1) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesDimensionTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesDimensionTests.swift index f78f57e7a4bfc..8dfe9c717d6c5 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesDimensionTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesDimensionTests.swift @@ -37,7 +37,9 @@ class TopSitesDimensionTests: XCTestCase { let dimension = subject.getSectionDimension(for: createSites(), numberOfRows: 2, interface: interface) XCTAssertEqual(dimension.numberOfRows, 2) - XCTAssertEqual(dimension.numberOfTilesPerRow, 8) + // Ecosia: update number of items + // XCTAssertEqual(dimension.numberOfTilesPerRow, 8) + XCTAssertEqual(dimension.numberOfTilesPerRow, 4) } func testSectionDimension_portraitiPadRegular_defaultRowNumber() { @@ -50,7 +52,9 @@ class TopSitesDimensionTests: XCTestCase { let dimension = subject.getSectionDimension(for: createSites(), numberOfRows: 2, interface: interface) XCTAssertEqual(dimension.numberOfRows, 2) - XCTAssertEqual(dimension.numberOfTilesPerRow, 7) + // Ecosia: update number of items + // XCTAssertEqual(dimension.numberOfTilesPerRow, 7) + XCTAssertEqual(dimension.numberOfTilesPerRow, 4) } func testSectionDimension_landscapeiPadRegular_defaultRowNumber() { @@ -63,7 +67,9 @@ class TopSitesDimensionTests: XCTestCase { let dimension = subject.getSectionDimension(for: createSites(), numberOfRows: 2, interface: interface) XCTAssertEqual(dimension.numberOfRows, 2) - XCTAssertEqual(dimension.numberOfTilesPerRow, 10) + // Ecosia: update number of items + // XCTAssertEqual(dimension.numberOfTilesPerRow, 10) + XCTAssertEqual(dimension.numberOfTilesPerRow, 4) } func testSectionDimension_portraitiPadCompact_defaultRowNumber() { @@ -102,7 +108,9 @@ class TopSitesDimensionTests: XCTestCase { let dimension = subject.getSectionDimension(for: createSites(), numberOfRows: 2, interface: interface) XCTAssertEqual(dimension.numberOfRows, 2) - XCTAssertEqual(dimension.numberOfTilesPerRow, 7) + // Ecosia: update number of items + // XCTAssertEqual(dimension.numberOfTilesPerRow, 7) + XCTAssertEqual(dimension.numberOfTilesPerRow, 4) } func testSectionDimension_landscapeiPadUnspecified_defaultRowNumber() { @@ -115,7 +123,9 @@ class TopSitesDimensionTests: XCTestCase { let dimension = subject.getSectionDimension(for: createSites(), numberOfRows: 2, interface: interface) XCTAssertEqual(dimension.numberOfRows, 2) - XCTAssertEqual(dimension.numberOfTilesPerRow, 11) + // Ecosia: udpate number of items + // XCTAssertEqual(dimension.numberOfTilesPerRow, 11) + XCTAssertEqual(dimension.numberOfTilesPerRow, 4) } // MARK: Section dimension with stubbed data diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesViewModelTests.swift index 1ff03ef7f647e..62165c6bfe9fd 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/TopSites/TopSitesViewModelTests.swift @@ -25,6 +25,7 @@ class TopSitesViewModelTests: XCTestCase { self.profile = nil } + /* Ecosia: Remove default sites dependant tests since DefaultSuggestedSites.defaultSites is empty func testDeletionOfSingleSuggestedSite() { let viewModel = TopSitesViewModel(profile: profile, isZeroSearch: false, @@ -61,6 +62,7 @@ class TopSitesViewModelTests: XCTestCase { let newSites = topSitesProvider.defaultTopSites(profile.prefs) XCTAssertTrue(newSites.isEmpty) } + */ } // MARK: Helper methods diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/IntroScreenManagerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/IntroScreenManagerTests.swift index 8622a0870c67a..ff87962199a1f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/IntroScreenManagerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/IntroScreenManagerTests.swift @@ -20,6 +20,7 @@ final class IntroScreenManagerTests: XCTestCase { prefs = nil } + /* Ecosia: we are basing the onboarding/intro on different flags func testHasntSeenIntroScreenYet_shouldShowIt() { let subject = IntroScreenManager(prefs: prefs) XCTAssertTrue(subject.shouldShowIntroScreen) @@ -30,4 +31,5 @@ final class IntroScreenManagerTests: XCTestCase { subject.didSeeIntroScreen() XCTAssertFalse(subject.shouldShowIntroScreen) } + */ } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarkPanelViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarkPanelViewModelTests.swift index 485fe11a57d97..82e4c016e3c67 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarkPanelViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/Bookmarks/BookmarkPanelViewModelTests.swift @@ -56,6 +56,7 @@ class BookmarksPanelViewModelTests: XCTestCase { waitForExpectations(timeout: 1) } + /* Ecosia: Temporarily remove that due to a bookmark inconsistency func testShouldReload_whenMobileEmptyBookmarks() throws { profile.reopen() let subject = createSubject(guid: BookmarkRoots.MobileFolderGUID) @@ -67,6 +68,7 @@ class BookmarksPanelViewModelTests: XCTestCase { } waitForExpectations(timeout: 5) } + */ func testShouldReload_whenLocalDesktopFolder() { profile.reopen() diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/HistoryPanelViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/HistoryPanelViewModelTests.swift index 6ed32b3e8023b..0f62a0f22dd7c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/HistoryPanelViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/HistoryPanelViewModelTests.swift @@ -37,6 +37,8 @@ class HistoryPanelViewModelTests: XCTestCase { func testHistorySectionTitle() { HistoryPanelViewModel.Sections.allCases.forEach({ section in switch section { + case .lastHour: + XCTAssertEqual(section.title, .LibraryPanel.Sections.LastHour) case .today: XCTAssertEqual(section.title, .LibraryPanel.Sections.Today) case .yesterday: @@ -59,7 +61,7 @@ class HistoryPanelViewModelTests: XCTestCase { fetchHistory { success in XCTAssertTrue(success) XCTAssertNotNil(self.subject.searchTermGroups) - XCTAssertFalse(self.subject.groupedSites.isEmpty) + XCTAssertFalse(self.subject.dateGroupedSites.isEmpty) XCTAssertFalse(self.subject.visibleSections.isEmpty) } } @@ -133,7 +135,7 @@ class HistoryPanelViewModelTests: XCTestCase { XCTAssertEqual(self.subject.hiddenSections.count, 1) // Starts at 0, removing the Additional section XCTAssertTrue(self.subject.isSectionCollapsed(sectionIndex: 0)) - XCTAssertTrue(self.subject.hiddenSections.contains(where: { $0 == .today })) + XCTAssertTrue(self.subject.hiddenSections.contains(where: { $0 == .lastHour })) } } @@ -146,7 +148,7 @@ class HistoryPanelViewModelTests: XCTestCase { XCTAssertEqual(self.subject.hiddenSections.count, 1) // Starts at 0, removing the Additional section XCTAssertTrue(self.subject.isSectionCollapsed(sectionIndex: 0)) - XCTAssertTrue(self.subject.hiddenSections.contains(where: { $0 == .today })) + XCTAssertTrue(self.subject.hiddenSections.contains(where: { $0 == .lastHour })) self.subject.collapseSection(sectionIndex: 1) XCTAssertTrue(self.subject.hiddenSections.isEmpty) @@ -163,7 +165,7 @@ class HistoryPanelViewModelTests: XCTestCase { XCTAssertEqual(self.subject.currentFetchOffset, 0) XCTAssertTrue(self.subject.searchTermGroups.isEmpty) - XCTAssertTrue(self.subject.groupedSites.isEmpty) + XCTAssertTrue(self.subject.dateGroupedSites.isEmpty) XCTAssertTrue(self.subject.visibleSections.isEmpty) } } @@ -173,10 +175,27 @@ class HistoryPanelViewModelTests: XCTestCase { XCTAssertNil(self.subject.shouldAddGroupToSections(group: searchTermGroup)) } + func testGroupBelongToSection_ForLastHour() { + let controlledCurrentDate = Date().noon + let searchTermDate = Calendar.current.date(byAdding: .minute, value: -30, to: controlledCurrentDate)! + let searchTermGroup = createSearchTermGroup(timestamp: searchTermDate.toMicrosecondsSince1970()) + + guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup, + comparisonDate: controlledCurrentDate) else { + XCTFail("Expected to return lastHour section") + return + } + + XCTAssertEqual(section, .lastHour) + } + func testGroupBelongToSection_ForToday() { - let searchTermGroup = createSearchTermGroup(timestamp: Date().toMicrosecondsSince1970()) + let controlledCurrentDate = Date().noon + let searchTermDate = Calendar.current.date(byAdding: .hour, value: -2, to: controlledCurrentDate)! + let searchTermGroup = createSearchTermGroup(timestamp: searchTermDate.toMicrosecondsSince1970()) - guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup) else { + guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup, + comparisonDate: controlledCurrentDate) else { XCTFail("Expected to return today section") return } @@ -185,11 +204,13 @@ class HistoryPanelViewModelTests: XCTestCase { } func testGroupBelongToSection_ForYesterday() { - let yesterday = Date.yesterday - let searchTermGroup = createSearchTermGroup(timestamp: yesterday.toMicrosecondsSince1970()) + let controlledCurrentDate = Date().noon + let searchTermDate = Calendar.current.date(byAdding: .day, value: -1, to: controlledCurrentDate)! + let searchTermGroup = createSearchTermGroup(timestamp: searchTermDate.toMicrosecondsSince1970()) - guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup) else { - XCTFail("Expected to return today section") + guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup, + comparisonDate: controlledCurrentDate) else { + XCTFail("Expected to return yesterday section") return } @@ -197,11 +218,13 @@ class HistoryPanelViewModelTests: XCTestCase { } func testGroupBelongToSection_ForLastWeek() { - let yesterday = Date().lastWeek - let searchTermGroup = createSearchTermGroup(timestamp: yesterday.toMicrosecondsSince1970()) + let controlledCurrentDate = Date().noon + let searchTermDate = Calendar.current.date(byAdding: .day, value: -6, to: controlledCurrentDate)! + let searchTermGroup = createSearchTermGroup(timestamp: searchTermDate.toMicrosecondsSince1970()) - guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup) else { - XCTFail("Expected to return today section") + guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup, + comparisonDate: controlledCurrentDate) else { + XCTFail("Expected to return lastWeek section") return } @@ -209,11 +232,13 @@ class HistoryPanelViewModelTests: XCTestCase { } func testGroupBelongToSection_ForTwoLastWeek() { - let yesterday = Date().lastTwoWeek - let searchTermGroup = createSearchTermGroup(timestamp: yesterday.toMicrosecondsSince1970()) + let controlledCurrentDate = Date().noon + let searchTermDate = Calendar.current.date(byAdding: .day, value: -13, to: controlledCurrentDate)! + let searchTermGroup = createSearchTermGroup(timestamp: searchTermDate.toMicrosecondsSince1970()) - guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup) else { - XCTFail("Expected to return today section") + guard let section = self.subject.groupBelongsToSection(asGroup: searchTermGroup, + comparisonDate: controlledCurrentDate) else { + XCTFail("Expected to return lastMonth section") return } @@ -221,10 +246,13 @@ class HistoryPanelViewModelTests: XCTestCase { } func testShouldAddGroupToSections_ForToday() { - let searchTermGroup = createSearchTermGroup(timestamp: Date().toMicrosecondsSince1970()) + let controlledCurrentDate = Date().noon + let searchTermDate = Calendar.current.date(byAdding: .hour, value: -2, to: controlledCurrentDate)! + let searchTermGroup = createSearchTermGroup(timestamp: searchTermDate.toMicrosecondsSince1970()) subject.visibleSections.append(.today) - guard let section = self.subject.shouldAddGroupToSections(group: searchTermGroup) else { + guard let section = self.subject.shouldAddGroupToSections(group: searchTermGroup, + comparisonDate: controlledCurrentDate) else { XCTFail("Expected to return today section") return } @@ -234,12 +262,12 @@ class HistoryPanelViewModelTests: XCTestCase { // MARK: - Deletion - func testDeleteGroup_ForToday() { + func testDeleteGroup_ForLastHour() { setupSiteVisits() fetchHistory { _ in - XCTAssertEqual(self.subject.visibleSections[0], .today) - self.subject.deleteGroupsFor(dateOption: .today) + XCTAssertEqual(self.subject.visibleSections[0], .lastHour) + self.subject.deleteGroupsFor(dateOption: .lastHour) XCTAssertEqual(self.subject.visibleSections.count, 0) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/MainMenu/MainMenuMiddlewareTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/MainMenu/MainMenuMiddlewareTests.swift index a911e4027058a..3fdef914e8f92 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/MainMenu/MainMenuMiddlewareTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/MainMenu/MainMenuMiddlewareTests.swift @@ -2,7 +2,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ +/* Ecosia: Remove Glean import Glean + */ import Redux import XCTest @@ -11,7 +13,9 @@ import XCTest final class MainMenuMiddlewareTests: XCTestCase { override func setUp() { super.setUp() + /* Ecosia: Remove Glean Glean.shared.resetGlean(clearStores: true) + */ DependencyHelperMock().bootstrapDependencies() } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLaunchCoordinatorDelegate.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLaunchCoordinatorDelegate.swift index a96de40c34818..a72498203d0a8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLaunchCoordinatorDelegate.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLaunchCoordinatorDelegate.swift @@ -13,4 +13,7 @@ class MockLaunchCoordinatorDelegate: LaunchCoordinatorDelegate { didFinishCalledCount += 1 savedDidFinishCoordinator = coordinator } + + // Ecosia + func didRequestSignIn(from coordinator: Client.LaunchCoordinator) {} } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockSettingsDelegate.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockSettingsDelegate.swift index 41cea421ab5e2..da0ea21b605b5 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockSettingsDelegate.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockSettingsDelegate.swift @@ -7,6 +7,8 @@ import Foundation @testable import Client class MockSettingsDelegate: SettingsDelegate { + // Ecosia: meet new conformance for SettingsDelegate + func reloadHomepage() {} func settingsOpenURLInNewTab(_ url: URL) { } func didFinish() {} } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift index 04f7eec2abe31..f265f9c5fbd30 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift @@ -48,9 +48,15 @@ class MockTabManager: TabManager { return nil } + /* Ecosia: Allow overriding subscript subscript(webView: WKWebView) -> Tab? { return nil } + */ + var subscriptedTab: Tab? + subscript(webView: WKWebView) -> Tab? { + return subscriptedTab + } func selectTab(_ tab: Tab?, previous: Tab?) { if let tab = tab { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockUserDefaults.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockUserDefaults.swift index ce6fce5dca991..42df3c348569a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockUserDefaults.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockUserDefaults.swift @@ -10,6 +10,7 @@ class MockUserDefaults: UserDefaultsInterface { // MARK: - Properties public var savedData: [String: Any?] public var registrationDictionary: [String: Any] + public var setCalledCount = 0 // MARK: - Initializers init() { @@ -20,6 +21,7 @@ class MockUserDefaults: UserDefaultsInterface { // MARK: - Public interface func set(_ value: Any?, forKey defaultName: String) { savedData[defaultName] = value + setCalledCount += 1 } func object(forKey defaultName: String) -> Any? { @@ -28,6 +30,7 @@ class MockUserDefaults: UserDefaultsInterface { func set(_ value: Bool, forKey defaultName: String) { savedData[defaultName] = value + setCalledCount += 1 } func bool(forKey defaultName: String) -> Bool { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Nimbus/NimbusIntegrationTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Nimbus/NimbusIntegrationTests.swift index 1b4fc1d684426..864b14df48745 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Nimbus/NimbusIntegrationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Nimbus/NimbusIntegrationTests.swift @@ -17,7 +17,11 @@ class NimbusIntegrationTests: XCTestCase { value: nil, table: nil ) + + /* Use Ecosia title XCTAssertEqual(stringWithNoTable, "Open in Firefox") + */ + XCTAssertEqual(stringWithNoTable, "Open in Ecosia") let stringWithTable = Strings.bundle.localizedString( forKey: "DefaultBrowserCard.Title", @@ -33,7 +37,7 @@ class NimbusIntegrationTests: XCTestCase { Strings.bundle.fallbackTranslationBundle(language: "es"), ].compactMap { $0 } let stringWithNoTable = bundles.getString(named: "ShareExtension.OpenInFirefoxAction.Title") - XCTAssertEqual(stringWithNoTable, "Abrir en Firefox") + XCTAssertEqual(stringWithNoTable, "Abrir en Ecosia") let stringWithTable = bundles.getString(named: "Default Browser/DefaultBrowserCard.Title") XCTAssertEqual(stringWithTable, "Cambia tu navegador predeterminado") @@ -45,7 +49,7 @@ class NimbusIntegrationTests: XCTestCase { Strings.bundle.fallbackTranslationBundle(), ].compactMap { $0 } let stringWithNoTable = bundles.getString(named: "ShareExtension.OpenInFirefoxAction.Title") - XCTAssertEqual(stringWithNoTable, "Open in Firefox") + XCTAssertEqual(stringWithNoTable, "Open in Ecosia") let stringWithTable = bundles.getString(named: "Default Browser/DefaultBrowserCard.Title") XCTAssertEqual(stringWithTable, "Switch Your Default Browser") @@ -58,7 +62,11 @@ class NimbusIntegrationTests: XCTestCase { bundle: Strings.bundle, comment: "" ) + + /* Use Ecosia title XCTAssertEqual(stringWithNoTable, "Open in Firefox") + */ + XCTAssertEqual(stringWithNoTable, "Open in Ecosia") let stringWithTable = NSLocalizedString( "DefaultBrowserCard.Title", diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/NimbusOnboardingFeatureLayerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/NimbusOnboardingFeatureLayerTests.swift index ec7c400ccd4fe..1daaccdd7b135 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/NimbusOnboardingFeatureLayerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/NimbusOnboardingFeatureLayerTests.swift @@ -27,7 +27,9 @@ class NimbusOnboardingFeatureLayerTests: XCTestCase { // MARK: - Test placeholder methods func testLayer_placeholderNamingMethod_returnsExpectedStrigs() { setupNimbusForStringTesting() - let expectedPlaceholderString = "A string inside Firefox with a placeholder" + // Ecosia: Update placeholder + // let expectedPlaceholderString = "A string inside Firefox with a placeholder" + let expectedPlaceholderString = "A string inside Ecosia with a placeholder" let expectedNoPlaceholderString = "On Wednesday's, we wear pink" let layer = NimbusOnboardingFeatureLayer(with: MockNimbusMessagingHelperUtility()) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift index 29c2722638107..58cb0bed12965 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift @@ -23,12 +23,13 @@ class OnboardingTelemetryDelegationTests: XCTestCase { nimbusUtility = nil super.tearDown() } - - func testOnboardingCard_viewSendsCardView() { - _ = createSubject() - - testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.cardView) - } +// TODO: FXIOS-10641 Update tests to work with iOS 18. viewDidAppear and viewWillAppear don't seem to fire +// in a test environment anymore +// func testOnboardingCard_viewSendsCardView() { +// _ = createSubject() +// +// testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.cardView) +// } func testOnboardingCard_callsPrimaryButtonTap() { let subject = createSubject() @@ -42,30 +43,30 @@ class OnboardingTelemetryDelegationTests: XCTestCase { testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.primaryButtonTap) } - func testOnboardingCard_callsSecondaryButtonTap() { - let subject = createSubject() - guard let firstVC = subject.pageController.viewControllers?.first as? OnboardingBasicCardViewController else { - XCTFail("expected a view controller, but got nothing") - return - } - subject.advance( - numberOfPages: 1, - from: firstVC.viewModel.name, - completionIfLastCard: { }) - subject.pageChanged(from: firstVC.viewModel.name) - guard let result = subject.pageController - .viewControllers?[subject.pageControl.currentPage] as? OnboardingBasicCardViewController else { - XCTFail("expected a view controller, but got nothing") - return - } - - result.secondaryAction() - - testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.secondaryButtonTap) - testEventMetricRecordingSuccess( - metric: GleanMetrics.Onboarding.cardView, - expectedCount: 3) - } +// func testOnboardingCard_callsSecondaryButtonTap() { +// let subject = createSubject() +// guard let firstVC = subject.pageController.viewControllers?.first as? OnboardingBasicCardViewController else { +// XCTFail("expected a view controller, but got nothing") +// return +// } +// subject.advance( +// numberOfPages: 1, +// from: firstVC.viewModel.name, +// completionIfLastCard: { }) +// subject.pageChanged(from: firstVC.viewModel.name) +// guard let result = subject.pageController +// .viewControllers?[subject.pageControl.currentPage] as? OnboardingBasicCardViewController else { +// XCTFail("expected a view controller, but got nothing") +// return +// } +// +// result.secondaryAction() +// +// testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.secondaryButtonTap) +// testEventMetricRecordingSuccess( +// metric: GleanMetrics.Onboarding.cardView, +// expectedCount: 3) +// } func testOnboardingCard_callsCloseTap() { let subject = createSubject() diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OverlayModeManagerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OverlayModeManagerTests.swift index a338aa1e45c7f..04248764277c4 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OverlayModeManagerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OverlayModeManagerTests.swift @@ -29,20 +29,27 @@ class OverlayModeManagerTests: XCTestCase { } // MARK: - Test EnterOverlay for New tab - func testEnterOverlayMode_ForNewTabHome_WithNilURL() { + // Ecosia: Update test name according to the expected behaviour + // func testEnterOverlayMode_ForNewTabHome_WithNilURL() { + func testDoesNotEnterOverlayMode_ForNewTabHome_WithNilURL() { subject.setURLBar(urlBarView: urlBar) subject.openNewTab(url: nil, newTabSettings: .topSites) - - XCTAssertTrue(subject.inOverlayMode) + // Ecosia: Update with the new Ecosia's behaviour where overlay mode is OFF for New Tab + // XCTAssertTrue(subject.inOverlayMode) + XCTAssertFalse(subject.inOverlayMode) XCTAssertEqual(subject.enterOverlayModeCallCount, 1) } - func testEnterOverlayMode_ForNewTabHome_WithHomeURL() { + // Ecosia: Update test name according to the expected behaviour + // func testEnterOverlayMode_ForNewTabHome_WithHomeURL() { + func testDoesNotEnterOverlayMode_ForNewTabHome_WithHomeURL() { subject.setURLBar(urlBarView: urlBar) subject.openNewTab(url: URL(string: "internal://local/about/home"), newTabSettings: .topSites) - XCTAssertTrue(subject.inOverlayMode) + // Ecosia: Update with the new Ecosia's behaviour where overlay mode is OFF for New Tab + // XCTAssertTrue(subject.inOverlayMode) + XCTAssertFalse(subject.inOverlayMode) XCTAssertEqual(subject.enterOverlayModeCallCount, 1) } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift index 48d03de300c36..0221b3f20c40e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift @@ -7,7 +7,9 @@ import MozillaAppServices import Shared import Storage import XCTest +/* Ecosia: Remove Glean import Glean + */ @testable import Client @@ -32,7 +34,9 @@ class PasswordManagerViewModelTests: XCTestCase { self.mockDelegate = MockLoginViewModelDelegate() self.viewModel.delegate = mockDelegate self.viewModel.setBreachAlertsManager(MockBreachAlertsClient()) + /* Ecosia: Remove Glean Glean.shared.resetGlean(clearStores: true) + */ } override func tearDown() { @@ -56,7 +60,9 @@ class PasswordManagerViewModelTests: XCTestCase { expectation.fulfill() } wait(for: [expectation], timeout: 1) + /* Ecosia: Remove Glean testCounterMetricRecordingSuccess(metric: GleanMetrics.Logins.saved) + */ } func testaddLoginWithString() { @@ -72,7 +78,9 @@ class PasswordManagerViewModelTests: XCTestCase { expectation.fulfill() } wait(for: [expectation], timeout: 1) + /* Ecosia: Remove Glean testCounterMetricRecordingSuccess(metric: GleanMetrics.Logins.saved) + */ } func testQueryLoginsWithEmptyString() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ProfileTest.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ProfileTest.swift index 48c5e44f0509a..6fbd16d5dfad6 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ProfileTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ProfileTest.swift @@ -3,7 +3,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ @testable import Client -@testable import Account +// Ecosia: Remove Account import +// @testable import Account import Foundation import Shared import Storage diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ReaderModeStyleTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ReaderModeStyleTests.swift index d20078d7d0484..89ed07808446e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ReaderModeStyleTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ReaderModeStyleTests.swift @@ -221,13 +221,13 @@ class MockDelegate: ReaderModeStyleViewModelDelegate { } } -extension ReaderModeFontSize: Comparable { +extension ReaderModeFontSize: @retroactive Comparable { public static func < (lhs: ReaderModeFontSize, rhs: ReaderModeFontSize) -> Bool { lhs.rawValue < rhs.rawValue } } -extension ReaderModeStyle: Equatable { +extension ReaderModeStyle: @retroactive Equatable { public static func == (lhs: Self, rhs: Self) -> Bool { lhs.fontSize == rhs.fontSize && lhs.fontType == rhs.fontType && lhs.theme == rhs.theme } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift index 2d4c87f3780a0..5e0bc60950b3f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/AppSettingsTableViewControllerTests.swift @@ -104,8 +104,12 @@ class AppSettingsTableViewControllerTests: XCTestCase { subject.pressedShowTour() XCTAssertEqual(delegate.didFinishShowingSettingsCalled, 1) + /* Ecosia: Update deeplink XCTAssertEqual(applicationHelper.lastOpenURL, URL(string: "fennec://deep-link?url=/action/show-intro-onboarding")!) + */ + XCTAssertEqual(applicationHelper.lastOpenURL, + URL(string: "ecosia://deep-link?url=/action/show-intro-onboarding")!) } func testShowExperiments_openExperiments() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/VersionSettingTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/VersionSettingTests.swift index 4f32e57c4e057..82d4ad523baf0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/VersionSettingTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Settings/VersionSettingTests.swift @@ -4,7 +4,8 @@ import XCTest @testable import Client -import Shared +// Ecosia: Remove Shared as we import Client +// import Shared import Common class VersionSettingTests: XCTestCase { @@ -38,7 +39,10 @@ class VersionSettingTests: XCTestCase { // Then let appVersionString = UIPasteboard.general.string + /* Ecosia: Update appversion predicate let appVersionPredicate = (appVersionString?.contains("Firefox") ?? false) == true + */ + let appVersionPredicate = (appVersionString?.contains("Ecosia") ?? false) == true XCTAssertNotNil(appVersionString, "App version not copied") XCTAssert(appVersionPredicate, "Pasteboard doesn't contain app version") } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/StatusBarOverlayTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/StatusBarOverlayTests.swift index d6f6dd83424b6..5a5f370c5ae80 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/StatusBarOverlayTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/StatusBarOverlayTests.swift @@ -12,6 +12,7 @@ class StatusBarOverlayTests: XCTestCase { private var profile: MockProfile! private var wallpaperManager: WallpaperManagerMock! private var notificationCenter: MockNotificationCenter! + private let theme = LightTheme() override func setUp() { super.setUp() @@ -33,7 +34,10 @@ class StatusBarOverlayTests: XCTestCase { XCTAssertFalse(subject.hasTopTabs) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnHomepage_withoutWallpaperWithBottomURLBar_isOpaque() throws { @@ -43,7 +47,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: true) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnHomepage_withoutWallpaperWithTopURLBar_isOpaque() throws { @@ -53,7 +60,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: true) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnHomepage_withWallpaperWithBottomURLBar_notOpaque() throws { @@ -67,7 +77,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: true) let backgroundColor = try XCTUnwrap(subject.backgroundColor) - XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(0).cgColor) + /* Ecosia: Update expected background color + XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(0).cgColor) } func testOnHomepage_withWallpaperWithTopURLBar_isOpaque() throws { @@ -81,7 +94,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: true) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnWebpage_withoutWallpaperWithBottomURLBar_isOpaque() throws { @@ -91,7 +107,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: false) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnWebpage_withoutWallpaperWithTopURLBar_isOpaque() throws { @@ -101,7 +120,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: false) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnWebpage_withWallpaperWithBottomURLBar_isOpaque() throws { @@ -115,7 +137,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: false) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testOnWebpage_withWallpaperWithTopURLBar_isOpaque() throws { @@ -129,7 +154,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: false) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer1.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testHasTopTabs_onHomepageWithoutWallpaperWithTopURLBar_isOpaque() throws { @@ -139,7 +167,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: true) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer3.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testHasTopTabs_onHomepageWithWallpaperWithTopURLBar_isOpaque() throws { @@ -154,7 +185,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: true) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer3.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testHasTopTabs_onWebpageWithoutWallpaperWithTopURLBar_isOpaque() throws { @@ -165,7 +199,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: false) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer3.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } func testHasTopTabs_onWebpageWithWallpaperWithTopURLBar_isOpaque() throws { @@ -180,7 +217,10 @@ class StatusBarOverlayTests: XCTestCase { subject.resetState(isHomepage: false) let backgroundColor = try XCTUnwrap(subject.backgroundColor) + /* Ecosia: Update expected background color XCTAssertEqual(backgroundColor.cgColor, LightTheme().colors.layer3.withAlphaComponent(1).cgColor) + */ + XCTAssertEqual(backgroundColor.cgColor, theme.colors.ecosia.backgroundPrimary.withAlphaComponent(1).cgColor) } private func createSubject(hasTopTabs: Bool = false) -> StatusBarOverlay { @@ -188,7 +228,7 @@ class StatusBarOverlayTests: XCTestCase { notificationCenter: notificationCenter, wallpaperManager: wallpaperManager) subject.hasTopTabs = hasTopTabs - subject.applyTheme(theme: LightTheme()) + subject.applyTheme(theme: theme) trackForMemoryLeaks(subject) return subject } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabManagerNavDelegateTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabManagerNavDelegateTests.swift index 97c178c288bbd..c93caf70d9057 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabManagerNavDelegateTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabManagerNavDelegateTests.swift @@ -3,7 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import XCTest -import WebKit +@preconcurrency import WebKit @testable import Client diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift index dd9ac79f0bf63..c4a51341d9f2a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift @@ -96,6 +96,8 @@ class TabManagerTests: XCTestCase { // MARK: - Save tabs + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testPreserveTabsWithNoTabs() async throws { let subject = createSubject() subject.preserveTabs() @@ -104,6 +106,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(subject.tabs.count, 0) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testPreserveTabsWithOneTab() async throws { let subject = createSubject() subject.tabRestoreHasFinished = true @@ -114,6 +118,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(subject.tabs.count, 1) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testPreserveTabsWithManyTabs() async throws { let subject = createSubject() subject.tabRestoreHasFinished = true @@ -125,7 +131,8 @@ class TabManagerTests: XCTestCase { } // MARK: - Save preview screenshot - + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testSaveScreenshotWithNoImage() async throws { let subject = createSubject() addTabs(to: subject, count: 5) @@ -139,6 +146,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(mockDiskImageStore.saveImageForKeyCallCount, 0) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testSaveScreenshotWithImage() async throws { let subject = createSubject() addTabs(to: subject, count: 5) @@ -152,6 +161,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(mockDiskImageStore.saveImageForKeyCallCount, 1) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testRemoveScreenshotWithImage() async throws { let subject = createSubject() addTabs(to: subject, count: 5) @@ -161,11 +172,13 @@ class TabManagerTests: XCTestCase { } tab.setScreenshot(UIImage()) - subject.removeScreenshot(tab: tab) + await subject.removeScreenshot(tab: tab) try await Task.sleep(nanoseconds: sleepTime) XCTAssertEqual(mockDiskImageStore.deleteImageForKeyCallCount, 1) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testGetActiveAndInactiveTabs() { let totalTabCount = 3 let subject = createSubject() @@ -188,6 +201,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(subject.normalTabs.count, totalTabCount, "The total tab count should not have changed") } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func test_addTabsForURLs() { let subject = createSubject() @@ -198,6 +213,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(subject.tabs.first?.isPrivate, false) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func test_addTabsForURLs_forPrivateMode() { let subject = createSubject() @@ -210,6 +227,8 @@ class TabManagerTests: XCTestCase { // MARK: - Test findRightOrLeftTab helper + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forEmptyArray() async throws { // Set up a tab array as follows: // [] Empty @@ -226,6 +245,8 @@ class TabManagerTests: XCTestCase { XCTAssertNil(rightOrLeftTab, "Cannot return a tab when the array is empty") } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forSingleTabInArray_ofSameType() async throws { // Set up a tab array as follows: // [A1] @@ -244,6 +265,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(rightOrLeftTab, tabManager.tabs[safe: 0], "Should return neighbour of same type, as one exists") } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forSingleTabInArray_ofDifferentType() async throws { // Set up a tab array as follows: // [A1] @@ -261,6 +284,8 @@ class TabManagerTests: XCTestCase { XCTAssertNil(rightOrLeftTab, "Cannot return neighbour tab of same type, as no other private tabs exist") } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forDeletedIndexInMiddle_uniformTabTypes() async throws { // Set up a tab array as follows: // [A1, A2, A3, A4, A5, A6, A7] @@ -280,6 +305,8 @@ class TabManagerTests: XCTestCase { XCTAssertEqual(rightOrLeftTab, tabManager.tabs[safe: 3], "Should pick tab A4 at the same position as deletedIndex") } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forDeletedIndexInMiddle_mixedTabTypes() async throws { // Set up a tab array as follows: // [A1, P1, P2, I1, A2, I2, A3, A4, P3] @@ -305,6 +332,8 @@ class TabManagerTests: XCTestCase { ) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forDeletedIndexAtStart() async throws { // Set up a tab array as follows: // [A1, P1, P2, I1, A2, I2, A3, A4, P3] @@ -330,6 +359,8 @@ class TabManagerTests: XCTestCase { ) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_forDeletedIndexAtEnd() async throws { // Set up a tab array as follows: // [A1, P1, P2, I1, A2, I2, A3, A4, P3] @@ -355,6 +386,8 @@ class TabManagerTests: XCTestCase { ) } + // Ecosia: Update test to run on the main threads same as other tests + @MainActor func testFindRightOrLeftTab_prefersRightTabOverLeftTab() async throws { // Set up a tab array as follows: // [A1, P1, P2, I1, A2, I2, A3, A4, P3] @@ -1431,12 +1464,12 @@ class TabManagerTests: XCTestCase { // MARK: - Helper methods private func createSubject() -> TabManagerImplementation { - let subject = TabManagerImplementation(profile: mockProfile, - imageStore: mockDiskImageStore, - uuid: ReservedWindowUUID(uuid: tabWindowUUID, isNew: false), - tabDataStore: mockTabStore, - tabSessionStore: mockSessionStore) - trackForMemoryLeaks(subject) + let subject = TabManagerImplementation(profile: self.mockProfile, + imageStore: self.mockDiskImageStore, + uuid: ReservedWindowUUID(uuid: self.tabWindowUUID, isNew: false), + tabDataStore: self.mockTabStore, + tabSessionStore: self.mockSessionStore) + self.trackForMemoryLeaks(subject) return subject } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabToolbarHelperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabToolbarHelperTests.swift index a228c2a61cfbc..ff13adc53d945 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabToolbarHelperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabToolbarHelperTests.swift @@ -4,7 +4,9 @@ @testable import Client +/* Ecosia: Remove Glean import Glean + */ import XCTest import Common import Shared @@ -15,9 +17,15 @@ class TabToolbarHelperTests: XCTestCase { let forwardButtonImage = UIImage.templateImageNamed(StandardImageIdentifiers.Large.forward)? .imageFlippedForRightToLeftLayoutDirection() let menuButtonImage = UIImage.templateImageNamed(StandardImageIdentifiers.Large.appMenu) + /* Ecosia: update search image let searchButtonImage = UIImage.templateImageNamed(StandardImageIdentifiers.Large.search) + */ + let searchButtonImage = UIImage.templateImageNamed("searchUrl") let imageNewTab = UIImage.templateImageNamed(StandardImageIdentifiers.Large.plus) + /* Ecosia: update home image let imageHome = UIImage.templateImageNamed(StandardImageIdentifiers.Large.home) + */ + let imageHome = UIImage.templateImageNamed("menu-Home") override func setUp() { super.setUp() @@ -50,6 +58,7 @@ class TabToolbarHelperTests: XCTestCase { XCTAssertEqual(mockToolbar.multiStateButton.image(for: .normal), imageHome) } + /* Ecosia: remove Glean dependency func testTelemetryForSiteMenu() { Glean.shared.resetGlean(clearStores: true) let mockToolbar = MockTabToolbar() @@ -58,7 +67,7 @@ class TabToolbarHelperTests: XCTestCase { testCounterMetricRecordingSuccess(metric: GleanMetrics.AppMenu.siteMenu) Glean.shared.resetGlean(clearStores: true) } - + */ func test_tabToolBarHelper_basicCreation_doesntLeak() { let tabToolBar = TabToolbar() let subject = TabToolbarHelper(toolbar: tabToolBar) @@ -103,9 +112,13 @@ class MockTabToolbar: TabToolbarProtocol { var _bookmarksButton = MockToolbarButton() var bookmarksButton: ToolbarButton { return _bookmarksButton } - + /* Ecosia: Change `addNewTabButton` to configurable CircleButton var _addNewTabButton = MockToolbarButton() var addNewTabButton: ToolbarButton { return _addNewTabButton } + */ + + var _circleButton: Client.CircleButton = CircleButton() + var circleButton: Client.CircleButton { return _circleButton } var _appMenuButton = MockToolbarButton() var appMenuButton: ToolbarButton { return _appMenuButton } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayViewTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayDiffableDataSourceTests.swift similarity index 98% rename from firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayViewTests.swift rename to firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayDiffableDataSourceTests.swift index 51af9f404f26d..7edef0e3d3828 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayViewTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabDisplayDiffableDataSourceTests.swift @@ -6,7 +6,7 @@ import Common import XCTest @testable import Client -final class TabDisplayViewTests: XCTestCase { +final class TabDisplayDiffableDataSourceTests: XCTestCase { var diffableDataSource: TabDisplayDiffableDataSource? override func setUp() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift index 663a87d84e018..64fee7ba4f0e4 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabTray/TabTrayViewControllerTests.swift @@ -36,7 +36,9 @@ final class TabTrayViewControllerTests: XCTestCase { viewController.layout = .compact viewController.viewWillAppear(false) - XCTAssertEqual(viewController.segmentControlItems.count, 3) + // Ecosia: update items + // XCTAssertEqual(viewController.segmentControlItems.count, 3) + XCTAssertEqual(viewController.segmentControlItems.count, 2) guard let navController = viewController.navigationController else { XCTFail("NavigationController is expected") return @@ -78,7 +80,9 @@ final class TabTrayViewControllerTests: XCTestCase { viewController.layout = .regular viewController.viewWillAppear(false) - XCTAssertEqual(viewController.segmentControlItems.count, 3) + // Ecosia: update items + // XCTAssertEqual(viewController.segmentControlItems.count, 3) + XCTAssertEqual(viewController.segmentControlItems.count, 2) guard let navController = viewController.navigationController else { XCTFail("NavigationController is expected") return diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/MainMenuTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/MainMenuTelemetryTests.swift new file mode 100644 index 0000000000000..1554e961754c3 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/MainMenuTelemetryTests.swift @@ -0,0 +1,63 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Glean +import XCTest + +@testable import Client + +final class MainMenuTelemetryTests: XCTestCase { + var subject: MainMenuTelemetry? + let isHomepageKey = "is_homepage" + let optionKey = "option" + + override func setUp() { + super.setUp() + Glean.shared.resetGlean(clearStores: true) + subject = MainMenuTelemetry() + } + + func testRecordMainMenuWhenMainMenuOptionTappedThenGleanIsCalled() throws { + subject?.mainMenuOptionTapped(with: true, and: "test_option") + testEventMetricRecordingSuccess(metric: GleanMetrics.AppMenu.mainMenuOptionSelected) + + let resultValue = try XCTUnwrap(GleanMetrics.AppMenu.mainMenuOptionSelected.testGetValue()) + XCTAssertEqual(resultValue[0].extra?[optionKey], "test_option") + XCTAssertEqual(resultValue[0].extra?[isHomepageKey], "true") + } + + func testRecordMainMenuWhenSaveSubmenuOptionTappedThenGleanIsCalled() throws { + subject?.saveSubmenuOptionTapped(with: true, and: "test_option") + testEventMetricRecordingSuccess(metric: GleanMetrics.AppMenu.saveMenuOptionSelected) + + let resultValue = try XCTUnwrap(GleanMetrics.AppMenu.saveMenuOptionSelected.testGetValue()) + XCTAssertEqual(resultValue[0].extra?[optionKey], "test_option") + XCTAssertEqual(resultValue[0].extra?[isHomepageKey], "true") + } + + func testRecordMainMenuWhenToolsSubmenuOptionTappedThenGleanIsCalled() throws { + subject?.toolsSubmenuOptionTapped(with: true, and: "test_option") + testEventMetricRecordingSuccess(metric: GleanMetrics.AppMenu.toolsMenuOptionSelected) + + let resultValue = try XCTUnwrap(GleanMetrics.AppMenu.toolsMenuOptionSelected.testGetValue()) + XCTAssertEqual(resultValue[0].extra?[optionKey], "test_option") + XCTAssertEqual(resultValue[0].extra?[isHomepageKey], "true") + } + + func testRecordMainMenuWhenCloseButtonTappedThenGleanIsCalled() throws { + subject?.closeButtonTapped(isHomepage: true) + testEventMetricRecordingSuccess(metric: GleanMetrics.AppMenu.closeButton) + + let resultValue = try XCTUnwrap(GleanMetrics.AppMenu.closeButton.testGetValue()) + XCTAssertEqual(resultValue[0].extra?[isHomepageKey], "true") + } + + func testRecordMainMenuWhenMenuIsDismissedThenGleanIsCalled() throws { + subject?.menuDismissed(isHomepage: true) + testEventMetricRecordingSuccess(metric: GleanMetrics.AppMenu.menuDismissed) + + let resultValue = try XCTUnwrap(GleanMetrics.AppMenu.menuDismissed.testGetValue()) + XCTAssertEqual(resultValue[0].extra?[isHomepageKey], "true") + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift index 23f98cdda5d42..624ad444b8b16 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift @@ -1567,6 +1567,27 @@ class TelemetryWrapperTests: XCTestCase { extras: extra) testEventMetricRecordingSuccess(metric: GleanMetrics.Webview.showErrorPage) } + + func testRecordIfUserDefault() { + TelemetryWrapper.recordEvent(category: .action, + method: .open, + object: .defaultBrowser, + extras: [TelemetryWrapper.EventExtraKey.isDefaultBrowser.rawValue: true]) + testBoolMetricSuccess(metric: GleanMetrics.App.defaultBrowser, + expectedValue: true, + failureMessage: "Failed to record is default browser") + } + + func testRecordChoiceScreenAcquisition() { + let key = TelemetryWrapper.EventExtraKey.didComeFromBrowserChoiceScreen.rawValue + TelemetryWrapper.recordEvent(category: .action, + method: .open, + object: .choiceScreenAcquisition, + extras: [key: true]) + testBoolMetricSuccess(metric: GleanMetrics.App.choiceScreenAcquisition, + expectedValue: true, + failureMessage: "Failed to record choice screen acquisition") + } } // MARK: - Helper functions to test telemetry diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TestHistory.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TestHistory.swift index e94fc695c3b90..8bfb1176cc0f1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TestHistory.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TestHistory.swift @@ -31,8 +31,14 @@ class TestHistory: ProfileTest { fileprivate func innerCheckSites(_ places: RustPlaces, callback: @escaping (_ cursor: Cursor) -> Void) { // Retrieve the entry places.getSitesWithBound(limit: 100, offset: 0, excludedTypes: VisitTransitionSet(0)).upon { - XCTAssertTrue($0.isSuccess) - callback($0.successValue!) + do { + XCTAssertTrue($0.isSuccess) + let successValue = try XCTUnwrap($0.successValue) + callback(successValue) + } catch { + XCTFail("Should not receive a nil successValue") + callback(Cursor()) + } } } @@ -47,14 +53,20 @@ class TestHistory: ProfileTest { excludedTypes: VisitTransitionSet(0) ).value.successValue { XCTAssertEqual(cursor.status, CursorStatus.success, "Returned success \(cursor.statusMessage).") - XCTAssertEqual(cursor.count, urls.count, "Cursor has \(urls.count) entries.") + XCTAssertEqual(cursor.count, urls.count, "Cursor has \(cursor.count) entries. Duplicates are filtered out.") for index in 0.. (Data, URLResponse) { + if let error = error { + throw error + } + + return (data ?? Data(), response ?? URLResponse()) + } + func dataTaskWith(_ url: URL, completionHandler completion: @escaping DataTaskResult ) -> URLSessionDataTaskProtocol { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperNetworkingModuleTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperNetworkingModuleTests.swift index 7f21c88387865..d57fddf58fb39 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperNetworkingModuleTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperNetworkingModuleTests.swift @@ -10,28 +10,41 @@ import XCTest class WallpaperNetworkingModuleTests: XCTestCase, WallpaperTestDataProvider { let url = URL(string: "my.testurl.com")! - func testResumeWasCalled() async { - let dataTask = WallpaperURLSessionDataTaskMock() - let session = WallpaperURLSessionMock(with: nil, response: nil, and: nil) - session.dataTask = dataTask + func testAsyncDataCall() async { + let expectedData = "Test data".data(using: .utf8)! + let expectedResponse = HTTPURLResponse(url: URL(string: "https://example.com")!, + statusCode: 200, + httpVersion: nil, + headerFields: nil)! + + let session = WallpaperURLSessionMock( + with: expectedData, + response: expectedResponse, + and: nil + ) let subject = WallpaperNetworkingModule(with: session) - _ = try? await subject.data(from: url) - XCTAssertTrue(dataTask.resumeWasCalled) + do { + let (responseData, response) = try await subject.data(from: url) + XCTAssertEqual(responseData, expectedData) + XCTAssertEqual((response as? HTTPURLResponse)?.statusCode, 200) + } catch { + XCTFail("This test should not throw an error, but it did.") + } } func testServerReturnsError() async { + let expectedError = URLError(.cannotConnectToHost) let session = WallpaperURLSessionMock(with: nil, response: nil, - and: URLError(.cannotConnectToHost)) + and: expectedError) let subject = WallpaperNetworkingModule(with: session) do { _ = try await subject.data(from: url) XCTFail("This test should throw an error, but it did not.") } catch { - XCTAssertEqual(error as! URLError, - URLError(.cannotConnectToHost)) + XCTAssertEqual(error as? URLError, expectedError) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSelectorViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSelectorViewModelTests.swift index 67d25cfbf31c7..769e7ab89dd8f 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSelectorViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSelectorViewModelTests.swift @@ -2,7 +2,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ +/* Ecosia: Remove Glean import Glean + */ import XCTest @testable import Client @@ -16,7 +18,8 @@ class WallpaperSelectorViewModelTests: XCTestCase { wallpaperManager = WallpaperManagerMock() addWallpaperCollections() - Glean.shared.resetGlean(clearStores: true) + // Ecosia: remove Glean dependency + // Glean.shared.resetGlean(clearStores: true) } override func tearDown() { @@ -54,6 +57,7 @@ class WallpaperSelectorViewModelTests: XCTestCase { } } + /* Ecosia: remove Glean dependency func testRecordsWallpaperSelectorView() { wallpaperManager = WallpaperManager() let subject = createSubject() @@ -69,7 +73,7 @@ class WallpaperSelectorViewModelTests: XCTestCase { testEventMetricRecordingSuccess(metric: GleanMetrics.Onboarding.wallpaperSelectorClose) } - + */ // func testClickingCell_recordsWallpaperChange() { // let subject = createSubject() // diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSettingsViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSettingsViewModelTests.swift index 9177124723c4c..5a34a567cd9a2 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSettingsViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Wallpaper/WallpaperSettingsViewModelTests.swift @@ -3,7 +3,9 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import Common +/* Ecosia: Remove Glean import Glean + */ import Shared import XCTest @@ -18,7 +20,8 @@ class WallpaperSettingsViewModelTests: XCTestCase { wallpaperManager = WallpaperManagerMock() addWallpaperCollections() - Glean.shared.resetGlean(clearStores: true) + // Ecosia: remove Glean dependency + // Glean.shared.resetGlean(clearStores: true) } override func tearDown() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan index 4cf368ded01ea..d5b38b125f7e3 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/ExperimentIntegrationTests.xctestplan @@ -46,6 +46,7 @@ }, "testTargets" : [ { + "enabled" : false, "skippedTests" : [ "ActivityStreamTest", "AuthenticationTest", diff --git a/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan b/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan index 961877cbf68a1..d934ffb1fd8cc 100644 --- a/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/FullFunctionalTestPlan.xctestplan @@ -28,6 +28,7 @@ }, "testTargets" : [ { + "enabled" : false, "skippedTests" : [ "ActivityStreamTest\/testActivityStreamPages()", "ActivityStreamTest\/testDefaultSites()", diff --git a/firefox-ios/firefox-ios-tests/Tests/SharedTests/UserAgentBuilderTests.swift b/firefox-ios/firefox-ios-tests/Tests/SharedTests/UserAgentBuilderTests.swift new file mode 100644 index 0000000000000..9569d95e8c066 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/SharedTests/UserAgentBuilderTests.swift @@ -0,0 +1,79 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +import Common +@testable import Shared + +final class UserAgentBuilderTests: XCTestCase { + func testUserAgent_generateCorrectString() { + let builder = createSubject( + product: "FXIOS", + systemInfo: "iPhone", + platform: "Apple", + platformDetails: "", + extensions: "15.09.0" + ) + let agent = builder.userAgent() + + XCTAssertEqual(agent, "FXIOS iPhone Apple 15.09.0") + } + + func testClone_generateCorrectString() { + let builder = createSubject( + product: "FXIOS", + systemInfo: "iPhone", + platform: "Apple ", + platformDetails: " Details ", + extensions: " 15.09.0 " + ) + + let agent = builder.clone( + product: "FXIOS1", + systemInfo: "iPhone12", + platform: "Apple 5", + platformDetails: "New details", + extensions: "14.090" + ) + return XCTAssertEqual(agent, "FXIOS1 iPhone12 Apple 5 New details 14.090") + } + + func testDefaultMobileUserAgent() { + let builder = UserAgentBuilder.defaultMobileUserAgent() + let systemInfo = "(\(UIDevice.current.model); CPU iPhone OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) like Mac OS X)" + /* Ecosia: Update extensions + let extensions = "FxiOS/\(AppInfo.appVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)" + */ + let extensions = "\(UserAgent.uaBitVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari) \(UserAgent.uaBitEcosia)" + let testAgent = "\(UserAgent.product) \(systemInfo) \(UserAgent.platform) \(UserAgent.platformDetails) \(extensions)" + XCTAssertEqual(builder.userAgent(), testAgent) + } + + func testDefaultDesktopUserAgent() { + let builder = UserAgentBuilder.defaultDesktopUserAgent() + let systemInfo = "(Macintosh; Intel Mac OS X 10.15)" + /* Ecosia: Update extensions + let extensions = "FxiOS/\(AppInfo.appVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)" + */ + let extensions = "\(UserAgent.uaBitVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)" + let testAgent = "\(UserAgent.product) \(systemInfo) \(UserAgent.platform) \(UserAgent.platformDetails) \(extensions)" + XCTAssertEqual(builder.userAgent(), testAgent) + } + + private func createSubject( + product: String, + systemInfo: String, + platform: String, + platformDetails: String, + extensions: String + ) -> UserAgentBuilder { + return UserAgentBuilder( + product: product, + systemInfo: systemInfo, + platform: platform, + platformDetails: platformDetails, + extensions: extensions + ) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/SharedTests/UserAgentTests.swift b/firefox-ios/firefox-ios-tests/Tests/SharedTests/UserAgentTests.swift new file mode 100644 index 0000000000000..b425f901ee12f --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/SharedTests/UserAgentTests.swift @@ -0,0 +1,43 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Shared +import Common + +final class UserAgentTests: XCTestCase { + func testGetUserAgentDesktop_withListedDomain_returnProperUserAgent() { + let domains = CustomUserAgentConstant.customDesktopUAForDomain + domains.forEach { domain, agent in + // Ecosia: Add not nil check for domain + XCTAssertNotNil(domain) + XCTAssertEqual(agent, UserAgent.getUserAgent(domain: domain, platform: .Desktop)) + } + } + + func testGetUserAgentMobile_withListedDomain_returnProperUserAgent() { + let domains = CustomUserAgentConstant.customMobileUAForDomain + domains.forEach { domain, agent in + XCTAssertEqual(agent, UserAgent.getUserAgent(domain: domain, platform: .Mobile)) + } + } + + func testGetUserAgentDesktop_withPaypalDomain_returnMobileUserAgent() { + let paypalDomain = "paypal.com" + /* Ecosia: Use default Firefox UA instead + XCTAssertEqual(UserAgent.mobileUserAgent(), + */ + XCTAssertEqual(UserAgentBuilder.defaultFirefoxMobileUserAgent().userAgent(), + UserAgent.getUserAgent(domain: paypalDomain, platform: .Desktop)) + } + + func testGetUserAgentMobile_withPaypalDomain_returnProperUserAgent() { + let paypalDomain = "paypal.com" + /* Ecosia: Use default Firefox UA instead + XCTAssertEqual(UserAgent.mobileUserAgent(), + */ + XCTAssertEqual(UserAgentBuilder.defaultFirefoxMobileUserAgent().userAgent(), + UserAgent.getUserAgent(domain: paypalDomain, platform: .Mobile)) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan index 9dc59b4b45efe..123fa935d7213 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest1.xctestplan @@ -14,6 +14,7 @@ }, "testTargets" : [ { + "enabled" : false, "skippedTests" : [ "ActivityStreamTest", "ActivityStreamTest\/testContextMenuInLandscape()", diff --git a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan index fa1470d46836c..ddebe31bae4c8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/Smoketest4.xctestplan @@ -14,6 +14,7 @@ }, "testTargets" : [ { + "enabled" : false, "skippedTests" : [ "ActivityStreamTest\/testContextMenuInLandscape()", "ActivityStreamTest\/testLongTapOnTopSiteOptions()", diff --git a/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan b/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan index eb4a93443274e..3193937b90b23 100644 --- a/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan @@ -95,133 +95,158 @@ } }, "testTargets" : [ - { - "target" : { - "containerPath" : "container:Client.xcodeproj", - "identifier" : "2FA436041ABB83B4008031D1", - "name" : "AccountTests" - } - }, { "skippedTests" : [ - "ContentBlockerTests", - "ETPCoverSheetTests", - "GleanPlumbMessageManagerTests\/testManagerOnMessagePressed_withMalformedURL()", - "HistoryHighlightsDataAdaptorTests", - "HistoryHighlightsDataAdaptorTests\/testReloadDataOnNotification()", - "HistoryHighlightsTests", - "IntroViewControllerTests\/testBasicSetupReturnsExpectedItems()", - "RustSyncManagerTests\/testGetEnginesAndKeys()", - "RustSyncManagerTests\/testGetEnginesAndKeysWithNoKey()", - "RustSyncManagerTests\/testUpdateEnginePrefs_addressesDisabled()", - "RustSyncManagerTests\/testUpdateEnginePrefs_addressesEnabled()", - "RustSyncManagerTests\/testUpdateEnginePrefs_bookmarksEnabled()", - "ShortcutRouteTests\/testOpenLastBookmarkShortcutWithInvalidUrl()", - "TabManagerTests\/testDeleteSelectedTab()", - "TabManagerTests\/testPrivatePreference_togglePBMDeletesPrivate()", - "TestFavicons\/testFaviconFetcherParse()", - "VersionSettingTests", - "VersionSettingTests\/testCopyAppVersion()" + "MetaGlobalTests\/testHappyOptimisticStateMachine()", + "MetaGlobalTests\/testMetaGlobalAndCryptoKeysFresh()", + "MetaGlobalTests\/testMetaGlobalModified()", + "MetaGlobalTests\/testUpdatedCryptoKeys()", + "StateTests\/testPickling()" ], "target" : { "containerPath" : "container:Client.xcodeproj", - "identifier" : "F84B21D21A090F8100AAB793", - "name" : "ClientTests" + "identifier" : "282731671ABC9BE700AA1954", + "name" : "SyncTests" } }, { "target" : { - "containerPath" : "container:Client.xcodeproj", - "identifier" : "E6F9650B1B2F1CF20034B023", - "name" : "SharedTests" + "containerPath" : "container:..\/BrowserKit", + "identifier" : "TabDataStoreTests", + "name" : "TabDataStoreTests" } }, { - "skippedTests" : [ - "RustLoginsTests\/testUpdateLogin()", - "TestBrowserDB\/testMovesDB()" - ], "target" : { - "containerPath" : "container:Client.xcodeproj", - "identifier" : "2FCAE2231ABB51F800877008", - "name" : "StorageTests" + "containerPath" : "container:..\/BrowserKit", + "identifier" : "CommonTests", + "name" : "CommonTests" } }, { "target" : { - "containerPath" : "container:Client.xcodeproj", - "identifier" : "E69DB07C1E97DEA9008A67E6", - "name" : "SyncTelemetryTests" + "containerPath" : "container:..\/BrowserKit", + "identifier" : "ContentBlockingGeneratorTests", + "name" : "ContentBlockingGeneratorTests" } }, { "skippedTests" : [ - "MetaGlobalTests\/testHappyOptimisticStateMachine()", - "MetaGlobalTests\/testMetaGlobalAndCryptoKeysFresh()", - "MetaGlobalTests\/testMetaGlobalModified()", - "MetaGlobalTests\/testUpdatedCryptoKeys()", - "StateTests\/testPickling()" + "TestBrowserDB\/testMovesDB()" ], "target" : { "containerPath" : "container:Client.xcodeproj", - "identifier" : "282731671ABC9BE700AA1954", - "name" : "SyncTests" + "identifier" : "2FCAE2231ABB51F800877008", + "name" : "StorageTests" } }, { "target" : { "containerPath" : "container:..\/BrowserKit", - "identifier" : "CommonTests", - "name" : "CommonTests" + "identifier" : "ToolbarKitTests", + "name" : "ToolbarKitTests" } }, { "target" : { "containerPath" : "container:..\/BrowserKit", - "identifier" : "SiteImageViewTests", - "name" : "SiteImageViewTests" + "identifier" : "MenuKitTests", + "name" : "MenuKitTests" } }, { + "skippedTests" : [ + "ContentBlockerTests\/testCompileListsNotInStore_callsCompletionHandlerSuccessfully()", + "ETPCoverSheetTests", + "FirefoxHomeViewModelTests", + "FirefoxHomeViewModelTests\/testNumberOfSection_withoutUpdatingData_has2Sections()", + "GeneralizedImageFetcherTests\/testBadStatusCode()", + "GeneralizedImageFetcherTests\/testErrorResponse()", + "GeneralizedImageFetcherTests\/testNilData()", + "GleanPlumbMessageManagerTests\/testManagerOnMessagePressed_withMalformedURL()", + "HistoryHighlightsDataAdaptorTests", + "HistoryHighlightsDataAdaptorTests\/testDelegateMemoryLeak()", + "HistoryHighlightsDataAdaptorTests\/testDeleteGroupItem()", + "HistoryHighlightsDataAdaptorTests\/testDeleteIndividualItem()", + "HistoryHighlightsDataAdaptorTests\/testInitialLoadWithHistoryData()", + "HistoryHighlightsDataAdaptorTests\/testInitialLoadWithNoHistoryData()", + "HistoryHighlightsDataAdaptorTests\/testReloadDataOnNotification()", + "IntroScreenManagerTests\/testHasSeenIntroScreen_shouldNotShowIt()", + "ShortcutRouteTests", + "ShortcutRouteTests\/testInvalidShortcut()", + "ShortcutRouteTests\/testNewPrivateTabShortcut()", + "ShortcutRouteTests\/testNewTabShortcut()", + "ShortcutRouteTests\/testOpenLastBookmarkShortcutWithInvalidUrl()", + "ShortcutRouteTests\/testOpenLastBookmarkShortcutWithValidUrl()", + "ShortcutRouteTests\/testQRCodeShortcut()", + "SyncContentSettingsViewControllerTests", + "SyncContentSettingsViewControllerTests\/test_syncContentSettingsViewController_engineSectionForSettings()", + "SyncContentSettingsViewControllerTests\/test_syncContentSettingsViewController_generateSettingsCount()", + "TabLocationViewTests\/testDelegateMemoryLeak()", + "TabManagerNavDelegateTests", + "TabManagerNavDelegateTests\/test_webViewDecidePolicyFor_actionPolicy_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDecidePolicyFor_responsePolicy_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidCommit_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidFailProvisionalNavigation_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidFail_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidFinish_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidReceiveAuthenticationChallenge_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidReceiveServerRedirectForProvisionalNavigation_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewDidStartProvisionalNavigation_sendsCorrectMessage()", + "TabManagerNavDelegateTests\/test_webViewWebContentProcessDidTerminate_sendsCorrectMessage()", + "TabManagerTests\/testDeleteSelectedTab()", + "TabManagerTests\/testPrivatePreference_togglePBMDeletesPrivate()", + "TestFavicons\/testFaviconFetcherParse()" + ], "target" : { - "containerPath" : "container:..\/BrowserKit", - "identifier" : "WebEngineTests", - "name" : "WebEngineTests" + "containerPath" : "container:Client.xcodeproj", + "identifier" : "F84B21D21A090F8100AAB793", + "name" : "ClientTests" } }, { "target" : { "containerPath" : "container:..\/BrowserKit", - "identifier" : "TabDataStoreTests", - "name" : "TabDataStoreTests" + "identifier" : "ReduxTests", + "name" : "ReduxTests" } }, { + "skippedTests" : [ + "AppDelegateFeatureManagementIntegrationTests\/testStateAfterDidBecomeActive_expectesSameModel_AfterDidFinishLaunchingWithOptions()" + ], "target" : { - "containerPath" : "container:..\/BrowserKit", - "identifier" : "ReduxTests", - "name" : "ReduxTests" + "containerPath" : "container:Client.xcodeproj", + "identifier" : "2CFE996E2D45329300B25CE0", + "name" : "EcosiaTests" } }, { "target" : { "containerPath" : "container:..\/BrowserKit", - "identifier" : "ToolbarKitTests", - "name" : "ToolbarKitTests" + "identifier" : "SiteImageViewTests", + "name" : "SiteImageViewTests" } }, { "target" : { - "containerPath" : "container:..\/BrowserKit", - "identifier" : "ContentBlockingGeneratorTests", - "name" : "ContentBlockingGeneratorTests" + "containerPath" : "container:Client.xcodeproj", + "identifier" : "E6F9650B1B2F1CF20034B023", + "name" : "SharedTests" } }, { "target" : { "containerPath" : "container:..\/BrowserKit", - "identifier" : "MenuKitTests", - "name" : "MenuKitTests" + "identifier" : "WebEngineTests", + "name" : "WebEngineTests" + } + }, + { + "target" : { + "containerPath" : "container:Client.xcodeproj", + "identifier" : "E69DB07C1E97DEA9008A67E6", + "name" : "SyncTelemetryTests" } } ], diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift index 958d4b3571d6d..4a66930c1a809 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift @@ -8,7 +8,7 @@ import Common let PDF_website = [ "url": "https://storage.googleapis.com/mobile_test_assets/public/pdf-test.pdf", "pdfValue": "storage.googleapis.com/mobile_test_assets/public/pdf-test.pdf", - "urlValue": "yukon.ca/en/education-and-schools", + "urlValue": "www.education.gov.yk.ca/", "bookmarkLabel": "https://storage.googleapis.com/mobile_test_assets/public/pdf-test.pdf", "longUrlValue": "http://www.education.gov.yk.ca/" ] diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift index 56571c6f8bd80..5f75ef0b5b729 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift @@ -439,7 +439,7 @@ class HistoryTests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.staticTexts[entry]) } mozWaitForElementToNotExist(app.staticTexts["Today"]) - mozWaitForElementToExist(app.staticTexts["Older"]) + mozWaitForElementToExist(app.staticTexts["Last month"]) // Begin Test for Today and Yesterday // Visit a page to create a recent history entry. @@ -452,7 +452,7 @@ class HistoryTests: BaseTestCase { XCTAssertTrue(app.tables.cells.staticTexts[entry].exists) } mozWaitForElementToNotExist(app.staticTexts["Today"]) - mozWaitForElementToExist(app.staticTexts["Older"]) + mozWaitForElementToExist(app.staticTexts["Last month"]) // Begin Test for Everything // Visit a page to create a recent history entry. @@ -464,15 +464,12 @@ class HistoryTests: BaseTestCase { mozWaitForElementToNotExist(app.tables.cells.staticTexts[entry]) } mozWaitForElementToNotExist(app.staticTexts["Today"]) - mozWaitForElementToNotExist(app.staticTexts["Older"]) + mozWaitForElementToNotExist(app.staticTexts["Last month"]) } // https://mozilla.testrail.io/index.php?/cases/view/2306890 // Smoketest func testDeleteHistoryEntryBySwiping() { - navigateToPage() - navigator.goto(LibraryPanel_History) - waitForExistence(app.cells.staticTexts["http://example.com/"]) navigateToPage() navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.cells.staticTexts["http://example.com/"]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift index f2d915e44c4ea..b2e27a6f4d074 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift @@ -26,7 +26,7 @@ class URLValidationTests: BaseTestCase { for url in urlTypes { navigator.openURL(url) waitUntilPageLoad() - mozWaitForElementToExist(app.otherElements.staticTexts["Mozilla"]) + mozWaitForElementToExist(app.otherElements.staticTexts["Welcome to Mozilla"]) mozWaitForElementToExist(app.buttons["Menu"]) // Getting the current system locale ex:- en-US var locale = Locale.preferredLanguages[0] diff --git a/focus-ios/Blockzilla.xcodeproj/project.pbxproj b/focus-ios/Blockzilla.xcodeproj/project.pbxproj index 82b3e6916fbd3..44b8f473a349c 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.pbxproj +++ b/focus-ios/Blockzilla.xcodeproj/project.pbxproj @@ -229,6 +229,7 @@ EB84F96F209380CE00BA6739 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB84F96E209380CE00BA6739 /* URLExtensions.swift */; }; EB84F9712093815500BA6739 /* effective_tld_names.dat in Resources */ = {isa = PBXBuildFile; fileRef = EB84F9702093815500BA6739 /* effective_tld_names.dat */; }; EBE44F4E20ADDF6A005AFEA6 /* SmartLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE44F4D20ADDF6A005AFEA6 /* SmartLabel.swift */; }; + ED5ACDB02CE6AABF00A55506 /* Sentry-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = ED5ACDAF2CE6AABF00A55506 /* Sentry-Dynamic */; }; F74E8109218183F400D18535 /* SearchSuggestionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74E8108218183F400D18535 /* SearchSuggestionsTest.swift */; }; F7B3E49D2165C32B00118785 /* SearchSuggestionsPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B3E49C2165C32B00118785 /* SearchSuggestionsPromptView.swift */; }; F805722F1DBEE504004339C1 /* WebCacheUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F805722E1DBEE504004339C1 /* WebCacheUtils.swift */; }; @@ -241,7 +242,6 @@ F861799F27961F4200CFD324 /* InternalSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F861799E27961F4200CFD324 /* InternalSettings.swift */; }; F86D00612790978700E83177 /* InternalSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86D00602790978700E83177 /* InternalSettingsView.swift */; }; F8714D2127A1E1F400CEB695 /* InternalCrashReportingSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8714D2027A1E1F400CEB695 /* InternalCrashReportingSettingsView.swift */; }; - F8B92FBE279EC73700183998 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = F8B92FBD279EC73700183998 /* Sentry */; }; F8BC6A0727224B170026AB9F /* NimbusExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8BC6A0627224B170026AB9F /* NimbusExtensions.swift */; }; F8BC6A0927224B890026AB9F /* NimbusWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8BC6A0827224B890026AB9F /* NimbusWrapper.swift */; }; F8DE0EC826CC887800A31419 /* SupportUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DE0EC726CC887800A31419 /* SupportUtilsTest.swift */; }; @@ -1275,8 +1275,8 @@ CDFA746F27ABD43D0055FE55 /* Glean in Frameworks */, C8FB1E6427F59D9C00C60DC6 /* UIHelpers in Frameworks */, 8A0E7F2E2BA0F0EE006BC6B6 /* Fuzi in Frameworks */, - F8B92FBE279EC73700183998 /* Sentry in Frameworks */, C8FB1E6627F5A79300C60DC6 /* DesignSystem in Frameworks */, + ED5ACDB02CE6AABF00A55506 /* Sentry-Dynamic in Frameworks */, C82F45F728193EE8000D7D84 /* AppShortcuts in Frameworks */, C871586027FB29AC006224BB /* Onboarding in Frameworks */, 451B81BF2BA4DB9F00CF3C50 /* RustMozillaAppServices.framework in Frameworks */, @@ -2123,7 +2123,6 @@ name = Blockzilla; packageProductDependencies = ( F85F7A282655AD5800395515 /* SnapKit */, - F8B92FBD279EC73700183998 /* Sentry */, CDFA746E27ABD43D0055FE55 /* Glean */, C8FB1E6327F59D9C00C60DC6 /* UIHelpers */, C8FB1E6527F5A79300C60DC6 /* DesignSystem */, @@ -2131,6 +2130,7 @@ C82F45F628193EE8000D7D84 /* AppShortcuts */, C809130D2A08F245000889B0 /* Licenses */, 8A0E7F2D2BA0F0EE006BC6B6 /* Fuzi */, + ED5ACDAF2CE6AABF00A55506 /* Sentry-Dynamic */, ); productName = Blockzilla; productReference = E4BF2DD31BACE8CA00DA9D68 /* Firefox Klar.app */; @@ -7168,7 +7168,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift"; requirement = { kind = exactVersion; - version = 133.0.20241027050311; + version = 133.0.0; }; }; 8A0E7F2C2BA0F0E0006BC6B6 /* XCRemoteSwiftPackageReference "Fuzi" */ = { @@ -7200,7 +7200,7 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa"; requirement = { kind = exactVersion; - version = 8.21.0; + version = 8.36.0; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -7246,16 +7246,16 @@ package = CDFA746D27ABD43D0055FE55 /* XCRemoteSwiftPackageReference "glean-swift" */; productName = Glean; }; + ED5ACDAF2CE6AABF00A55506 /* Sentry-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = F8B92FBC279EC73700183998 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; + productName = "Sentry-Dynamic"; + }; F85F7A282655AD5800395515 /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = F8324C2D264C807C007E4BFA /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - F8B92FBD279EC73700183998 /* Sentry */ = { - isa = XCSwiftPackageProductDependency; - package = F8B92FBC279EC73700183998 /* XCRemoteSwiftPackageReference "sentry-cocoa" */; - productName = Sentry; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = E4BF2DCB1BACE8CA00DA9D68 /* Project object */; diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8416ff4b5cc5f..72a127f834b94 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -28,7 +28,7 @@ "version": null } }, - { + { "package": "Glean", "repositoryURL": "https://github.com/mozilla/glean-swift", "state": { @@ -46,13 +46,13 @@ "version": "7.12.0" } }, - { + { "package": "MozillaRustComponentsSwift", "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "e891a641517fc8c76097e33a77b3e92818fc5656", - "version": "133.0.20241027050311" + "revision": "277bceafc423d793f5a1c1a50f76d67fb1feec4e", + "version": "133.0.0" } }, { @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/getsentry/sentry-cocoa", "state": { "branch": null, - "revision": "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version": "8.21.0" + "revision": "5575af93efb776414f243e93d6af9f6258dc539a", + "version": "8.36.0" } }, { diff --git a/focus-ios/Blockzilla/Info.plist b/focus-ios/Blockzilla/Info.plist index d3b7ed885db34..7585b035f9c8e 100644 --- a/focus-ios/Blockzilla/Info.plist +++ b/focus-ios/Blockzilla/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 133.0 + 133.4 CFBundleSignature ???? CFBundleURLTypes diff --git a/focus-ios/Blockzilla/metrics.yaml b/focus-ios/Blockzilla/metrics.yaml index 4ba001719aa00..cd490e407e26c 100644 --- a/focus-ios/Blockzilla/metrics.yaml +++ b/focus-ios/Blockzilla/metrics.yaml @@ -19,7 +19,7 @@ legacy.ids: notification_emails: - focus-ios-data-stewards@mozilla.com - sarentz@mozilla.com - expires: never + expires: "2025-03-01" app: opened_as_default_browser: @@ -35,7 +35,7 @@ app: - https://github.com/mozilla-mobile/focus-ios/pull/3855 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" keyboard_type: type: string @@ -49,7 +49,7 @@ app: - https://github.com/mozilla-mobile/focus-ios/pull/4023 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-10" + expires: "2025-03-01" browser: total_uri_count: @@ -69,7 +69,7 @@ browser: notification_emails: - focus-ios-data-stewards@mozilla.com - sarentz@mozilla.com - expires: never + expires: "2025-03-01" send_in_pings: - metrics - baseline @@ -89,7 +89,7 @@ browser: - focus-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: "2024-07-10" + expires: "2025-03-01" mozilla_products: has_firefox_installed: @@ -107,7 +107,7 @@ mozilla_products: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" settings_screen: set_as_default_browser_pressed: @@ -124,7 +124,7 @@ settings_screen: - focus-ios-data-stewards@mozilla.com data_sensitivity: - interaction - expires: never + expires: "2025-03-01" autocomplete_domain_added: type: counter @@ -140,7 +140,7 @@ settings_screen: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" tracking_protection: toolbar_shield_clicked: @@ -157,7 +157,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" tracking_protection_changed: type: event @@ -172,7 +172,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" extra_keys: is_enabled: description: The new setting for ETP, true for ON, false for OFF @@ -193,7 +193,7 @@ tracking_protection: notification_emails: - focus-ios-data-stewards@mozilla.com - sarentz@mozilla.com - expires: never + expires: "2025-03-01" tracker_setting_changed: type: event @@ -208,7 +208,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" extra_keys: source_of_change: description: The source of interaction, "Panel" or "Settings" @@ -235,7 +235,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" has_advertising_blocked: type: boolean @@ -251,7 +251,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" has_analytics_blocked: type: boolean @@ -267,7 +267,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" has_content_blocked: type: boolean @@ -283,7 +283,7 @@ tracking_protection: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-17" + expires: "2025-03-01" shortcuts: shortcuts_on_home_number: @@ -300,7 +300,7 @@ shortcuts: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-20" + expires: "2025-03-01" unit: shortcut(s) shortcut_opened_counter: type: counter @@ -316,7 +316,7 @@ shortcuts: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-20" + expires: "2025-03-01" shortcut_added_counter: type: counter description: | @@ -331,7 +331,7 @@ shortcuts: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-20" + expires: "2025-03-01" shortcut_removed_counter: type: labeled_counter description: | @@ -347,7 +347,7 @@ shortcuts: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-20" + expires: "2025-03-01" labels: - removed_from_browser_menu - removed_from_home_screen @@ -368,7 +368,7 @@ preferences: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-07-01" + expires: "2025-03-01" browser_menu: browser_menu_action: @@ -383,7 +383,7 @@ browser_menu: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" extra_keys: item: description: | @@ -406,7 +406,7 @@ show_search_suggestions: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" disabled_from_panel: type: event description: The "no"" option from the suggestion panel has been tapped. @@ -419,7 +419,7 @@ show_search_suggestions: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" changed_from_settings: type: event description: The enabled state has been changed from the settings screen. @@ -432,7 +432,7 @@ show_search_suggestions: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" extra_keys: is_enabled: description: The new setting value, true for ON, false for OFF @@ -451,7 +451,7 @@ search_suggestions: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" extra_keys: engine_name: description: The name of the engine used to perform the search. @@ -468,7 +468,7 @@ search_suggestions: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" extra_keys: engine_name: description: The name of the engine used to perform the search. @@ -485,7 +485,7 @@ search_suggestions: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" url_interaction: drag_started: @@ -499,7 +499,7 @@ url_interaction: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" drop_ended: type: event description: Drop interaction ended. @@ -511,7 +511,7 @@ url_interaction: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" paste_and_go: type: event description: Paste url and navigate. @@ -523,7 +523,7 @@ url_interaction: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" siri: open_favorite_site: @@ -537,7 +537,7 @@ siri: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" erase_and_open: type: event description: Siri request erase session and open. @@ -549,7 +549,7 @@ siri: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" erase_in_background: type: event description: Siri request erase session. @@ -561,7 +561,7 @@ siri: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" browser_search: with_ads: @@ -583,7 +583,7 @@ browser_search: - https://github.com/mozilla-mobile/firefox-ios/pull/22609#issuecomment-2419002541 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" ad_clicks: type: labeled_counter description: | @@ -603,7 +603,7 @@ browser_search: - https://github.com/mozilla-mobile/firefox-ios/pull/22609#issuecomment-2419002541 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" search_count: type: labeled_counter description: | @@ -632,7 +632,7 @@ browser_search: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" in_content: type: labeled_counter description: | @@ -645,7 +645,7 @@ browser_search: - https://github.com/mozilla-mobile/firefox-ios/pull/22609#issuecomment-2419002541 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" send_in_pings: - metrics - baseline @@ -669,7 +669,7 @@ onboarding: - https://github.com/mozilla-mobile/focus-ios/pull/3487 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" primary_button_tap: type: event description: | @@ -685,7 +685,7 @@ onboarding: - https://github.com/mozilla-mobile/focus-ios/pull/3487 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" secondary_button_tap: type: event description: | @@ -701,7 +701,7 @@ onboarding: - https://github.com/mozilla-mobile/focus-ios/pull/3487 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" close_tap: type: event description: | @@ -717,7 +717,7 @@ onboarding: - https://github.com/mozilla-mobile/focus-ios/pull/3487 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" default_browser_onboarding: dismiss_pressed: @@ -730,7 +730,7 @@ default_browser_onboarding: - https://github.com/mozilla-mobile/focus-ios/pull/3487 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" go_to_settings_pressed: type: counter description: | @@ -742,7 +742,7 @@ default_browser_onboarding: - https://github.com/mozilla-mobile/focus-ios/pull/3487 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" skip_button_tapped: type: event description: The user has tapped to skip onboarding. @@ -754,7 +754,7 @@ default_browser_onboarding: - interaction notification_emails: - focus-ios-data-stewards@mozilla.com - expires: "2024-03-05" + expires: "2025-03-01" extra_keys: current_item: description: The curent displayed item position. @@ -775,7 +775,7 @@ search: - https://github.com/mozilla-mobile/firefox-ios/pull/9673 notification_emails: - focus-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" send_in_pings: - metrics - baseline @@ -793,7 +793,7 @@ webview: - https://github.com/mozilla-mobile/focus-ios/pull/3968 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" fail: type: event description: | @@ -804,4 +804,4 @@ webview: - https://github.com/mozilla-mobile/focus-ios/pull/3968 notification_emails: - fx-ios-data-stewards@mozilla.com - expires: never + expires: "2025-03-01" diff --git a/focus-ios/BlockzillaPackage/Sources/DesignSystem/Assets.xcassets/Tracking Protection/Drawer/icon_close.imageset/Contents.json b/focus-ios/BlockzillaPackage/Sources/DesignSystem/Assets.xcassets/Tracking Protection/Drawer/icon_close.imageset/Contents.json index c3ac8d357c150..c61317dd862c9 100644 --- a/focus-ios/BlockzillaPackage/Sources/DesignSystem/Assets.xcassets/Tracking Protection/Drawer/icon_close.imageset/Contents.json +++ b/focus-ios/BlockzillaPackage/Sources/DesignSystem/Assets.xcassets/Tracking Protection/Drawer/icon_close.imageset/Contents.json @@ -10,6 +10,7 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } } diff --git a/focus-ios/ContentBlocker/Info.plist b/focus-ios/ContentBlocker/Info.plist index 1c57ee10ca652..ee12f403b47d8 100644 --- a/focus-ios/ContentBlocker/Info.plist +++ b/focus-ios/ContentBlocker/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 133.0 + 133.4 CFBundleSignature ???? CFBundleVersion diff --git a/focus-ios/FocusIntentExtension/Info.plist b/focus-ios/FocusIntentExtension/Info.plist index cc2cee8dc946d..2882df292d7dc 100644 --- a/focus-ios/FocusIntentExtension/Info.plist +++ b/focus-ios/FocusIntentExtension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 133.0 + 133.4 CFBundleVersion 1 NSExtension diff --git a/focus-ios/OpenInFocus/Info.plist b/focus-ios/OpenInFocus/Info.plist index ab14b30f2559b..1c5d74e08a4bc 100644 --- a/focus-ios/OpenInFocus/Info.plist +++ b/focus-ios/OpenInFocus/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 133.0 + 133.4 CFBundleVersion 1 NSExtension diff --git a/focus-ios/README.md b/focus-ios/README.md index af63f621032d7..64989fe013d9e 100644 --- a/focus-ios/README.md +++ b/focus-ios/README.md @@ -8,7 +8,7 @@ Download on the [App Store](https://itunes.apple.com/app/id1055677337). ## Main Branch -This branch works with Xcode 15.4 and supports iOS 15.0 and newer. +This branch works with Xcode 16.1 and supports iOS 15.0 and newer. Pull requests should be submitted with `main` as the base branch. diff --git a/focus-ios/Widgets/Info.plist b/focus-ios/Widgets/Info.plist index 4bce717215b04..adeae6eb0a0c6 100644 --- a/focus-ios/Widgets/Info.plist +++ b/focus-ios/Widgets/Info.plist @@ -3,7 +3,7 @@ CFBundleShortVersionString - 133.0 + 133.4 NSExtension NSExtensionPointIdentifier diff --git a/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift b/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift index 8bd92dee7bcc2..312e75ea1a529 100644 --- a/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift @@ -29,7 +29,7 @@ class URLValidationTest: BaseTestCase { private func loadAndValidateURL(URL: String) { loadWebPage(URL) waitForWebPageLoad() - mozWaitForElementToExist(app.otherElements.staticTexts["Mozilla"]) + mozWaitForElementToExist(app.otherElements.staticTexts["Welcome to Mozilla"]) if !iPad() { mozWaitForElementToExist(app.buttons["Menu"]) } diff --git a/hooks/post-checkout b/hooks/post-checkout new file mode 100755 index 0000000000000..74d852068568b --- /dev/null +++ b/hooks/post-checkout @@ -0,0 +1,23 @@ +#!/bin/bash + +# Directory containing your custom hooks +HOOKS_DIR="hooks" + +# Check if hooks have already been installed in the .git/hooks directory +# We check for one example hook to verify, you can change this to your preference +required_hooks=("prepare-commit-msg" "post-checkout") +missing_hooks=() + +for hook in "${required_hooks[@]}"; do + if [ ! -f ".git/hooks/$hook" ]; then + missing_hooks+=("$hook") + fi +done + +if [ ${#missing_hooks[@]} -ne 0 ]; then + echo "Missing hooks: ${missing_hooks[*]}. Running setup_hooks.sh to install them." + # Run the setup script to copy the hooks + ./setup_hooks.sh +else + echo "Git hooks are already installed." +fi \ No newline at end of file diff --git a/hooks/prepare-commit-msg b/hooks/prepare-commit-msg new file mode 100755 index 0000000000000..6828238f49027 --- /dev/null +++ b/hooks/prepare-commit-msg @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import sys +import re +from subprocess import check_output, CalledProcessError + +commit_msg_filepath = sys.argv[1] + +try: + # Try to get the current branch name + branch = check_output( + ["git", "symbolic-ref", "--short", "HEAD"] + ).strip().decode() + + # Define the regex to match the JIRA ticket in the branch name + regex = r"(?i)([A-Za-z]+[-_]\d+)" + match = re.search(regex, branch) + + if match: + # Extract the JIRA ticket from the branch name and format it correctly + jira_ticket = match.group(0).upper().replace('_', '-') # Ensure ticket uses hyphen + with open(commit_msg_filepath, "r+") as fh: + commit_msg = fh.read().strip() + + # Prefix the commit message with the JIRA ticket + updated_commit_msg = f"[{jira_ticket}] {commit_msg}" + + # Write the updated commit message back to the file + fh.seek(0, 0) + fh.write(updated_commit_msg) + fh.truncate() + +except CalledProcessError: + # If we're in a detached HEAD state (such as during rebase), skip the branch name lookup + print("Detached HEAD state detected. Skipping branch name extraction.") \ No newline at end of file diff --git a/perform_snapshot_tests.sh b/perform_snapshot_tests.sh new file mode 100755 index 0000000000000..5b9923ff9915c --- /dev/null +++ b/perform_snapshot_tests.sh @@ -0,0 +1,468 @@ +#!/usr/bin/env bash +set -euo pipefail +# Uncomment the following line to enable debug mode for troubleshooting +# set -x + +# ================================ +# Ensure the script runs with Bash +# ================================ + +# Check if the script is running under Bash +if [ -z "${BASH_VERSION:-}" ]; then + echo "Re-executing the script with Bash..." + exec bash "$0" "$@" +fi + +# ================================ +# Script Parameters Validation +# ================================ + +# Check if the required parameters are passed +if [ "$#" -lt 4 ]; then + echo "Usage: ./perform_snapshot_tests.sh " + echo "Example: ./perform_snapshot_tests.sh config.json environment.json Results SchemeName" + exit 1 +fi + +cd firefox-ios + +config_file="$1" +environment_file="$2" # Fixed environment file path +results_dir="$3" +scheme="$4" + +# ================================ +# Read Configuration Files +# ================================ + +# Validate JSON configuration +if ! jq empty "$config_file" >/dev/null 2>&1; then + echo "Error: Invalid JSON in configuration file." + exit 1 +fi + +# Read the devices and test bundles from the JSON configuration file +devices_json=$(jq -c '.devices[]' "$config_file") +tests_json=$(jq -c '.testBundles[]' "$config_file") + +# Read all locales into an array +mapfile -t all_locales < <(jq -r '.locales[]' "$config_file") + +# Initialize arrays to store device information +device_names=() +orientations=() +os_versions=() +is_defaults=() + +default_device_name="" +default_device_os_version="" +default_device_count=0 + +# Initialize xcodebuild execution counter +xcodebuild_count=0 + +# ================================ +# Collect Device Information +# ================================ + +# Populate device information arrays and identify the default device +while IFS= read -r device; do + name=$(echo "$device" | jq -r '.name') + orientation=$(echo "$device" | jq -r '.orientation') + os_version=$(echo "$device" | jq -r '.os // empty') + is_default=$(echo "$device" | jq -r '.isDefaultTestDevice // empty') + + device_names+=("$name") + orientations+=("$orientation") + os_versions+=("$os_version") + is_defaults+=("$is_default") + + if [ "$is_default" == "true" ]; then + default_device_name="$name" + default_device_os_version="$os_version" + default_device_count=$((default_device_count + 1)) + fi +done <<< "$devices_json" + +# Validate default device count +if [ "$default_device_count" -eq 0 ]; then + echo "Error: No default device specified in the configuration (isDefaultTestDevice: true)." + exit 1 +elif [ "$default_device_count" -gt 1 ]; then + echo "Error: More than one default device specified in the configuration (isDefaultTestDevice: true)." + exit 1 +fi + +echo "Default device: $default_device_name" + +# Set OS versions to default if missing +for i in "${!device_names[@]}"; do + if [ -z "${os_versions[$i]}" ] || [ "${os_versions[$i]}" == "null" ]; then + os_versions[$i]="$default_device_os_version" + fi +done + +# ================================ +# Initialize Test Groups +# ================================ + +# Use associative arrays to map device sets to test classes +declare -A device_set_tests +declare -A device_set_devices + +# Function to create a unique key for a device set +create_device_set_key() { + local devices=("$@") + IFS=$'\n' sorted_devices=($(printf '%s\n' "${devices[@]}" | sort)) + echo "$(printf '%s|' "${sorted_devices[@]}")" | sed 's/|$//' +} + +# Function to sanitize device set key for filenames (if needed) +sanitize_filename() { + local filename="$1" + # Replace spaces, parentheses, slashes, and other special characters with underscores + echo "$filename" | tr ' /()' '_' | tr -cd '[:alnum:]_' +} + +# Function to get the test target for a given test class +get_test_target() { + local test_class="$1" + # Adjust this function based on your project's test target names + # For example, if all tests are in the target "EcosiaSnapshotTests" + echo "EcosiaSnapshotTests" +} + +# ================================ +# Process Test Bundles and Classes +# ================================ + +# Loop through each test bundle +while IFS= read -r test_bundle; do + test_bundle_name=$(echo "$test_bundle" | jq -r '.name') + test_classes_json=$(echo "$test_bundle" | jq -c '.testClasses[]') + + # Loop through each test class in the test bundle + while IFS= read -r test_class; do + class_name=$(echo "$test_class" | jq -r '.name') + runs_on_device=$(echo "$test_class" | jq -r '.runsOn // empty') + devices_field=$(echo "$test_class" | jq -r '.devices // empty') + locales_field=$(echo "$test_class" | jq -r '.locales // empty') + + echo "Processing Test Class: $class_name" + + # Determine the devices this test class should run on + test_devices=() + + if [ -n "$runs_on_device" ] && [ "$runs_on_device" != "null" ]; then + # Test class with runsOn specified + test_device_name="$runs_on_device" + # Check if test_device_name exists in device_names + if ! printf '%s\n' "${device_names[@]}" | grep -Fxq "$test_device_name"; then + echo "Error: The runsOn device '$test_device_name' specified for test class '$class_name' does not exist in the devices list." + exit 1 + fi + test_devices+=("$test_device_name") + echo " - Runs on specified device: $test_device_name" + else + # Determine devices from devices_field + if [ -n "$devices_field" ] && [ "$devices_field" != "null" ]; then + # Read each device/item in the devices array + devices_array=() + while IFS= read -r device_item; do + devices_array+=("$device_item") + done < <(echo "$devices_field" | jq -r '.[]') + + devices_specified=() + orientations_specified=() + all_devices=false + + # Categorize each item in the devices array + for device_item in "${devices_array[@]}"; do + if [ "$device_item" == "all" ]; then + all_devices=true + echo " - Includes all devices" + elif [ "$device_item" == "portrait" ] || [ "$device_item" == "landscape" ]; then + orientations_specified+=("$device_item") + echo " - Includes orientation: $device_item" + else + # It's a device name + if ! printf '%s\n' "${device_names[@]}" | grep -Fxq "$device_item"; then + echo "Error: The device '$device_item' specified in 'devices' for test class '$class_name' does not exist in the devices list." + exit 1 + fi + devices_specified+=("$device_item") + echo " - Includes specific device: $device_item" + fi + done + + # Determine test_devices based on 'all' and orientations + if [ "$all_devices" = true ]; then + if [ "${#orientations_specified[@]}" -gt 0 ]; then + # "all" with orientations specified + echo " - Selecting all devices with specified orientations" + for i in "${!device_names[@]}"; do + if [[ " ${orientations_specified[@]} " =~ " ${orientations[$i]} " ]]; then + test_devices+=("${device_names[$i]}") + echo " - Selected Device: ${device_names[$i]}" + fi + done + else + # "all" without orientations + echo " - Selecting all devices without orientation filter" + test_devices=("${device_names[@]}") + fi + else + if [ "${#devices_specified[@]}" -gt 0 ]; then + if [ "${#orientations_specified[@]}" -gt 0 ]; then + # Specific devices with orientations + echo " - Selecting specific devices with specified orientations" + for device in "${devices_specified[@]}"; do + for i in "${!device_names[@]}"; do + if [ "${device_names[$i]}" == "$device" ] && [[ " ${orientations_specified[@]} " =~ " ${orientations[$i]} " ]]; then + test_devices+=("${device_names[$i]}") + echo " - Selected Device: ${device_names[$i]}" + fi + done + done + else + # Specific devices without orientations + echo " - Selecting specific devices without orientation filter" + for device in "${devices_specified[@]}"; do + for i in "${!device_names[@]}"; do + if [ "${device_names[$i]}" == "$device" ]; then + test_devices+=("${device_names[$i]}") + echo " - Selected Device: ${device_names[$i]}" + fi + done + done + fi + else + if [ "${#orientations_specified[@]}" -gt 0 ]; then + # Only orientations specified, which is not allowed + echo "Error: Only orientations specified in 'devices' for test class '$class_name'. Please specify at least one device." + exit 1 + else + # No devices specified, use default device + test_devices+=("$default_device_name") + echo " - No devices specified. Using default device: $default_device_name" + fi + fi + fi + else + # No devices specified, use default device + test_devices+=("$default_device_name") + echo " - No devices specified. Using default device: $default_device_name" + fi + fi + + # Handle case where no devices are selected after filtering + if [ "${#test_devices[@]}" -eq 0 ]; then + echo "Warning: No devices selected for test class '$class_name'. Skipping this test class." + continue + fi + + # Create a unique key for the device set + device_set_key=$(create_device_set_key "${test_devices[@]}") + echo " - Device Set Key: $device_set_key" + + # Append the test class to the corresponding device set + device_set_tests["$device_set_key"]+="$class_name|" + device_set_devices["$device_set_key"]=$(printf '%s|' "${test_devices[@]}") + echo " - Appended Test Class '$class_name' to Device Set" + done <<< "$test_classes_json" +done <<< "$tests_json" + +# ================================ +# Execute Tests with xcodebuild +# ================================ + +# Verify all device sets are to be processed +echo "All device_set_keys: ${!device_set_tests[@]}" + +# Iterate over each device set group and run xcodebuild +for device_set_key in "${!device_set_tests[@]}"; do + test_classes_str="${device_set_tests[$device_set_key]}" + device_set_devices_str="${device_set_devices[$device_set_key]}" + + echo "Processing Device Set: $device_set_key" + echo " - Test Classes: $test_classes_str" + echo " - Device Set Devices String: $device_set_devices_str" + + # Split the device set into an array using '|' as delimiter and remove trailing '|' + IFS='|' read -r -a device_set <<< "${device_set_devices_str}|" + + # Remove the trailing empty element if exists + if [ -z "${device_set[-1]}" ]; then + unset device_set[-1] + fi + + echo " - Device Set Array after Splitting: ${device_set[@]}" + + # Prepare the devices for environment.json with orientation + devices_json_array=$(for device_name in "${device_set[@]}"; do + if [ -n "$device_name" ]; then + # Initialize device_orientation + device_orientation="" + + # Find index of the device to get orientation + for i in "${!device_names[@]}"; do + if [ "${device_names[$i]}" == "$device_name" ]; then + device_orientation="${orientations[$i]}" + break + fi + done + + # Fallback if orientation is still empty + if [ -z "$device_orientation" ]; then + echo "Warning: Orientation not found for device '$device_name'. Defaulting to 'portrait'." + device_orientation="portrait" + fi + + # Escape double quotes and backslashes in device_name + escaped_device_name=$(echo "$device_name" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g') + echo "{\"name\": \"$escaped_device_name\", \"orientation\": \"$device_orientation\"}" + fi + done | jq -s .) + + echo " - Devices JSON Array: $devices_json_array" + + # Prepare the list of test classes by splitting on '|' + IFS='|' read -r -a test_classes <<< "${test_classes_str%|}" + echo " - Test Classes Array: ${test_classes[@]}" + + # Prepare the -only-testing parameters + only_testing_params="" + for test_class in "${test_classes[@]}"; do + test_target=$(get_test_target "$test_class") + test_identifier="$test_target/$test_class" + only_testing_params+=" -only-testing \"$test_identifier\"" + echo " - Adding Only Testing Parameter: $test_identifier" + done + + # Determine which device to use for xcodebuild + device_name="" + for dev in "${device_set[@]}"; do + if [ "$dev" == "$default_device_name" ]; then + device_name="$default_device_name" + break + fi + done + + if [ -z "$device_name" ]; then + # If default device not in the device set, use the first device + device_name="${device_set[0]}" + echo " - Default device not in set. Using first device: $device_name" + else + echo " - Using Default Device: $device_name" + fi + + # Get the device name to pass into the env file + simulator_device_name="$device_name" + echo " - Simulator Device Name: $simulator_device_name" + + # Overwrite the fixed environment.json with current device set + locales_json_array=$(printf '%s\n' "${all_locales[@]}" | jq -R . | jq -s .) + echo " - Locales JSON Array: $locales_json_array" + + echo "{ + \"DEVICES\": $devices_json_array, + \"LOCALES\": $locales_json_array, + \"SIMULATOR_DEVICE_NAME\": \"$simulator_device_name\" + }" > "$environment_file" + + echo " - Environment file created at: $environment_file" + cat "$environment_file" # Print the contents of the file for verification + + # Validate the generated environment.json + if ! jq empty "$environment_file" >/dev/null 2>&1; then + echo "Error: Generated environment.json ($environment_file) is invalid." + exit 1 + fi + + # Find index of the device to get OS version + os_version="" + for i in "${!device_names[@]}"; do + if [ "${device_names[$i]}" == "$device_name" ]; then + os_version="${os_versions[$i]}" + break + fi + done + + echo " - OS Version for Device '$device_name': $os_version" + + # Prepare result path + # Concatenate test class names + test_classes_concat=$(printf '%s_' "${test_classes[@]}") + test_classes_concat=${test_classes_concat%_} # Remove trailing underscore + # Sanitize test_classes_concat to remove spaces and special characters + test_classes_concat=$(echo "$test_classes_concat" | tr ' /' '__') + result_path="$results_dir/${test_classes_concat}_tests.xcresult" + mkdir -p "$results_dir" + + echo " - Result Path: $result_path" + + # Prepare the xcodebuild command + xcodebuild_cmd="xcodebuild test \ + -scheme \"$scheme\" \ + -clonedSourcePackagesDirPath \"SourcePackages/\" \ + -destination \"platform=iOS Simulator,name=$device_name,OS=$os_version\" \ + $only_testing_params \ + -resultBundlePath \"$result_path\"" + + echo " - Running xcodebuild Command: $xcodebuild_cmd" + + # Disable 'set -e' temporarily to ensure the script continues even if the test fails + set +e + + # Run the xcodebuild command + eval $xcodebuild_cmd + + # Re-enable 'set -e' + set -e + + # Increment xcodebuild execution counter + xcodebuild_count=$((xcodebuild_count + 1)) +done + +# ================================ +# Combine Test Results +# ================================ + +echo "Combining all xcresult files into a single xcresult..." + +combined_result_path="$results_dir/all_tests.xcresult" + +# Define the Xcode path based on the CI environment variable +if [ "${CI:-false}" = "true" ]; then + xcresulttool_path="/Applications/Xcode_16.4.app/Contents/Developer/usr/bin/xcresulttool" +else + xcresulttool_path="/Applications/Xcode.app/Contents/Developer/usr/bin/xcresulttool" +fi + +# Verify that xcresulttool exists +if [ ! -x "$xcresulttool_path" ]; then + echo "Error: xcresulttool not found at $xcresulttool_path" + exit 1 +fi + +# Find all xcresult files in the results directory +xcresult_files=($(find "$results_dir" -name "*.xcresult")) + +# Check if any xcresult files were found +if [ "${#xcresult_files[@]}" -eq 0 ]; then + echo "Error: No xcresult files found in $results_dir to combine." + exit 1 +fi + +# Check if xcresult is one +if [ "${#xcresult_files[@]}" -eq 1 ]; then + echo "Only one xcresult file found. Copying to combined result path." + cp -R "${xcresult_files[0]}" "$combined_result_path" + echo "Combined xcresult created at: $combined_result_path" + exit 0 +fi + +# Merge the xcresult files into one +$xcresulttool_path merge "${xcresult_files[@]}" --output-path "$combined_result_path" + +echo "Combined xcresult created at: $combined_result_path" \ No newline at end of file diff --git a/setup_hooks.sh b/setup_hooks.sh new file mode 100755 index 0000000000000..5beca943d6e59 --- /dev/null +++ b/setup_hooks.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Check if the .git/hooks directory exists +if [ ! -d ".git/hooks" ]; then + echo ".git/hooks directory does not exist. Creating it now." + mkdir -p .git/hooks +fi + +# Copy all custom hooks from hooks/ to .git/hooks/ +cp hooks/* .git/hooks/ + +# Ensure the hooks are executable +chmod +x .git/hooks/* + +echo "Git hooks have been installed successfully." \ No newline at end of file diff --git a/swiftlint_baseline.json b/swiftlint_baseline.json new file mode 100644 index 0000000000000..1717a550a4f4a --- /dev/null +++ b/swiftlint_baseline.json @@ -0,0 +1,332 @@ +[ + { + "violation": { + "severity": "warning", + "reason": "nonisolated modifier should come before private", + "ruleIdentifier": "modifier_order", + "ruleName": "Modifier Order", + "location": { + "file": "BrowserKit/Sources/TabDataStore/TabDataStore.swift", + "line": 256, + "character": 25 + }, + "ruleDescription": "Modifier order should be consistent." + }, + "text": " private nonisolated func windowUUID(fromURL url: URL) -> WindowUUID? {" + }, + { + "violation": { + "severity": "warning", + "reason": "Header comments should be consistent with project patterns", + "ruleIdentifier": "file_header", + "ruleName": "File Header", + "location": { + "file": "firefox-ios/Client/AdjustHelper.swift", + "line": 1, + "character": 1 + }, + "ruleDescription": "Header comments should be consistent with project patterns. The SWIFTLINT_CURRENT_FILENAME placeholder can optionally be used in the required and forbidden patterns. It will be replaced by the real file name." + }, + "text": "" + }, + { + "violation": { + "severity": "warning", + "reason": "Header comments should be consistent with project patterns", + "ruleIdentifier": "file_header", + "ruleName": "File Header", + "location": { + "file": "firefox-ios/Client/AdjustTelemetryHelper.swift", + "line": 1, + "character": 1 + }, + "ruleDescription": "Header comments should be consistent with project patterns. The SWIFTLINT_CURRENT_FILENAME placeholder can optionally be used in the required and forbidden patterns. It will be replaced by the real file name." + }, + "text": "" + }, + { + "violation": { + "severity": "warning", + "reason": "expected 48, got 12", + "ruleIdentifier": "closure_end_indentation", + "ruleName": "Closure End Indentation", + "location": { + "file": "firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift", + "line": 190, + "character": 13 + }, + "ruleDescription": "Closure end should have the same indentation as the line that started it." + }, + "text": " }" + }, + { + "violation": { + "severity": "warning", + "reason": "expected 48, got 12", + "ruleIdentifier": "closure_end_indentation", + "ruleName": "Closure End Indentation", + "location": { + "file": "firefox-ios/Client/Frontend/Library/Bookmarks/Legacy/LegacyBookmarksPanel.swift", + "line": 294, + "character": 13 + }, + "ruleDescription": "Closure end should have the same indentation as the line that started it." + }, + "text": " }" + }, + { + "violation": { + "severity": "warning", + "reason": "Header comments should be consistent with project patterns", + "ruleIdentifier": "file_header", + "ruleName": "File Header", + "location": { + "file": "firefox-ios/Client/Telemetry/Wrapper/AdjustWrapper.swift", + "line": 1, + "character": 1 + }, + "ruleDescription": "Header comments should be consistent with project patterns. The SWIFTLINT_CURRENT_FILENAME placeholder can optionally be used in the required and forbidden patterns. It will be replaced by the real file name." + }, + "text": "" + }, + { + "violation": { + "severity": "warning", + "reason": "Images that provide context should have an accessibility label or should be explicitly hidden from accessibility", + "ruleIdentifier": "accessibility_label_for_image", + "ruleName": "Accessibility Label for Image", + "location": { + "file": "firefox-ios/Ecosia/UI/NTP/Header/EcosiaAISearchButton.swift", + "line": 25, + "character": 13 + }, + "ruleDescription": "Images that provide context should have an accessibility label or should be explicitly hidden from accessibility" + }, + "text": " Image(\"ai-sparkle\", bundle: .ecosia)" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space after generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/Shared/Extensions/OptionalExtensions.swift", + "line": 16, + "character": 20 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": "public func ??? (optional: T?, defaultValue: @autoclosure () -> String) -> String {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/ClientTests/DateGroupedTableDataTests.swift", + "line": 113, + "character": 44 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testCustomIntervalsItemsForSection () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Header comments should be consistent with project patterns", + "ruleIdentifier": "file_header", + "ruleName": "File Header", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AdjustTelemetryHelperTests.swift", + "line": 1, + "character": 1 + }, + "ruleDescription": "Header comments should be consistent with project patterns. The SWIFTLINT_CURRENT_FILENAME placeholder can optionally be used in the required and forbidden patterns. It will be replaced by the real file name." + }, + "text": "" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/L10nSnapshotTests/L10nSuite1SnapshotTests.swift", + "line": 78, + "character": 32 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testWebViewContextMenu () throws {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 45, + "character": 28 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func allowNotifications () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 88, + "character": 28 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncHistory () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 98, + "character": 38 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncPageUsingChinaFxA () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 111, + "character": 29 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncBookmark () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 125, + "character": 36 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncBookmarkDesktop () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 135, + "character": 25 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncTabs () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 159, + "character": 27 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncLogins () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 181, + "character": 35 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncHistoryDesktop () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 193, + "character": 36 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncPasswordDesktop () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift", + "line": 216, + "character": 32 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func testFxASyncTabsDesktop () {" + }, + { + "violation": { + "severity": "warning", + "reason": "Superfluous space between function name and generic parameter(s)", + "ruleIdentifier": "function_name_whitespace", + "ruleName": "Function Name Whitespace", + "location": { + "file": "firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift", + "line": 331, + "character": 21 + }, + "ruleDescription": "There should be consistent whitespace before and after function names and generic parameters." + }, + "text": " func closeURLBar () {" + } +] diff --git a/test-fixtures/generate-metrics.sh b/test-fixtures/generate-metrics.sh index b16bc68fff0e8..6f4b1b049aef7 100755 --- a/test-fixtures/generate-metrics.sh +++ b/test-fixtures/generate-metrics.sh @@ -4,8 +4,8 @@ set -e BUILD_LOG_FILE="$1" TYPE_LOG_FILE="$2" -THRESHOLD_UNIT_TEST=9 -THRESHOLD_XCUITEST=2 +THRESHOLD_UNIT_TEST=28 +THRESHOLD_XCUITEST=28 WARNING_COUNT=$(grep -E -v "SourcePackages/checkouts" "$BUILD_LOG_FILE" | grep -E "(^|:)[0-9]+:[0-9]+:|warning:|ld: warning:|:0: warning:|fatal|===" | uniq | wc -l)